{"version":3,"file":"chain-BBbVbWVv.mjs","names":[],"sources":["../src/workers/chain.ts"],"sourcesContent":["/**\n * chain.ts — draft-then-implement chains: `--chain draft,implement[,review]`.\n *\n * The chain runs each stage as its own worker (own id, own pane, `parent` set\n * to the chain id), so `ps` shows the chain as a tree and every stage can be\n * followed, replayed and said to like any other worker:\n *\n *   - draft    turns the operator's brief into a full spec file under\n *              <logDir>/specs/<chain id>.md (goal, constraints, files likely\n *              touched, acceptance checks, verification commands);\n *   - any other stage (implement, plan, …) runs with that spec as its prompt\n *              and the original brief attached;\n *   - review   reads the spec and the working-tree diff and produces the\n *              structured report.\n *\n * A stage that fails (or a draft that produces no spec file) stops the chain;\n * the caller then writes the spec itself and re-runs without the draft stage.\n */\n\nimport { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { readWorkersSection } from \"./config.js\";\nimport { appendLedger } from \"./ledger.js\";\nimport { ledgerPath, workersLogDir } from \"./paths.js\";\nimport { newWorkerId } from \"./status.js\";\nimport { shortText } from \"./args.js\";\nimport { runWorker, type RunOptions } from \"./run.js\";\n\n/** Where a chain's spec file lives: <logDir>/specs/<chain id>.md. */\nexport function specPathFor(logDir: string, chainId: string): string {\n  return join(logDir, \"specs\", `${chainId}.md`);\n}\n\n/**\n * Replace the caller's -p value with `prompt`, dropping every existing\n * -p/--print pair first (two -p flags on one claude command line are an error,\n * so the chain must never leave the brief in place when swapping prompts).\n */\nexport function swapPromptArg(claudeArgs: string[], prompt: string): string[] {\n  const out: string[] = [];\n  for (let i = 0; i < claudeArgs.length; i++) {\n    const a = claudeArgs[i];\n    if (a === \"-p\" || a === \"--print\") {\n      if (i + 1 < claudeArgs.length && !claudeArgs[i + 1].startsWith(\"-\")) i += 1;\n      continue;\n    }\n    out.push(a);\n  }\n  out.push(\"-p\", prompt);\n  return out;\n}\n\nexport function draftPrompt(brief: string, specPath: string): string {\n  return [\n    \"You are the DRAFT stage of a worker chain. Turn the operator's brief below into a full implementation spec and write it to\",\n    specPath,\n    \"with the Write tool.\",\n    \"\",\n    \"Use exactly these five sections as Markdown headings:\",\n    \"# Goal\",\n    \"# Constraints\",\n    \"# Files likely touched\",\n    \"# Acceptance checks\",\n    \"# Verification commands\",\n    \"\",\n    \"Read the repository first (Glob/Grep/Read) so the spec names real files and real commands. Do not implement anything.\",\n    \"\",\n    \"## Operator brief\",\n    \"\",\n    brief,\n  ].join(\"\\n\");\n}\n\nexport function implementPrompt(brief: string, specPath: string | null, spec: string | null): string {\n  const head = spec\n    ? [\n        \"You are the IMPLEMENT stage of a worker chain. The draft stage wrote the spec below (also at \" +\n          specPath +\n          \"). Implement it exactly, then run the spec's verification commands before finishing.\",\n        \"\",\n        \"## Spec\",\n        \"\",\n        spec,\n      ]\n    : [\"Implement the operator's brief below.\"];\n  return [...head, \"\", \"## Operator brief\", \"\", brief].join(\"\\n\");\n}\n\nexport function reviewPrompt(brief: string, specPath: string | null, spec: string | null): string {\n  const specPart = spec\n    ? [\n        \"\",\n        \"## Spec (also at \" + specPath + \")\",\n        \"\",\n        spec,\n      ]\n    : [];\n  return [\n    \"You are the REVIEW stage of a worker chain. The implement stage just ran. Read the repository's diff (run `git diff` and `git status`; use `git diff --stat` for the overview) and check it against the spec's acceptance checks and verification commands — run the checks when they are cheap. Do not fix anything you find; report it.\",\n    ...specPart,\n    \"\",\n    \"## Operator brief\",\n    \"\",\n    brief,\n  ].join(\"\\n\");\n}\n\nexport interface ChainOptions {\n  /** Class names, run in order: e.g. [\"draft\", \"implement\", \"review\"]. */\n  stages: string[];\n  /** Overrides the class of every stage when given (--class with --chain). */\n  className?: string;\n  providerFlag?: string;\n  modelFlag?: string;\n  label?: string;\n  noPane?: boolean;\n  mcpFlag?: string;\n  /** --spec value, resolved for display/recording (\"-\" for stdin). */\n  specPath?: string;\n  /** The operator's brief — the -p value of the run. */\n  brief: string;\n  /** The caller's claude args (allowedTools etc.); the -p value is swapped. */\n  claudeArgs: string[];\n  cwd?: string;\n  /** Internal: notified with the chain id once it exists (worker_run uses it). */\n  onChainStart?: (chainId: string) => void;\n  /** Internal: suppress result printing (the MCP shim's stdout is the RPC channel). */\n  quiet?: boolean;\n}\n\nexport interface ChainDeps {\n  /** Stage runner; tests inject a mock, production uses runWorker. */\n  runStage?: (opts: RunOptions) => Promise<number>;\n  /** logDir override for tests; default: the configured workers logDir. */\n  logDir?: string;\n}\n\n/** Run a chain of stages; returns the exit code of the first failed stage, 0 when all pass. */\nexport async function runChain(opts: ChainOptions, deps: ChainDeps = {}): Promise<number> {\n  const stages = opts.stages.map((s) => s.trim()).filter(Boolean);\n  if (!stages.length) throw new Error(\"--chain needs at least one class, e.g. --chain draft,implement\");\n  const runStage = deps.runStage ?? runWorker;\n  const logDir = deps.logDir ?? workersLogDir(readWorkersSection().workers);\n  const chainId = newWorkerId();\n  const specPath = specPathFor(logDir, chainId);\n  mkdirSync(join(logDir, \"specs\"), { recursive: true }); // the draft stage writes into it\n  const baseLabel = opts.label ?? shortText(opts.brief, 40);\n\n  appendLedger(ledgerPath(logDir), \"WORKER-CHAIN\", {\n    chain: chainId,\n    stages: stages.join(\",\"),\n    label: baseLabel,\n  });\n  opts.onChainStart?.(chainId);\n\n  let spec: string | null = null;\n  for (let i = 0; i < stages.length; i++) {\n    const stage = stages[i];\n    if (stage === \"draft\") {\n      spec = null; // a chain may legally start over; the draft rewrites it\n    } else if (spec === null && existsSync(specPath)) {\n      spec = readFileSync(specPath, \"utf8\");\n    }\n    const prompt =\n      stage === \"draft\"\n        ? draftPrompt(opts.brief, specPath)\n        : stage === \"review\"\n          ? reviewPrompt(opts.brief, spec ? specPath : null, spec)\n          : implementPrompt(opts.brief, spec ? specPath : null, spec);\n    process.stderr.write(\n      `chain ${chainId}: stage ${i + 1}/${stages.length} ${stage} (spec: ${specPath})\\n`\n    );\n    const rc = await runStage({\n      className: opts.className ?? stage,\n      providerFlag: opts.providerFlag,\n      modelFlag: opts.modelFlag,\n      label: `${baseLabel} · ${stage}`,\n      noPane: opts.noPane,\n      mcpFlag: opts.mcpFlag,\n      specPath: opts.specPath,\n      claudeArgs: swapPromptArg(opts.claudeArgs, prompt),\n      cwd: opts.cwd,\n      parent: chainId,\n      stage,\n      quiet: opts.quiet,\n    });\n    if (stage === \"draft\") {\n      if (!existsSync(specPath)) {\n        process.stderr.write(\n          `chain ${chainId}: draft stage produced no spec at ${specPath} — stopping. ` +\n            `Write the spec yourself and re-run without the draft stage.\\n`\n        );\n        appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", {\n          chain: chainId,\n          rc: rc !== 0 ? rc : 1,\n          failed: \"draft\",\n        });\n        return rc !== 0 ? rc : 1;\n      }\n      spec = readFileSync(specPath, \"utf8\");\n    }\n    if (rc !== 0) {\n      appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", {\n        chain: chainId,\n        rc,\n        failed: stage,\n      });\n      return rc;\n    }\n  }\n  appendLedger(ledgerPath(logDir), \"WORKER-CHAIN-END\", { chain: chainId, rc: 0 });\n  return 0;\n}\n"],"mappings":"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"}