@tpypan/graphcraft
Advanced tools
| { | ||
| "name": "graphcraft", | ||
| "interface": { "displayName": "Graphcraft" }, | ||
| "plugins": [ | ||
| { | ||
| "name": "graphcraft", | ||
| "source": { | ||
| "source": "npm", | ||
| "package": "@tpypan/graphcraft", | ||
| "version": "0.1.5", | ||
| "registry": "https://registry.npmjs.org" | ||
| }, | ||
| "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" }, | ||
| "category": "Productivity" | ||
| } | ||
| ] | ||
| } |
| { | ||
| "name": "graphcraft", | ||
| "owner": { "name": "Tony Pan" }, | ||
| "description": "Durable, inspectable execution for long-running coding-agent workflows.", | ||
| "plugins": [ | ||
| { | ||
| "name": "graphcraft", | ||
| "displayName": "Graphcraft", | ||
| "source": { | ||
| "source": "npm", | ||
| "package": "@tpypan/graphcraft", | ||
| "version": "0.1.5", | ||
| "registry": "https://registry.npmjs.org" | ||
| }, | ||
| "description": "Progress-aware execution for durable coding agents.", | ||
| "author": { "name": "Tony Pan", "url": "https://github.com/tpypan" }, | ||
| "homepage": "https://github.com/tpypan/graphcraft", | ||
| "repository": "https://github.com/tpypan/graphcraft", | ||
| "license": "MIT", | ||
| "keywords": ["ai-agents", "coding-agents", "graph-engineering"], | ||
| "category": "productivity" | ||
| } | ||
| ] | ||
| } |
| { | ||
| "name": "graphcraft", | ||
| "version": "0.1.5", | ||
| "description": "Progress-aware execution for durable coding agents.", | ||
| "author": { "name": "Tony Pan", "url": "https://github.com/tpypan" }, | ||
| "homepage": "https://github.com/tpypan/graphcraft", | ||
| "repository": "https://github.com/tpypan/graphcraft", | ||
| "license": "MIT", | ||
| "keywords": ["ai-agents", "coding-agents", "graph-engineering"] | ||
| } |
| { | ||
| "name": "graphcraft", | ||
| "version": "0.1.5", | ||
| "description": "Progress-aware execution for long-running coding agents.", | ||
| "author": { "name": "Tony Pan", "url": "https://github.com/tpypan" }, | ||
| "homepage": "https://github.com/tpypan/graphcraft", | ||
| "repository": "https://github.com/tpypan/graphcraft", | ||
| "license": "MIT", | ||
| "mcpServers": { "graphcraft": { "command": "node", "args": ["dist/mcp.mjs"], "cwd": "." } }, | ||
| "keywords": ["ai-agents", "coding-agents", "graph-engineering", "long-running-agents"], | ||
| "interface": { | ||
| "displayName": "Graphcraft", | ||
| "shortDescription": "Keep coding agents making verified progress.", | ||
| "longDescription": "Graphcraft is a progress-aware execution layer for durable, grounded, token-efficient coding-agent workflows.", | ||
| "developerName": "Tony Pan", | ||
| "category": "Productivity", | ||
| "capabilities": ["Write"], | ||
| "defaultPrompt": ["Use Graphcraft for this long-running repository task."] | ||
| } | ||
| } |
| { | ||
| "mcpServers": { | ||
| "graphcraft": { "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/dist/mcp.mjs"] } | ||
| } | ||
| } |
| { | ||
| "schemaVersion": 2, | ||
| "id": "stable-v1", | ||
| "version": 4, | ||
| "description": "Versioned local corpus for matched Codex and Claude baseline-versus-Graphcraft trials.", | ||
| "tasks": [ | ||
| { | ||
| "id": "bug-empty-input", | ||
| "family": "bug", | ||
| "task": "Fix normalizeName so empty input returns an empty string, preserve non-empty trimming, add or update regression coverage, and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-bug-empty-input\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "src.js": "export function normalizeName(value) { return value.trim() || 'unknown'; }\n", | ||
| "verify.mjs": "import { normalizeName } from './src.js'; if (normalizeName('') !== '' || normalizeName(' Ada ') !== 'Ada') process.exit(1);\n", | ||
| "score.mjs": "import { normalizeName } from './src.js'; if (normalizeName('') !== '' || normalizeName(' Ada ') !== 'Ada') process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "contains", "path": "src.js", "value": "return value.trim()" }, | ||
| { "kind": "not_contains", "path": "src.js", "value": "unknown" } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "bug-inclusive-range", | ||
| "family": "bug", | ||
| "task": "Fix range so it includes the requested end value without changing its start behavior, add regression coverage, and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-bug-inclusive-range\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "range.js": "export function range(start, end) { return Array.from({ length: end - start }, (_, index) => start + index); }\n", | ||
| "verify.mjs": "import { range } from './range.js'; if (JSON.stringify(range(2, 4)) !== '[2,3,4]') process.exit(1);\n", | ||
| "score.mjs": "import { range } from './range.js'; if (JSON.stringify(range(2, 4)) !== '[2,3,4]') process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [{ "kind": "contains", "path": "range.js", "value": "end - start + 1" }], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "feature-uppercase-slug", | ||
| "family": "feature", | ||
| "task": "Add an exported upperSlug helper that trims input, replaces whitespace with hyphens, uppercases the result, and passes the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-feature-uppercase-slug\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "slug.js": "export function slug(value) { return value.trim().replace(/\\s+/g, '-').toLowerCase(); }\n", | ||
| "verify.mjs": "import { upperSlug } from './slug.js'; if (upperSlug(' hello world ') !== 'HELLO-WORLD') process.exit(1);\n", | ||
| "score.mjs": "import { upperSlug } from './slug.js'; if (upperSlug(' hello world ') !== 'HELLO-WORLD') process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "contains", "path": "slug.js", "value": "export function upperSlug" }, | ||
| { "kind": "contains", "path": "slug.js", "value": "toUpperCase" } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "feature-summary-report", | ||
| "family": "feature", | ||
| "task": "Add report.js with an exported summarize function returning count and total for numeric inputs, reject non-numeric values, and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-feature-summary-report\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "index.js": "export const version = 1;\n", | ||
| "verify.mjs": "import { summarize } from './report.js'; const value = summarize([2,3,5]); if (value.count !== 3 || value.total !== 10) process.exit(1); let rejected = false; try { summarize([1,'x']); } catch { rejected = true; } if (!rejected) process.exit(1);\n", | ||
| "score.mjs": "import { summarize } from './report.js'; const value = summarize([2,3,5]); if (value.count !== 3 || value.total !== 10) process.exit(1); let rejected = false; try { summarize([1,'x']); } catch { rejected = true; } if (!rejected) process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "exists", "path": "report.js" }, | ||
| { "kind": "contains", "path": "report.js", "value": "count" }, | ||
| { "kind": "contains", "path": "report.js", "value": "total" } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "migration-client-v3", | ||
| "family": "migration", | ||
| "task": "Migrate every createClientV2 use to createClientV3, remove the v2 export, preserve behavior, and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-migration-client-v3\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "client.js": "export const createClientV2 = () => ({ version: 2 }); export const createClientV3 = () => ({ version: 3 });\n", | ||
| "app.js": "import { createClientV2 } from './client.js'; export const client = createClientV2();\n", | ||
| "verify.mjs": "const app = await import('./app.js'); const source = await import('node:fs/promises').then(fs => fs.readFile('client.js','utf8')); if (app.client.version !== 3 || source.includes('createClientV2')) process.exit(1);\n", | ||
| "score.mjs": "const app = await import('./app.js'); const source = await import('node:fs/promises').then(fs => fs.readFile('client.js','utf8')); if (app.client.version !== 3 || source.includes('createClientV2')) process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "contains", "path": "app.js", "value": "createClientV3" }, | ||
| { "kind": "not_contains", "path": "client.js", "value": "createClientV2" } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "migration-timeout-key", | ||
| "family": "migration", | ||
| "task": "Migrate the timeoutSeconds configuration to timeoutMs everywhere, convert the value correctly, remove the old key, and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-migration-timeout-key\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "config.json": "{\"timeoutSeconds\":5}\n", | ||
| "config.js": "import data from './config.json' with { type: 'json' }; export const timeout = data.timeoutSeconds * 1000;\n", | ||
| "verify.mjs": "import { timeout } from './config.js'; const data = JSON.parse(await import('node:fs/promises').then(fs => fs.readFile('config.json','utf8'))); if (timeout !== 5000 || data.timeoutMs !== 5000 || 'timeoutSeconds' in data) process.exit(1);\n", | ||
| "score.mjs": "import { timeout } from './config.js'; const data = JSON.parse(await import('node:fs/promises').then(fs => fs.readFile('config.json','utf8'))); if (timeout !== 5000 || data.timeoutMs !== 5000 || 'timeoutSeconds' in data) process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "contains", "path": "config.json", "value": "timeoutMs" }, | ||
| { "kind": "not_contains", "path": "config.json", "value": "timeoutSeconds" }, | ||
| { "kind": "contains", "path": "config.js", "value": "timeoutMs" } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "refactor-shared-clamp", | ||
| "family": "refactor", | ||
| "task": "Refactor the duplicated clamp logic into one shared exported helper without changing behavior and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-refactor-shared-clamp\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "values.js": "export function volume(value) { return Math.max(0, Math.min(100, value)); } export function brightness(value) { return Math.max(0, Math.min(100, value)); }\n", | ||
| "verify.mjs": "import { volume, brightness, clamp } from './values.js'; if (volume(120) !== 100 || brightness(-2) !== 0 || clamp(40) !== 40) process.exit(1);\n", | ||
| "score.mjs": "import { volume, brightness, clamp } from './values.js'; if (volume(120) !== 100 || brightness(-2) !== 0 || clamp(40) !== 40) process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "contains", "path": "values.js", "value": "export function clamp" }, | ||
| { "kind": "contains", "path": "values.js", "value": "return clamp(value)" } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "audit-api-boundary", | ||
| "family": "audit", | ||
| "task": "Audit the API boundary for validation, error handling, and missing tests; return a read-only report with Findings, Evidence, and Unknowns grounded in api.js; run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-audit-api-boundary\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "api.js": "export async function handle(input) { return JSON.parse(input); }\n", | ||
| "verify.mjs": "import { handle } from './api.js'; const value = await handle('{\"ok\":true}'); if (value.ok !== true) process.exit(1);\n", | ||
| "score.mjs": "import { handle } from './api.js'; const value = await handle('{\"ok\":true}'); if (value.ok !== true) process.exit(1); let malformed = false; try { await handle('{'); } catch (error) { malformed = error instanceof SyntaxError; } const nonStringResult = await handle(null); if (!malformed || nonStringResult !== null) process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "summary_contains", "value": "Findings" }, | ||
| { "kind": "summary_contains", "value": "Evidence" }, | ||
| { "kind": "summary_contains", "value": "Unknowns" }, | ||
| { "kind": "summary_contains", "value": "api.js" }, | ||
| { "kind": "summary_contains", "value": "JSON.parse" }, | ||
| { "kind": "summary_contains", "value": "SyntaxError" }, | ||
| { "kind": "summary_contains", "value": "null" }, | ||
| { "kind": "summary_contains", "value": "non-string" }, | ||
| { | ||
| "kind": "equals", | ||
| "path": "api.js", | ||
| "value": "export async function handle(input) { return JSON.parse(input); }\n" | ||
| } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "pr-repair-email-validation", | ||
| "family": "pr_repair", | ||
| "task": "Address review feedback by making parseEmail reject values without exactly one @ separator, preserve trimming and lowercase normalization, and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-pr-repair-email-validation\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "email.js": "export function parseEmail(value) { return value.trim().toLowerCase(); }\n", | ||
| "REVIEW.md": "Review: parseEmail currently accepts malformed addresses. Require exactly one @ separator.\n", | ||
| "verify.mjs": "import { parseEmail } from './email.js'; if (parseEmail(' A@B.COM ') !== 'a@b.com') process.exit(1); for (const bad of ['abc','a@@b']) { let rejected = false; try { parseEmail(bad); } catch { rejected = true; } if (!rejected) process.exit(1); }\n", | ||
| "score.mjs": "import { parseEmail } from './email.js'; if (parseEmail(' A@B.COM ') !== 'a@b.com') process.exit(1); for (const bad of ['abc','a@@b']) { let rejected = false; try { parseEmail(bad); } catch { rejected = true; } if (!rejected) process.exit(1); }\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "contains", "path": "email.js", "value": "split" }, | ||
| { "kind": "contains", "path": "email.js", "value": "throw" } | ||
| ], | ||
| "repetitions": 3 | ||
| }, | ||
| { | ||
| "id": "pr-repair-null-owner", | ||
| "family": "pr_repair", | ||
| "task": "Address review feedback by making ownerLabel handle a null owner as Unassigned while preserving named-owner output, add regression coverage, and run the declared checks.", | ||
| "finishLine": "local_verified", | ||
| "initialFiles": { | ||
| "package.json": "{\"name\":\"benchmark-pr-repair-null-owner\",\"private\":true,\"type\":\"module\",\"scripts\":{\"test\":\"node verify.mjs\"}}\n", | ||
| "owner.js": "export function ownerLabel(owner) { return `Owner: ${owner.name}`; }\n", | ||
| "REVIEW.md": "Review: owner can be null for imported records. Render Unassigned instead of throwing.\n", | ||
| "verify.mjs": "import { ownerLabel } from './owner.js'; if (ownerLabel(null) !== 'Unassigned' || ownerLabel({name:'Ada'}) !== 'Owner: Ada') process.exit(1);\n", | ||
| "score.mjs": "import { ownerLabel } from './owner.js'; if (ownerLabel(null) !== 'Unassigned' || ownerLabel({name:'Ada'}) !== 'Owner: Ada') process.exit(1);\n" | ||
| }, | ||
| "checks": [{ "command": "node", "scorerPath": "score.mjs" }], | ||
| "acceptance": [ | ||
| { "kind": "contains", "path": "owner.js", "value": "Unassigned" }, | ||
| { "kind": "contains", "path": "owner.js", "value": "owner.name" } | ||
| ], | ||
| "repetitions": 3 | ||
| } | ||
| ] | ||
| } |
+12
-2
| { | ||
| "name": "@tpypan/graphcraft", | ||
| "version": "0.1.1", | ||
| "version": "0.1.5", | ||
| "description": "Progress-aware execution for durable coding agents.", | ||
@@ -30,2 +30,8 @@ "license": "MIT", | ||
| "files": [ | ||
| ".agents/plugins/marketplace.json", | ||
| ".claude-plugin/marketplace.json", | ||
| ".claude-plugin/plugin.json", | ||
| ".codex-plugin/plugin.json", | ||
| ".mcp.json", | ||
| "benchmarks/stable-v1.json", | ||
| "dist/graphcraft.mjs", | ||
@@ -43,7 +49,9 @@ "dist/mcp.mjs" | ||
| "build": "node scripts/build.mjs", | ||
| "check": "pnpm format:check && pnpm typecheck && pnpm test && pnpm build && pnpm check:context && pnpm check:package", | ||
| "check": "pnpm format:check && pnpm typecheck && pnpm build && pnpm test && pnpm check:plugins && pnpm check:context && pnpm check:package", | ||
| "check:context": "node scripts/check-context-budget.mjs", | ||
| "check:package": "node scripts/check-package.mjs", | ||
| "check:plugins": "node scripts/generate-plugin-artifacts.mjs --check", | ||
| "format": "prettier --write .", | ||
| "format:check": "prettier --check .", | ||
| "generate:plugins": "node scripts/generate-plugin-artifacts.mjs", | ||
| "graphcraft": "tsx packages/cli/src/bin.ts", | ||
@@ -57,3 +65,5 @@ "mcp": "tsx packages/mcp/src/bin.ts", | ||
| "devDependencies": { | ||
| "@types/cross-spawn": "6.0.6", | ||
| "@types/node": "26.1.1", | ||
| "cross-spawn": "7.0.6", | ||
| "esbuild": "0.28.1", | ||
@@ -60,0 +70,0 @@ "prettier": "3.9.5", |
+74
-19
@@ -8,9 +8,13 @@ # Graphcraft | ||
| > [!WARNING] | ||
| > Graphcraft v0.1 is an alpha. It supports local verification and atomic-commit finish lines. It does not yet push, open pull requests, monitor CI, merge, or deploy. | ||
| > Graphcraft v0.1 is an alpha. It supports local verification, atomic commits, normal non-force pushes, idempotent pull-request opening, token-free waiting for an exact pull request to reach required-check green, bounded review-first or actionable-CI repair pushes, journaled review replies and resolutions, and justified infrastructure or cancellation reruns. It does not merge or deploy. | ||
| ## Install the alpha | ||
| Requirements: Git, Node.js 22+, and an authenticated Codex or Claude Code CLI. | ||
| Requirements: Git, Node.js 22+, and an authenticated, protocol-qualified Codex or Claude Code CLI. | ||
| The current exact profiles are Codex CLI 0.144.6 and Claude Code 2.1.212; other versions fail | ||
| closed until their structured output, streaming, usage, cancellation, and resume behavior is | ||
| qualified. `graphcraft doctor` reports the supported versions without invoking a model. | ||
| Remote `pushed`, `pr_open`, and `pr_green` finish lines additionally require an authenticated GitHub CLI (`gh`). | ||
| The public npm package is `@tpypan/graphcraft`; the unscoped `graphcraft` name belongs to an unrelated project. Once the first npm publication is live, install it globally with either package manager: | ||
| The public npm package is `@tpypan/graphcraft`; the unscoped `graphcraft` name belongs to an unrelated project. Install it globally with either package manager: | ||
@@ -27,11 +31,20 @@ ```bash | ||
| Until that registry publication is complete, install the same executable directly from GitHub: | ||
| If the npm registry is unavailable, install the versioned package asset from the matching | ||
| workflow-verified GitHub release: | ||
| ```bash | ||
| npm install --global https://github.com/tpypan/graphcraft/archive/refs/heads/main.tar.gz | ||
| GRAPHCRAFT_VERSION=0.1.5 | ||
| npm install --global "https://github.com/tpypan/graphcraft/releases/download/v${GRAPHCRAFT_VERSION}/tpypan-graphcraft-${GRAPHCRAFT_VERSION}.tgz" | ||
| graphcraft install --host codex | ||
| ``` | ||
| For a one-shot installation, use `npx @tpypan/graphcraft install --host codex` or `pnpm dlx @tpypan/graphcraft install --host claude` after publication. The installer copies its MCP runtime to `~/.graphcraft/runtime/<version>/` before host registration, so clearing the package-manager cache does not break Graphcraft. | ||
| Each release publishes `SHA256SUMS` beside the tarball for independent verification before install. | ||
| Direct npm, pnpm, and GitHub installation is the permanent supported fallback even when a host | ||
| marketplace is unavailable. Graphcraft also ships version-locked Codex and Claude marketplace | ||
| catalogs; [marketplace distribution](https://github.com/tpypan/graphcraft/blob/v0.1.5/docs/MARKETPLACES.md) records their validation and the | ||
| separate boundary for hosted public-directory submission. | ||
| For a one-shot installation, use `npx @tpypan/graphcraft install --host codex` or `pnpm dlx @tpypan/graphcraft install --host claude`. The installer copies its MCP runtime to `~/.graphcraft/runtime/<version>/` before host registration, so clearing the package-manager cache does not break Graphcraft. | ||
| Installation registers one local MCP tool; Graphcraft does not inject a large prompt or install a skill. Start a new coding-agent session after installation. | ||
@@ -50,11 +63,28 @@ | ||
| - Compiles a user request into linked execution and governance graphs. | ||
| - Lets the selected host propose a task-specific execution graph from bounded repository evidence, then validates and displays the actual plan before approval. | ||
| - Keeps the finish line, permissions, repository policy, and acceptance anchors outside worker control. | ||
| - Creates an isolated Git worktree without stashing, cleaning, or resetting the current checkout. | ||
| - Stores a hashed append-only event log and rebuildable state under the repository's local `.graphcraft/` directory. | ||
| - Gives each worker a small context capsule instead of replaying raw transcripts. | ||
| - Runs deterministic repository probes and classifies progress as advanced, learning, stalled, regressed, oscillating, blocked, or done. | ||
| - Schedules one evidence-driven repair when verification fails, then stops if the changed strategy does not clear the failure. | ||
| - Resumes interrupted runs without repeating accepted nodes and can switch between Codex and Claude Code. | ||
| - Tracks cached, uncached, output, reasoning, and total tokens when the host exposes them. | ||
| - Creates an isolated Git worktree without stashing, cleaning, or resetting the current checkout. Repeatable `--include` and `--exclude` globs become enforced runtime policy: actual tracked, untracked, and explicitly excluded ignored paths are content-snapshotted around every worker, while unauthorized HEAD, branch, index, read-only, or node-scope changes block acceptance and remain preserved for inspection. | ||
| - Stores a hashed append-only event log and rebuildable state under the repository's local `.graphcraft/` directory. Individual events, the event log, and the materialized state have explicit growth limits; Graphcraft reserves enough log capacity to persist one accurate blocker before refusing further appends. | ||
| - Applies one durable artifact policy to logs, transcripts, capsules, reports, and content-addressed evidence. Ordinary artifacts are redacted before sizing and safely truncated with source/stored-byte metadata, identity-bound artifacts fail closed instead of changing hashes, and a bounded inventory plus recoverable publication journal keeps run-wide quotas inspectable across interruption. | ||
| - Redacts known credential formats, authorization headers, credential-bearing URLs, password assignments, private keys, and configured sensitive environment values before model-visible summaries, durable events/transcripts/artifacts/reports, terminal/MCP output, and viewer/export responses. Integrity-hashed probe definitions reject secret-like content instead of being silently rewritten. | ||
| - Gives each worker a grounded, size-bounded context capsule, records what was selected, omitted, and reused, and never replays raw transcripts or probe logs. | ||
| - Infers deterministic, task-family-specific progress and completion probes from repository evidence, then lets users inspect or replace the versioned probe plan before approval. | ||
| - Keeps executable completion definitions in an integrity-hashed held-out plan, gives planner graphs only opaque references, and blocks omitted, substituted, or weakened checks before acceptance. | ||
| - Runs approved probes outside model context and classifies progress as advanced, learning, stalled, regressed, oscillating, blocked, or done. | ||
| - Persists task-specific evidence vectors and strategy trajectories across restarts, distinguishes advancing A→B→done work from A→B→A churn, and returns a concise decision packet when autonomous progress stops. | ||
| - Uses a fresh read-only semantic verifier only when structural probes cannot ground reported progress or completion, and persists its verdict and cost separately. | ||
| - Enforces control edges during scheduling and acceptance: observers record evidence, vetoes block without gaining write authority, target owners must approve, and user-owned arbitrators resolve explicit conflicts through durable decision packets. | ||
| - Applies evidence-backed add, supersede, split, fuse, and dependency amendments only to unfinished work, preserves governance anchors and completion probes, and stops repeated repair signatures. | ||
| - Runs sequentially by default and can overlap at most two independent read-only branches; all shared-worktree writes and Git side effects remain sequential. | ||
| - Optimizes the approved shape deterministically by fusing redundant bounded reads, splitting safely partitionable broad writes, recording concurrency choices, and reusing a durable host context only for tightly dependent same-authority reasoning with reconciled cost evidence. | ||
| - Checkpoints host sessions and results during execution, resumes the same host session when safe, and falls back to repository evidence when switching hosts or native continuation is unavailable. | ||
| - Accepts pause or stop from another CLI process, terminates the active child with bounded escalation, and records the exact cause and outcome before releasing the run lock. | ||
| - Executes explicit time, file-exists, and file-changed wait nodes without a model call while state is unchanged; wake conditions, content baselines, observations, and the next wake time survive restart in the event log. | ||
| - Runs approved work under an optional detached local supervisor with atomic PID/heartbeat records, mode-`0600` logs, stale-process replacement, and the same coordinated pause/stop channel. Supervisor files are operational projections; run events remain authoritative. | ||
| - Uses the authenticated `gh` CLI for a read-only GitHub preflight and fully paginated pull-request snapshot: exact head/base SHAs, required checks, reviews, review threads, mergeability, permissions, branch protection, and rate limits. Snapshots are marked untrusted and rejected when either SHA moves. | ||
| - Journals atomic commits, normal pushes, pull-request creation, review replies, thread resolutions, and check reruns as durable claim–act–confirm side effects. Exact remote preconditions and action markers reconcile mutations where GitHub supports them; reruns use a durable dispatch checkpoint and stop uncertain rather than risk a duplicate request. PR creation binds exact head/base SHAs, fully paginates existing branch PRs, and recovers an existing exact open PR. | ||
| - Classifies exact-SHA PR lifecycle state deterministically, giving current review feedback precedence over same-head CI failures and separating pending, actionable, infrastructure, cancelled, stale, human-decision, and green outcomes. `pr_green` waits with persisted bounded backoff and no model calls while checks or approvals are pending. Review and actionable-CI changes receive bounded, fully reverified repair pushes; unchanged signatures stop. Verified review fixes receive an exact reply and resolution, infrastructure or cancelled GitHub check runs receive at most one justified rerun, changes-requested decisions remain sticky until approval, and base movement is durably rebound without inferring rebase or merge authority. | ||
| - Serves `graphcraft view [run]` only on `127.0.0.1` as a read-only live projection of verified run files. The accessible local viewer distinguishes dependency and governance edges, exposes node context/probes/evidence, revisions, recovery and side-effect timelines, per-phase/per-node token dimensions, redacted on-demand artifacts, and a redacted self-contained export without writing to the run. | ||
| - Tracks cached, uncached, output, reasoning, and total tokens with explicit provider availability, and reports planning, worker, repair, semantic-verification, and Graphcraft-overhead costs by phase and node. | ||
| - Provides an experimental matched benchmark harness with a versioned ten-task public corpus, fresh deterministic fixtures, explicit model/effort controls, immutable fixture-bound scoring, atomic checkpoints, resumable randomized trials, deterministic blinded-review packets, and digest-bound Markdown publication. | ||
@@ -65,20 +95,45 @@ ## Commands | ||
| graphcraft install --host <codex|claude> | ||
| graphcraft run <task> | ||
| graphcraft update --host <codex|claude> | ||
| graphcraft run <task> [--include <glob>] [--exclude <glob>] [--finish-line <local_verified|committed|pushed|pr_open|pr_green>] [--max-workers 2] [--background] | ||
| graphcraft runs [--json] | ||
| graphcraft status [run] | ||
| graphcraft inspect [run] | ||
| graphcraft probes [run] [--set probe-plan.json] | ||
| graphcraft amend [run] --set amendment.json [--approve] | ||
| graphcraft decide [run] --source <id> --target <node> --verdict <approve|veto> --reason <text> | ||
| graphcraft pause [run] | ||
| graphcraft resume [run] | ||
| graphcraft resume [run] [--background] | ||
| graphcraft supervisors [run] | ||
| graphcraft stop [run] | ||
| graphcraft delete <run> [--yes] | ||
| graphcraft prune --completed-before <date> [--keep <count>] [--confirm-run <id>...] [--yes] | ||
| graphcraft trace [run] | ||
| graphcraft view [run] [--no-open] [--port <port>] | ||
| graphcraft doctor | ||
| graphcraft github-snapshot [pull-request] | ||
| graphcraft benchmark <suite> --host both --codex-model <model> --claude-model <model> --effort <level> | ||
| graphcraft benchmark-review <report> [--suite <suite>] --blinding-key-stdin --output <blinded-review.json> | ||
| graphcraft benchmark-report <report> [--suite <suite>] --blinding-key-stdin --labels <review-labels.json> --output <report.md> | ||
| graphcraft uninstall --host <codex|claude> | ||
| ``` | ||
| Small localized tasks bypass Graphcraft by default. Pass `--force` when you deliberately want a durable graph. | ||
| `runs`, `status`, `inspect`, and `trace` are concise human-readable views by default. Pass `--json` when a script or another tool needs the stable structured form. `runs` orders durable runs by their last update and prints an unambiguous run prefix that every run-specific command accepts. | ||
| `--background` detaches only after contract approval. `status` shows the current supervisor and `supervisors` shows every supervisor instance, including stale replacements and local log paths. A machine restart does not auto-launch a process; rerun `graphcraft resume <run> --background` to recover the persisted wait and continue without repeating accepted work. Filesystem wait paths are resolved inside the isolated worktree, whose exact path is exposed with the wait state. | ||
| `delete` and `prune` remove only Graphcraft-owned run state, never the preserved worktree or its branch. Both commands are read-only dry runs unless `--yes` is supplied. Deletion requires the exact reviewed run ID; pruning additionally requires every selected run ID through repeatable `--confirm-run` options and revalidates terminal state, cutoff, locks, and supervisors before removal. | ||
| `github-snapshot` itself is read-only. The separate `pushed`, `pr_open`, and `pr_green` finish lines perform only the approved normal push and optional PR creation after GitHub preflight. `pr_green` adds token-free lifecycle polling, bounded reverified repair pushes, exact review replies and resolutions, and one justified rerun for a rerunnable infrastructure or cancelled check. Unchanged repair signatures, non-rerunnable checks, sticky human decisions, uncertain mutations, and base conflicts stop with classified evidence. These finish lines never force-push, reopen, rebase a published branch, merge, deploy, or edit an unrelated PR. | ||
| Small localized tasks bypass Graphcraft by default using measured task-shape signals rather than request length. Pass `--force` when you deliberately want a durable graph. | ||
| Use `stable-v1` as the bundled benchmark suite name. A dry run validates and prints its schedule without requiring model options. Real trials require an explicit model for every selected host and one shared `low`, `medium`, `high`, or `xhigh` effort policy; reports remain local under `.graphcraft/benchmarks/` unless `--output` is supplied. | ||
| `benchmark-review` and `benchmark-report` accept the blinding key only through standard input when `--blinding-key-stdin` is explicit. Supply the same private high-entropy 32-byte key to both commands as exactly 64 lowercase hexadecimal characters, with an optional final LF or CRLF, from a protected external source such as a secret manager. Graphcraft bounds the input, does not accept a key-file path, and does not persist or print the key. Packet IDs use domain-separated HMAC; artifacts contain only the key digest. The review export is separate and create-only, removes explicit host/model/mode/session/usage metadata, and includes packet digests for external review. After every packet has one digest-bound reviewer verdict, `benchmark-report` validates the same key digest and exact packet coverage, then renders a separate create-only report containing raw/blinded/label provenance, per-task and aggregate results, blinded defect findings, uncertainty intervals, unsuccessful trials, and the existing quantitative gate. Neither command modifies the raw report, performs model calls, publishes results, or turns a passing quantitative gate into a stable-release claim. | ||
| ## Evidence and scope | ||
| The [v0.1 implementation report](docs/V0.1.md) records the acceptance boundary, architecture, tests, real-host dogfood, and known gaps. The [product plan](docs/PLAN.md) remains authoritative for the path to stable. Research and competitive rationale live under [docs/research](docs/research). | ||
| The [v0.1 implementation report](https://github.com/tpypan/graphcraft/blob/v0.1.5/docs/V0.1.md) records the acceptance boundary, architecture, tests, real-host dogfood, and known gaps. Research and competitive rationale live under [docs/research](https://github.com/tpypan/graphcraft/tree/v0.1.5/docs/research). | ||
| Graphcraft does not yet claim the stable plan's reliability or 20% token-savings gate. Those require the matched multi-task, dual-host benchmark described in the plan. | ||
| Graphcraft does not yet claim stable reliability or a 20% token-savings gate. The harness, public fixtures, and blinded-review/publication tooling exist, but repeated real Codex and Claude trials, completed independent defect review, published evidence, and a passing stable gate remain outstanding. | ||
@@ -85,0 +140,0 @@ ## Development |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
4586329
135.08%11
120%114911
117.35%148
59.14%0
-100%0
-100%8
33.33%