cleargate 0.8.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/README.md +11 -0
  3. package/dist/MANIFEST.json +259 -28
  4. package/dist/{chunk-OM4FAEA7.js → chunk-Q3BTSXCK.js} +69 -3
  5. package/dist/chunk-Q3BTSXCK.js.map +1 -0
  6. package/dist/cli.cjs +2621 -548
  7. package/dist/cli.cjs.map +1 -1
  8. package/dist/cli.js +2548 -560
  9. package/dist/cli.js.map +1 -1
  10. package/dist/lib/ledger.cjs +120 -0
  11. package/dist/lib/ledger.cjs.map +1 -0
  12. package/dist/lib/ledger.d.cts +64 -0
  13. package/dist/lib/ledger.d.ts +64 -0
  14. package/dist/lib/ledger.js +96 -0
  15. package/dist/lib/ledger.js.map +1 -0
  16. package/dist/templates/cleargate-planning/.claude/agents/architect.md +10 -8
  17. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  18. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  19. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  20. package/dist/templates/cleargate-planning/.claude/agents/developer.md +29 -2
  21. package/dist/templates/cleargate-planning/.claude/agents/qa.md +50 -1
  22. package/dist/templates/cleargate-planning/.claude/agents/reporter.md +31 -9
  23. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  24. package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  25. package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +314 -96
  26. package/dist/templates/cleargate-planning/.claude/settings.json +4 -0
  27. package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +473 -0
  28. package/dist/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  29. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  30. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  31. package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +31 -0
  32. package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  33. package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  34. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +387 -27
  35. package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  36. package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  37. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  38. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  39. package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  40. package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +355 -13
  41. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +20 -20
  42. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  43. package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +125 -0
  44. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +24 -1
  45. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +32 -1
  46. package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  47. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +37 -3
  48. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +50 -0
  49. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  50. package/dist/templates/cleargate-planning/.cleargate/templates/proposal.md +17 -4
  51. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  52. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +55 -3
  53. package/dist/templates/cleargate-planning/CLAUDE.md +28 -10
  54. package/dist/templates/cleargate-planning/MANIFEST.json +259 -28
  55. package/dist/{whoami-CX7CXJD5.js → whoami-W4U6DPVG.js} +17 -17
  56. package/dist/whoami-W4U6DPVG.js.map +1 -0
  57. package/package.json +13 -2
  58. package/templates/cleargate-planning/.claude/agents/architect.md +10 -8
  59. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  60. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  61. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  62. package/templates/cleargate-planning/.claude/agents/developer.md +29 -2
  63. package/templates/cleargate-planning/.claude/agents/qa.md +50 -1
  64. package/templates/cleargate-planning/.claude/agents/reporter.md +31 -9
  65. package/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  66. package/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  67. package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +314 -96
  68. package/templates/cleargate-planning/.claude/settings.json +4 -0
  69. package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +473 -0
  70. package/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  71. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  72. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  73. package/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +31 -0
  74. package/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  75. package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  76. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +387 -27
  77. package/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  78. package/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  79. package/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  80. package/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  81. package/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  82. package/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +355 -13
  83. package/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +20 -20
  84. package/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  85. package/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +125 -0
  86. package/templates/cleargate-planning/.cleargate/templates/Bug.md +24 -1
  87. package/templates/cleargate-planning/.cleargate/templates/CR.md +32 -1
  88. package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  89. package/templates/cleargate-planning/.cleargate/templates/epic.md +37 -3
  90. package/templates/cleargate-planning/.cleargate/templates/hotfix.md +50 -0
  91. package/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  92. package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  93. package/templates/cleargate-planning/.cleargate/templates/story.md +55 -3
  94. package/templates/cleargate-planning/CLAUDE.md +28 -10
  95. package/templates/cleargate-planning/MANIFEST.json +259 -28
  96. package/dist/chunk-OM4FAEA7.js.map +0 -1
  97. package/dist/whoami-CX7CXJD5.js.map +0 -1
  98. package/templates/cleargate-planning/.cleargate/templates/proposal.md +0 -61
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../package.json","../src/commands/scaffold-lint.ts","../src/lib/scaffold-blocklist.ts","../src/commands/join.ts","../src/auth/identity-flow.ts","../src/commands/stamp.ts","../src/lib/codebase-version.ts","../src/lib/stamp-frontmatter.ts","../src/wiki/parse-frontmatter.ts","../src/lib/frontmatter-yaml.ts","../src/commands/init.ts","../src/init/copy-payload.ts","../src/init/merge-settings.ts","../src/init/inject-claude-md.ts","../src/init/inject-mcp-json.ts","../src/commands/wiki-build.ts","../src/wiki/scan.ts","../src/wiki/derive-bucket.ts","../src/wiki/derive-repo.ts","../src/wiki/git-sha.ts","../src/wiki/page-schema.ts","../src/wiki/synthesis/active-sprint.ts","../src/wiki/synthesis/render.ts","../src/wiki/synthesis/open-gates.ts","../src/wiki/synthesis/product-state.ts","../src/wiki/synthesis/roadmap.ts","../src/lib/manifest.ts","../src/lib/sha256.ts","../src/lib/prompts.ts","../src/lib/identity.ts","../src/commands/wiki-ingest.ts","../src/commands/wiki-lint.ts","../src/wiki/load-wiki.ts","../src/wiki/lint-checks.ts","../src/lib/work-item-type.ts","../src/lib/wiki-config.ts","../src/commands/wiki-query.ts","../src/commands/wiki-audit-status.ts","../src/commands/doctor.ts","../src/lib/pricing.ts","../src/commands/gate.ts","../src/commands/execution-mode.ts","../src/lib/readiness-predicates.ts","../src/lib/frontmatter-cache.ts","../src/commands/gate-run.ts","../src/commands/sprint.ts","../src/commands/story.ts","../src/commands/state.ts","../src/commands/stamp-tokens.ts","../src/lib/ledger-reader.ts","../src/commands/upgrade.ts","../src/lib/claude-md-surgery.ts","../src/lib/settings-json-surgery.ts","../src/lib/merge-ui.ts","../src/lib/editor.ts","../src/commands/uninstall.ts","../src/commands/sync.ts","../src/lib/sync-log.ts","../src/lib/conflict-detector.ts","../src/lib/merge-helper.ts","../src/lib/mcp-client.ts","../src/lib/intake.ts","../src/lib/slug.ts","../src/lib/active-criteria.ts","../src/lib/comments-cache.ts","../src/lib/wiki-comments-render.ts","../src/commands/pull.ts","../src/commands/push.ts","../src/commands/conflicts.ts","../src/commands/sync-log.ts","../src/commands/admin-login.ts","../src/commands/hotfix.ts","../src/commands/mcp-serve.ts","../src/auth/refresh.ts"],"sourcesContent":["// SPRINT-14 M5 dogfood smoke — STORY-099-01\nimport { Command } from 'commander';\nimport pkg from '../package.json' with { type: 'json' };\nimport { scaffoldLintHandler } from './commands/scaffold-lint.js';\nimport { joinHandler } from './commands/join.js';\nimport { stampHandler } from './commands/stamp.js';\nimport { initHandler } from './commands/init.js';\nimport { wikiBuildHandler } from './commands/wiki-build.js';\nimport { wikiIngestHandler } from './commands/wiki-ingest.js';\nimport { wikiLintHandler } from './commands/wiki-lint.js';\nimport { wikiQueryHandler } from './commands/wiki-query.js';\nimport { wikiAuditStatusHandler } from './commands/wiki-audit-status.js';\nimport { doctorHandler } from './commands/doctor.js';\nimport { gateCheckHandler, gateExplainHandler, gateQaHandler, gateArchHandler } from './commands/gate.js';\nimport { gateRunHandler } from './commands/gate-run.js';\nimport { sprintInitHandler, sprintCloseHandler, sprintArchiveHandler } from './commands/sprint.js';\nimport { storyStartHandler, storyCompleteHandler } from './commands/story.js';\nimport { stateUpdateHandler, stateValidateHandler } from './commands/state.js';\nimport { stampTokensHandler } from './commands/stamp-tokens.js';\nimport { upgradeHandler } from './commands/upgrade.js';\nimport { uninstallHandler } from './commands/uninstall.js';\nimport { syncHandler, syncCheckHandler } from './commands/sync.js';\nimport { pullHandler } from './commands/pull.js';\nimport { pushHandler } from './commands/push.js';\nimport { conflictsHandler } from './commands/conflicts.js';\nimport { syncLogHandler } from './commands/sync-log.js';\nimport { adminLoginHandler } from './commands/admin-login.js';\nimport { hotfixNewHandler } from './commands/hotfix.js';\nimport { mcpServeHandler } from './commands/mcp-serve.js';\n\nconst program = new Command();\n\nprogram\n .name('cleargate')\n .description('ClearGate CLI — connects AI agent teams to the ClearGate MCP server')\n .version(pkg.version, '-V, --version')\n .option('--profile <name>', 'configuration profile to use', 'default')\n .option('--mcp-url <url>', 'MCP server URL (overrides config file and env)')\n .showHelpAfterError('(use `cleargate --help`)');\n\nprogram\n .command('join <invite-url>')\n .description('join a ClearGate workspace using an invite URL')\n .option('--auth <provider>', 'identity provider: github | email')\n .option('--non-interactive', 'fail instead of prompting (CI mode)')\n .option('--code <code>', 'OTP code for non-interactive email auth')\n .action(async (inviteUrl: string, _opts: Record<string, unknown>, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n const cmdOpts = command.opts<{ auth?: string; nonInteractive?: boolean; code?: string }>();\n await joinHandler({\n inviteUrl,\n profile: globals.profile,\n mcpUrlFlag: globals.mcpUrl,\n // FLASHCARD #cli #commander #optional-key: only set keys when defined\n ...(cmdOpts.auth !== undefined ? { auth: cmdOpts.auth } : {}),\n ...(cmdOpts.nonInteractive === true ? { nonInteractive: true } : {}),\n ...(cmdOpts.code !== undefined ? { code: cmdOpts.code } : {}),\n });\n });\n\nprogram\n .command('init')\n .description('initialise a repo with ClearGate scaffold (CLAUDE.md block, hook config, agents, templates)')\n .option('--force', 'overwrite existing files that differ from the bundled payload')\n .option('--yes', 'non-interactive: accept all defaults without prompting')\n .option('--pin <ver>', 'CR-009: pin hook resolver to a specific cleargate CLI version (default: package version)')\n .action(async (opts: { force?: boolean; yes?: boolean; pin?: string }) => {\n await initHandler({ force: opts.force ?? false, yes: opts.yes ?? false, pin: opts.pin });\n });\n\nprogram\n .command('whoami')\n .description('print the currently authenticated agent identity')\n .action(async () => {\n const { whoamiHandler } = await import('./commands/whoami.js');\n const parentOpts = program.opts<{ profile: string; mcpUrl?: string }>();\n await whoamiHandler({\n profile: parentOpts.profile,\n mcpUrlFlag: parentOpts.mcpUrl,\n });\n });\n\nprogram\n .command('stamp <file>')\n .description('stamp ClearGate metadata fields into a file\\'s frontmatter')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampHandler(file, { dryRun: opts.dryRun });\n });\n\nconst wiki = program\n .command('wiki')\n .description('query or update the workspace wiki');\n\nwiki\n .command('build')\n .description('full rebuild of .cleargate/wiki/ from raw delivery items')\n .action(async () => {\n await wikiBuildHandler();\n });\n\nwiki\n .command('ingest <file>')\n .description('ingest a single raw delivery file into the wiki')\n .action(async (file: string) => {\n await wikiIngestHandler({ rawPath: file });\n });\n\nwiki\n .command('lint')\n .description('check wiki pages for drift vs raw sources')\n .option('--suggest', 'advisory mode — exit 0, emit suggestions only')\n .helpOption('--help', [\n 'Usage: cleargate wiki lint [--suggest]',\n '',\n 'Enforcement mode (default): exits 1 on any finding.',\n 'Suggest mode (--suggest): exits 0, prefixes findings with [advisory],',\n ' and emits Karpathy cross-ref discovery candidates.',\n ].join('\\n'))\n .action(async (_opts: Record<string, unknown>, command: Command) => {\n const cmdOpts = command.opts<{ suggest?: boolean }>();\n await wikiLintHandler({\n mode: cmdOpts.suggest ? 'suggest' : 'enforce',\n });\n });\n\nwiki\n .command('query <terms...>')\n .description('search the wiki index for matching work items')\n .option('--persist', 'write result as a topic page under wiki/topics/')\n .addHelpText('after', [\n '',\n 'NOTE: CLI synthesis is grep-and-list. For NL synthesis with the',\n 'cleargate-wiki-query subagent, invoke from a Claude Code session.',\n 'This diverges from PROPOSAL-002 §2.2 intentionally for testability',\n 'and offline/scripted use.',\n ].join('\\n'))\n .action(async (terms: string[], opts: { persist?: boolean }) => {\n await wikiQueryHandler({\n query: terms.join(' '),\n persist: opts.persist ?? false,\n });\n });\n\nwiki\n .command('audit-status')\n .description('detect raw-item status/location drift; --fix applies safe corrections')\n .option('--fix', 'apply safe status corrections to frontmatter')\n .option('--yes', 'required together with --fix to confirm writes')\n .option('--quiet', 'suppress diff output')\n .action(async (opts: { fix?: boolean; yes?: boolean; quiet?: boolean }) => {\n await wikiAuditStatusHandler(opts);\n });\n\nconst gate = program\n .command('gate')\n .description('evaluate readiness gates for a ClearGate work-item file');\n\ngate\n .command('check <file>')\n .description('evaluate readiness criteria and write result to frontmatter')\n .option('-v, --verbose', 'show full expected-vs-actual detail per criterion')\n .option('--transition <name>', 'override auto-detected transition name')\n .action(async (file: string, opts: { verbose?: boolean; transition?: string }) => {\n await gateCheckHandler(file, { verbose: opts.verbose, transition: opts.transition });\n });\n\ngate\n .command('explain <file>')\n .description('render cached gate result in ≤50 agent tokens (read-only)')\n .action(async (file: string) => {\n await gateExplainHandler(file);\n });\n\ngate\n .command('qa <worktree> <branch>')\n .description('run QA pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateQaHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\ngate\n .command('arch <worktree> <branch>')\n .description('run Architect pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateArchHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\n// STORY-018-03: config-driven gates. Commander v12 does NOT treat `<name>` as a\n// catch-all fallback when sibling literal subcommands exist (QA'd on 2026-04-25\n// — it emits \"unknown command\" before a parameterized handler can fire). Since\n// the gate names are a closed set, enumerate them explicitly.\nfor (const gateName of ['precommit', 'test', 'typecheck', 'lint'] as const) {\n gate\n .command(gateName)\n .description(`run the configured ${gateName} gate command`)\n .option('--strict', 'exit non-zero if gate not configured')\n .action((opts: { strict?: boolean }) => {\n gateRunHandler(gateName, { strict: opts.strict === true ? true : undefined });\n });\n}\n\nprogram\n .command('scaffold-lint')\n .description('grep cleargate-planning/ for stack-specific strings; fail on leaks')\n .option('--fix-hint', 'emit placeholder suggestions per finding')\n .option('--versions', 'also flag semver-shaped strings')\n .option('--quiet', 'suppress per-finding output; exit code only')\n .action(async (opts: { fixHint?: boolean; versions?: boolean; quiet?: boolean }) => {\n await scaffoldLintHandler(opts);\n });\n\nconst sprint = program\n .command('sprint')\n .description('sprint lifecycle commands (v2 only — inert under v1)');\n\nsprint\n .command('init <sprint-id>')\n .description('initialise a new sprint — creates state.json and worktree skeleton')\n .requiredOption('--stories <csv>', 'comma-separated story IDs for this sprint')\n .action((sprintId: string, opts: { stories: string }) => {\n sprintInitHandler({ sprintId, stories: opts.stories });\n });\n\nsprint\n .command('close <sprint-id>')\n .description('close a sprint — validates all stories are terminal, runs prefill + suggest_improvements')\n .option('--assume-ack', 'skip the \"waiting for Reporter\" gate and flip state to Completed directly')\n .action((sprintId: string, opts: { assumeAck?: boolean }) => {\n const handlerOpts: { sprintId: string; assumeAck?: boolean } = { sprintId };\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n if (opts.assumeAck === true) {\n handlerOpts.assumeAck = true;\n }\n sprintCloseHandler(handlerOpts);\n });\n\nsprint\n .command('archive <sprint-id>')\n .description('archive a completed sprint — move pending-sync files, clear .active, merge + delete sprint branch')\n .option('--dry-run', 'print the archive plan without making any changes')\n .action(async (sprintId: string, opts: { dryRun?: boolean }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const handlerOpts: { sprintId: string; dryRun?: boolean } = { sprintId };\n if (opts.dryRun === true) {\n handlerOpts.dryRun = true;\n }\n await sprintArchiveHandler(handlerOpts);\n });\n\nconst story = program\n .command('story')\n .description('story lifecycle commands (v2 only — inert under v1)');\n\nstory\n .command('start <story-id>')\n .description('create a git worktree for a story on the sprint branch')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyStartHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nstory\n .command('complete <story-id>')\n .description('mark a story complete and clean up its worktree (stub — requires complete_story.mjs)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyCompleteHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nconst state = program\n .command('state')\n .description('state.json management commands (v2 only — inert under v1)');\n\nstate\n .command('update <story-id> <new-state>')\n .description('update a story\\'s state in state.json')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup (overrides .active sentinel)')\n .action((storyId: string, newState: string, opts: { sprint?: string }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const cliOpts: Parameters<typeof stateUpdateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateUpdateHandler({ storyId, newState }, cliOpts);\n });\n\nstate\n .command('validate <sprint-id>')\n .description('validate all story states in a sprint\\'s state.json')\n .option('--sprint <id>', 'override sprint ID for execution_mode lookup')\n .action((sprintId: string, opts: { sprint?: string }) => {\n const cliOpts: Parameters<typeof stateValidateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateValidateHandler({ sprintId: opts.sprint ?? sprintId }, cliOpts);\n });\n\nprogram\n .command('stamp-tokens <file>')\n .description('stamp draft_tokens from token-ledger into a work-item file (hook-invoked)')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampTokensHandler(file, { dryRun: opts.dryRun });\n });\n\nconst admin = program\n .command('admin')\n .description('administrative operations (login, create-project, invite, issue-token, revoke-token)');\n\nadmin\n .command('login')\n .description('log in as a ClearGate admin via GitHub OAuth device flow')\n .option('--mcp-url <url>', 'MCP server URL (overrides CLEARGATE_MCP_URL and config file)')\n .action(async (opts: { mcpUrl?: string }, command: Command) => {\n const globals = command.parent!.parent!.opts<{ mcpUrl?: string }>();\n await adminLoginHandler({\n mcpUrl: opts.mcpUrl ?? globals.mcpUrl,\n });\n });\n\nadmin\n .command('bootstrap-root <handle>')\n .description('seed the first root admin in admin_users (idempotent)')\n .option('--database-url <url>', 'Postgres connection string; falls back to DATABASE_URL env')\n .option('--force', 'override second-root guard / promote non-root user to root')\n .action(async (handle: string, opts: { databaseUrl?: string; force?: boolean }) => {\n const { bootstrapRootHandler } = await import('./commands/bootstrap-root.js');\n await bootstrapRootHandler({ handle, databaseUrl: opts.databaseUrl, force: opts.force ?? false });\n });\n\nprogram\n .command('doctor')\n .description('diagnose scaffold drift, hook health, blocked items, and token cost')\n .option('--check-scaffold', 'check scaffold files for drift against install snapshot')\n .option('--session-start-mode', 'hidden: enables daily throttle (used by session-start hook)', false)\n .option('--session-start', 'emit blocked pending-sync items summary (used by SessionStart hook)')\n .option('--pricing <file>', 'compute USD cost estimate from a work item\\'s draft_tokens')\n .option('--can-edit <file>', 'CR-008: exit 0 if editing file is allowed, exit 1 if planning required')\n .option('--cwd <dir>', 'working directory for the doctor check (default: process.cwd())')\n .option('-v, --verbose', 'show per-file drift detail')\n .addHelpText('after', [\n '',\n 'Modes (mutually exclusive):',\n ' --check-scaffold Compute drift for all tracked scaffold files.',\n ' Writes .cleargate/.drift-state.json.',\n ' --session-start List blocked pending-sync items (≤10, ≤100 tokens).',\n ' --pricing <file> Compute USD estimate from a work item\\'s draft_tokens.',\n ' --can-edit <file> Check if editing a file requires a planning work item.',\n ' (default) Print a minimal hook-config health report.',\n '',\n 'Exit codes:',\n ' 0 Clean — no blockers, no config errors.',\n ' 1 Blocked items or advisory issues — see stdout.',\n ' 2 ClearGate misconfigured or partially installed — see stdout for remediation.',\n ].join('\\n'))\n .action(async (opts: { checkScaffold?: boolean; sessionStartMode?: boolean; sessionStart?: boolean; pricing?: string; canEdit?: string; cwd?: string; verbose?: boolean }) => {\n await doctorHandler({\n checkScaffold: opts.checkScaffold,\n sessionStartMode: opts.sessionStartMode,\n sessionStart: opts.sessionStart,\n pricing: !!opts.pricing,\n pricingFile: opts.pricing,\n canEdit: !!opts.canEdit,\n canEditFile: opts.canEdit,\n verbose: opts.verbose,\n }, opts.cwd ? { cwd: opts.cwd } : undefined);\n });\n\nprogram\n .command('upgrade')\n .description('three-way merge scaffold files with upstream changes')\n .option('--dry-run', 'print plan without making any changes')\n .option('--yes', 'auto-accept \"take theirs\" for all merge-3way files (non-interactive)')\n .option('--only <tier>', 'restrict to a specific scaffold tier (protocol/template/agent/hook/skill/cli-config)')\n .addHelpText('after', [\n '',\n 'Overwrite policies:',\n ' always — silent overwrite with package content',\n ' never — silent skip',\n ' preserve — silent skip',\n ' merge-3way — interactive: [k]eep mine / [t]ake theirs / [e]dit in $EDITOR',\n '',\n '--yes auto-accepts [t]ake theirs for all merge-3way files.',\n ].join('\\n'))\n .action(async (opts: { dryRun?: boolean; yes?: boolean; only?: string }) => {\n await upgradeHandler({ dryRun: opts.dryRun, yes: opts.yes, only: opts.only });\n });\n\nprogram\n .command('uninstall')\n .description('remove ClearGate scaffold from a project (preservation-first)')\n .option('--dry-run', 'preview planned actions without making any changes (CI-safe)')\n .option('--preserve <tiers>', 'comma-separated tier ids to force-preserve (default: user-artifact)')\n .option('--remove <tiers>', 'comma-separated tier ids to force-remove; use \"all\" to remove everything including user artifacts (DANGEROUS)')\n .option('--yes', 'skip typed project-name confirmation (dangerous — use in scripts/CI)')\n .option('--path <dir>', 'target directory (must contain .cleargate/.install-manifest.json); defaults to cwd')\n .option('--force', 'bypass uncommitted-changes safety check (not applicable for non-git targets)')\n .addHelpText('after', [\n '',\n 'Preservation defaults:',\n ' user-artifact tier → kept (FLASHCARD.md, archive, pending-sync, sprint REPORT.md)',\n ' framework tiers → removed (protocol, template, agent, hook, skill, cli-config)',\n '',\n 'Always removed (no prompt): .claude/agents/*.md, ClearGate hooks,',\n ' .claude/skills/flashcard/, CLAUDE.md CLEARGATE block,',\n ' `cleargate` from package.json, .install-manifest.json, .drift-state.json.',\n '',\n 'Non-git targets: uncommitted-changes check is skipped silently.',\n ].join('\\n'))\n .action(async (opts: {\n dryRun?: boolean;\n preserve?: string;\n remove?: string;\n yes?: boolean;\n path?: string;\n force?: boolean;\n }) => {\n await uninstallHandler({\n dryRun: opts.dryRun,\n preserve: opts.preserve ? opts.preserve.split(',').map((s) => s.trim()) : undefined,\n remove: opts.remove ? opts.remove.split(',').map((s) => s.trim()) : undefined,\n yes: opts.yes,\n path: opts.path,\n force: opts.force,\n });\n });\n\nprogram\n .command('sync')\n .description('pull remote updates, resolve conflicts, push local changes')\n .option('--dry-run', 'print plan without making any changes or sync-log entries')\n .option('--check', 'read-only drift probe — prints JSON, no mutation, hook-safe')\n .action(async (opts: { dryRun?: boolean; check?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n if (opts.check) {\n await syncCheckHandler({ profile: globals.profile });\n return;\n }\n await syncHandler({ dryRun: opts.dryRun ?? false, profile: globals.profile });\n });\n\nprogram\n .command('pull <id-or-remote-id>')\n .description('pull a single item from the remote PM tool by local ID or remote_id')\n .option('--comments', 'also pull comments for this item (STORY-010-06; not yet implemented)')\n .action(async (idOrRemoteId: string, opts: { comments?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pullHandler(idOrRemoteId, { comments: opts.comments, profile: globals.profile });\n });\n\nprogram\n .command('push <file>')\n .description('push a local work item to the MCP server (requires approved: true in frontmatter)')\n .option('--revert <id-or-remote-id>', 'soft-revert a pushed item by setting status to archived-without-shipping')\n .option('--force', 'bypass the \"done\" status guard when reverting')\n .addHelpText('after', [\n '',\n 'Push mode:',\n ' Reads local frontmatter. Requires approved: true — exits 1 without network call otherwise.',\n ' On success: writes pushed_by + pushed_at from server back to local frontmatter.',\n ' Appends sync-log entry op=push.',\n '',\n 'Revert mode (--revert <id-or-remote-id>):',\n ' Calls cleargate_sync_status with status=archived-without-shipping.',\n ' Does NOT delete the remote item or remove local remote_id.',\n ' Refuses if local status=done unless --force is passed.',\n ' Appends sync-log entry op=push-revert.',\n ].join('\\n'))\n .action(async (file: string, opts: { revert?: string; force?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pushHandler(file, { revert: opts.revert, force: opts.force, profile: globals.profile });\n });\n\nprogram\n .command('conflicts')\n .description('list unresolved sync conflicts from .cleargate/.conflicts.json')\n .option('--refresh', 'force a new /auth/refresh even if the cached token is still valid')\n .action(async (opts: { refresh?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await conflictsHandler({ refresh: opts.refresh, profile: globals.profile });\n });\n\nprogram\n .command('sync-log')\n .description('filter and print sync-log entries')\n .option('--actor <email>', 'filter by actor email')\n .option('--op <op>', 'filter by operation (push|pull|pull-intake|...)')\n .option('--target <id>', 'filter by target work item ID')\n .option('--limit <n>', 'maximum number of entries to show (default 50)', '50')\n .action(async (opts: { actor?: string; op?: string; target?: string; limit?: string }) => {\n await syncLogHandler({\n actor: opts.actor,\n op: opts.op,\n target: opts.target,\n limit: opts.limit !== undefined ? parseInt(opts.limit, 10) : 50,\n });\n });\n\nconst hotfix = program\n .command('hotfix')\n .description('hotfix lane commands (off-sprint trivial fix scaffolding)');\n\n// FLASHCARD #cli #commander #subcommand-routing (2026-04-25): Commander v12\n// does NOT treat `<verb>` as a catch-all fallback when sibling literal\n// subcommands exist. Since `new` is the only verb for now, enumerate it\n// explicitly as a literal subcommand.\nhotfix\n .command('new <slug>')\n .description('scaffold a new HOTFIX-NNN_<slug>.md in pending-sync/')\n .action((slug: string) => {\n hotfixNewHandler({ slug });\n });\n\n// BUG-019: stdio↔HTTP MCP proxy with auto-refresh auth.\nconst mcp = program\n .command('mcp')\n .description('MCP-server bridge commands (stdio shim, registration helpers)');\n\nmcp\n .command('serve')\n .description('run a stdio MCP server that proxies to the cleargate HTTP /mcp endpoint with auto-refresh Bearer auth')\n .action(async (_opts, command: Command) => {\n const globals = command.parent!.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await mcpServeHandler({\n profile: globals.profile,\n ...(globals.mcpUrl !== undefined ? { mcpUrlFlag: globals.mcpUrl } : {}),\n });\n });\n\nvoid program.parseAsync(process.argv);\n","{\n \"name\": \"cleargate\",\n \"version\": \"0.8.2\",\n \"private\": false,\n \"type\": \"module\",\n \"description\": \"Planning framework for Claude Code agents — sprint/epic/story protocol, four-agent loop (architect/developer/qa/reporter), Karpathy-style awareness wiki.\",\n \"license\": \"MIT\",\n \"bin\": {\n \"cleargate\": \"dist/cli.js\"\n },\n \"main\": \"./dist/cli.cjs\",\n \"module\": \"./dist/cli.js\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/cli.d.ts\",\n \"import\": \"./dist/cli.js\",\n \"require\": \"./dist/cli.cjs\"\n },\n \"./admin-api\": {\n \"types\": \"./dist/admin-api/index.d.ts\",\n \"import\": \"./dist/admin-api/index.js\",\n \"require\": \"./dist/admin-api/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"scripts\": {\n \"prebuild\": \"tsx scripts/build-manifest.ts && node scripts/copy-planning-payload.mjs\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"pretest\": \"npm run build\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@napi-rs/keyring\": \"^1.2.0\",\n \"commander\": \"^12\",\n \"diff\": \"^5.2.2\",\n \"js-yaml\": \"^4.1.0\",\n \"pg\": \"^8.12.0\",\n \"zod\": \"^4.3.0\"\n },\n \"devDependencies\": {\n \"@types/diff\": \"^5.2.3\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^24.0.0\",\n \"@types/pg\": \"^8.11.10\",\n \"tsup\": \"^8\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.8.0\",\n \"vitest\": \"^2.1.0\"\n }\n}\n","/**\n * scaffold-lint.ts — `cleargate scaffold-lint` command handler.\n *\n * STORY-018-04: Scans cleargate-planning/ for stack-specific strings that\n * should not appear in the installable scaffold.\n *\n * Exit codes: 0 = clean, 1 = findings, 2 = config/parse error.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n DEFAULT_BLOCKLIST,\n getTermCategory,\n CATEGORY_PLACEHOLDERS,\n parseAllowlist,\n parseUserBlocklist,\n type AllowlistEntry,\n} from '../lib/scaffold-blocklist.js';\n\n// ─── Scan extensions ──────────────────────────────────────────────────────────\n\nconst SCAN_EXTENSIONS = new Set(['.md', '.sh', '.mjs', '.json']);\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ScaffoldLintOptions {\n fixHint?: boolean;\n versions?: boolean;\n quiet?: boolean;\n cwd?: string;\n planningDir?: string; // override for tests; defaults to path.join(cwd, 'cleargate-planning')\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n}\n\ninterface Finding {\n file: string; // relative to planningDir parent\n line: number;\n term: string;\n context: string; // matched line truncated to 80 chars\n}\n\n// ─── scaffoldLintHandler ──────────────────────────────────────────────────────\n\nexport function scaffoldLintHandler(opts: ScaffoldLintOptions): void {\n const stdoutFn = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n opts.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = opts.cwd ?? process.cwd();\n const planningDir = opts.planningDir ?? path.join(cwd, 'cleargate-planning');\n\n const result = runScaffoldLint(opts, cwd, planningDir, stdoutFn, stderrFn);\n\n if (result.exitCode === 0) {\n stdoutFn('scaffold-lint: clean');\n }\n\n if (result.exitCode !== 0) {\n exitFn(result.exitCode);\n }\n}\n\ninterface LintResult {\n exitCode: number;\n}\n\nfunction runScaffoldLint(\n opts: ScaffoldLintOptions,\n cwd: string,\n planningDir: string,\n stdoutFn: (s: string) => void,\n stderrFn: (s: string) => void,\n): LintResult {\n // ── 1. Load user blocklist (fail-fast on malformed) ─────────────────────────\n const userBlocklistPath = path.join(cwd, '.cleargate', 'scaffold-blocklist.txt');\n let userTerms: string[] = [];\n\n if (fs.existsSync(userBlocklistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(userBlocklistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: error reading ${userBlocklistPath}: ${String(err)}`);\n return { exitCode: 2 };\n }\n const parsed = parseUserBlocklist(raw, userBlocklistPath, stderrFn);\n if (parsed === null) {\n return { exitCode: 2 };\n }\n userTerms = parsed;\n }\n\n // ── 2. Load allowlist (skip + warn on malformed) ──────────────────────────\n const allowlistPath = path.join(cwd, '.cleargate', 'scaffold-allowlist.txt');\n let allowlistEntries: AllowlistEntry[] = [];\n\n if (fs.existsSync(allowlistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(allowlistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: warning: error reading ${allowlistPath}: ${String(err)}`);\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n allowlistEntries = parseAllowlist(raw!, allowlistPath, stderrFn);\n } catch {\n // parseAllowlist is synchronous and doesn't throw in normal usage\n }\n }\n\n // ── 3. Build combined blocklist ───────────────────────────────────────────\n const allTerms = [...DEFAULT_BLOCKLIST, ...userTerms];\n\n // ── 4. Walk cleargate-planning/ ───────────────────────────────────────────\n if (!fs.existsSync(planningDir)) {\n // No planning dir — nothing to scan, exit clean\n stdoutFn('scaffold-lint: clean');\n return { exitCode: 0 };\n }\n\n const files = walkDir(planningDir);\n\n // ── 5. Scan files ─────────────────────────────────────────────────────────\n const findings: Finding[] = [];\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!SCAN_EXTENSIONS.has(ext)) continue;\n\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf8');\n } catch {\n continue; // skip unreadable files\n }\n\n // Relative path from cwd for reporting\n const relPath = path.relative(cwd, filePath).replace(/\\\\/g, '/');\n\n const lines = content.split('\\n');\n for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {\n const lineNum = lineIdx + 1;\n const lineText = lines[lineIdx];\n\n for (const term of allTerms) {\n const re = new RegExp(escapeRegex(term), 'gi');\n if (!re.test(lineText)) continue;\n\n // Check allowlist\n if (isAllowlisted(relPath, term, allowlistEntries)) continue;\n\n findings.push({\n file: relPath,\n line: lineNum,\n term: term.toLowerCase(),\n context: lineText.slice(0, 80),\n });\n // Only record the FIRST matching term per line per occurrence\n // (break inner term loop to avoid duplicate lines for the same match)\n break;\n }\n }\n }\n\n // ── 6. Sort findings by file asc, line asc ───────────────────────────────\n findings.sort((a, b) => {\n if (a.file < b.file) return -1;\n if (a.file > b.file) return 1;\n return a.line - b.line;\n });\n\n // ── 7. Emit findings ──────────────────────────────────────────────────────\n if (findings.length === 0) {\n return { exitCode: 0 };\n }\n\n if (!opts.quiet) {\n for (const f of findings) {\n const line = `${f.file}:${f.line}: ${f.term} — example context: ${f.context}`;\n stderrFn(line);\n\n if (opts.fixHint) {\n const category = getTermCategory(f.term);\n const placeholder = category ? CATEGORY_PLACEHOLDERS.get(category) : undefined;\n if (placeholder) {\n stderrFn(` hint: replace with ${placeholder}`);\n } else {\n stderrFn(` hint: replace with <your-replacement>`);\n }\n }\n }\n }\n\n return { exitCode: 1 };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction walkDir(dir: string): string[] {\n const results: string[] = [];\n\n function recurse(current: string): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(current, { withFileTypes: true }) as fs.Dirent[];\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = path.join(current, entry.name);\n if (entry.isDirectory()) {\n recurse(fullPath);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n }\n\n recurse(dir);\n return results;\n}\n\nfunction escapeRegex(term: string): string {\n return term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction isAllowlisted(\n relPath: string,\n term: string,\n entries: AllowlistEntry[],\n): boolean {\n const termLower = term.toLowerCase();\n\n for (const entry of entries) {\n if (entry.term !== termLower) continue;\n\n if (!entry.glob) {\n // Global suppression for this term\n return true;\n }\n\n // Glob is relative to repo root (cwd)\n // Use simple star matching via path.matchesGlob (Node 24)\n if (matchesGlob(relPath, entry.glob)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Minimal glob matcher using Node 24's path.matchesGlob when available,\n * with a fallback star-replace regex for older environments.\n */\nfunction matchesGlob(filePath: string, glob: string): boolean {\n // Node 24 built-in\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pathModule = path as any;\n if (typeof pathModule.matchesGlob === 'function') {\n return pathModule.matchesGlob(filePath, glob);\n }\n } catch {\n // fall through\n }\n\n // Fallback: simple star matching\n const regexStr = glob\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '§DSTAR§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§DSTAR§/g, '.*');\n\n return new RegExp(`^${regexStr}$`).test(filePath);\n}\n","/**\n * scaffold-blocklist.ts — Default blocklist for `cleargate scaffold-lint`.\n *\n * STORY-018-04: Stack-leak detection. These terms should not appear in the\n * installable scaffold under cleargate-planning/ — they indicate ClearGate's\n * own dogfooding vocabulary leaking into the template that downstream users\n * receive.\n *\n * Categories: ORMs, web frameworks, infra, DB engines, cache/queue, styling.\n */\n\n// ─── Blocklist categories ─────────────────────────────────────────────────────\n\nconst ORMS = ['drizzle', 'prisma', 'sequelize', 'typeorm'] as const;\n\nconst WEB_FRAMEWORKS = [\n 'fastify',\n 'express',\n 'hono',\n 'svelte',\n 'sveltekit',\n 'react',\n 'next.js',\n 'nextjs',\n 'nuxt',\n 'remix',\n 'vue',\n] as const;\n\nconst INFRA = ['coolify', 'vercel', 'netlify', 'heroku', 'render.com', 'fly.io'] as const;\n\nconst DB_ENGINES = ['postgres', 'postgresql', 'mysql', 'sqlite', 'mongodb', 'dynamodb'] as const;\n\nconst CACHE_QUEUE = ['redis', 'ioredis', 'memcached', 'rabbitmq', 'kafka'] as const;\n\nconst STYLING = ['daisyui', 'tailwind', 'bootstrap', 'mui'] as const;\n\n// ─── Category map (for --fix-hint placeholders) ───────────────────────────────\n\nexport type BlocklistCategory = 'orm' | 'framework' | 'infra' | 'db' | 'cache' | 'styling';\n\nconst TERM_CATEGORY_MAP: ReadonlyMap<string, BlocklistCategory> = new Map<string, BlocklistCategory>([\n ...ORMS.map((t): [string, BlocklistCategory] => [t, 'orm']),\n ...WEB_FRAMEWORKS.map((t): [string, BlocklistCategory] => [t, 'framework']),\n ...INFRA.map((t): [string, BlocklistCategory] => [t, 'infra']),\n ...DB_ENGINES.map((t): [string, BlocklistCategory] => [t, 'db']),\n ...CACHE_QUEUE.map((t): [string, BlocklistCategory] => [t, 'cache']),\n ...STYLING.map((t): [string, BlocklistCategory] => [t, 'styling']),\n]);\n\nexport const CATEGORY_PLACEHOLDERS: ReadonlyMap<BlocklistCategory, string> = new Map([\n ['orm', '<your-orm>'],\n ['framework', '<your-framework>'],\n ['infra', '<your-infra>'],\n ['db', '<your-db>'],\n ['cache', '<your-cache>'],\n ['styling', '<your-styling>'],\n]);\n\n// ─── Public exports ───────────────────────────────────────────────────────────\n\nexport const DEFAULT_BLOCKLIST: readonly string[] = [\n ...ORMS,\n ...WEB_FRAMEWORKS,\n ...INFRA,\n ...DB_ENGINES,\n ...CACHE_QUEUE,\n ...STYLING,\n];\n\n/**\n * Get the category for a blocklist term (default or user-supplied).\n * Returns undefined for user-supplied terms that don't map to a category.\n */\nexport function getTermCategory(term: string): BlocklistCategory | undefined {\n return TERM_CATEGORY_MAP.get(term.toLowerCase());\n}\n\n// ─── Allowlist parser ─────────────────────────────────────────────────────────\n\nexport interface AllowlistEntry {\n term: string;\n glob?: string;\n}\n\nexport interface ParsedAllowlist {\n entries: AllowlistEntry[];\n warnings: string[];\n}\n\n/**\n * Parse `.cleargate/scaffold-allowlist.txt` content.\n *\n * Format per line: `<term>[ <file-glob>]`\n * - Lines starting with `#` are comments.\n * - Blank lines are skipped.\n * - Term is case-insensitive substring.\n * - Glob is optional; when absent, suppresses ALL files.\n *\n * Returns parsed entries + any warnings (for malformed lines — per orchestrator\n * decision: skip + warn rather than fail; malformed-allowlist exit-2 is\n * handled by the blocklist file, not the allowlist).\n *\n * NOTE: Per M2.md \"malformed lines: fail fast\" applies to the *blocklist* file\n * format only. For allowlist, skip + warn to stderr per orchestrator Q2 decision.\n */\nexport function parseAllowlist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): AllowlistEntry[] {\n const entries: AllowlistEntry[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n // Skip blank lines and comments\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // Split on whitespace — first token is term, optional second is glob\n const parts = trimmed.split(/\\s+/);\n\n if (parts.length === 0 || parts[0] === '') {\n stderrFn(`scaffold-lint: warning: malformed line ${i + 1} in ${filePath}: ${raw}`);\n continue;\n }\n\n const entry: AllowlistEntry = { term: parts[0].toLowerCase() };\n if (parts.length >= 2) {\n entry.glob = parts[1];\n }\n entries.push(entry);\n }\n\n return entries;\n}\n\n/**\n * Parse `.cleargate/scaffold-blocklist.txt` (user-extensible).\n * Fail-fast on malformed lines (exit code 2).\n * Returns null if a parse error was encountered (caller should exit 2).\n */\nexport function parseUserBlocklist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): string[] | null {\n const terms: string[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // A valid term is a single non-whitespace token\n if (/\\s/.test(trimmed)) {\n stderrFn(`scaffold-lint: malformed line ${i + 1} in ${filePath}: ${raw}`);\n return null; // signal exit 2\n }\n\n terms.push(trimmed.toLowerCase());\n }\n\n return terms;\n}\n","/**\n * `cleargate join` — two-step identity-bound workspace invitation redemption.\n *\n * Flow:\n * 1. Parse invite URL/token.\n * 2. Pick provider (--auth flag, interactive picker, or error in non-interactive mode).\n * 3. POST /join/:token/challenge { provider } → get challengeId + clientHints.\n * 4. Drive provider flow:\n * - GitHub: client-side device-flow polling (via identity-flow.startDeviceFlow).\n * - Email: OTP prompt with 3-in-process retries (OD-3 resolution).\n * 5. POST /join/:token/complete { challenge_id, proof } → get refresh_token.\n * 6. Seat token via TokenStore.\n *\n * CR-006 EPIC-019.\n */\nimport * as os from 'node:os';\nimport { loadConfig } from '../config.js';\nimport { createTokenStore } from '../auth/factory.js';\nimport {\n pickProvider,\n startDeviceFlow,\n mapProviderError,\n DeviceFlowError,\n IdentityFlowError,\n type Provider,\n} from '../auth/identity-flow.js';\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\nconst UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n// GitHub device-flow token endpoint (same as admin-login)\nconst GITHUB_DEVICE_FLOW_URL = 'https://github.com/login/oauth/access_token';\n\nexport interface JoinOptions {\n inviteUrl: string;\n profile: string;\n mcpUrlFlag?: string;\n /** Identity provider (--auth flag): 'github' | 'email'. */\n auth?: string;\n /** Non-interactive CI mode (--non-interactive flag). */\n nonInteractive?: boolean;\n /** Pre-seeded OTP code for non-interactive email auth (--code flag). */\n code?: string;\n /** Test seam: replaces globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n /** Test seam: replaces createTokenStore */\n createStore?: typeof createTokenStore;\n /** Test seam: replaces os.hostname() */\n hostname?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: replaces process.stdin for interactive prompts */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: whether stdin is a TTY (default: process.stdin.isTTY) */\n isTTY?: boolean;\n /** Test seam: sleepFn for device-flow polling (default: real setTimeout) */\n sleepFn?: (ms: number) => Promise<void>;\n /** Test seam: override device-flow poll interval in ms (0 = instant in tests) */\n intervalOverrideMs?: number;\n}\n\nexport async function joinHandler(opts: JoinOptions): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const hostname = opts.hostname ?? (() => os.hostname());\n const isTTY = opts.isTTY ?? (process.stdin.isTTY === true);\n\n // ── Parse inviteUrl → { token, baseUrl } ──────────────────────────────────\n let token: string;\n let baseUrl: string;\n\n try {\n if (UUID_V4_RE.test(opts.inviteUrl)) {\n // Bare UUID form — requires mcpUrl from config\n token = opts.inviteUrl;\n const cfg = loadConfig({\n flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },\n });\n if (!cfg.mcpUrl) {\n stderr(\n 'cleargate: bare invite token requires mcpUrl. Pass --mcp-url <url> or set CLEARGATE_MCP_URL.\\n',\n );\n exit(5);\n return;\n }\n baseUrl = cfg.mcpUrl;\n } else {\n // Full URL form: https://host/join/<uuid>\n const url = new URL(opts.inviteUrl);\n const m = url.pathname.match(/^\\/join\\/([0-9a-f-]{36})$/i);\n if (!m || !UUID_V4_RE.test(m[1]!)) {\n throw new Error('bad path');\n }\n token = m[1]!;\n baseUrl = url.origin;\n }\n } catch {\n stderr('cleargate: invalid invite URL or token format.\\n');\n exit(5);\n return;\n }\n\n // ── Non-interactive guards ────────────────────────────────────────────────\n if (opts.nonInteractive && !opts.auth) {\n stderr('cleargate: --auth required in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'email' && !opts.code) {\n stderr('cleargate: --code required for email provider in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'github') {\n stderr('cleargate: GitHub auth requires browser interaction; use `--auth email` for non-interactive flows\\n');\n exit(1);\n return;\n }\n\n // ── Pick provider ─────────────────────────────────────────────────────────\n let provider: Provider;\n try {\n provider = await pickProvider({\n flag: opts.auth,\n isTTY: !opts.nonInteractive && isTTY,\n available: ['github', 'email'],\n stdin: opts.stdin,\n stdout,\n });\n } catch (err) {\n if (err instanceof IdentityFlowError) {\n stderr(`${err.message}\\n`);\n exit(1);\n return;\n }\n throw err;\n }\n\n // ── POST /challenge ────────────────────────────────────────────────────────\n let challengeRes: Response;\n try {\n challengeRes = await fetchFn(`${baseUrl}/join/${token}/challenge`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ provider }),\n });\n } catch (err) {\n stderr(\n `cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`,\n );\n exit(2);\n return;\n }\n\n if (!challengeRes.ok) {\n const body = (await challengeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n challengeRes.status,\n body.error ?? '',\n parseRetryAfter(challengeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n let challengeBody: {\n challenge_id: string;\n provider: Provider;\n expires_in: number;\n client_hints: Record<string, unknown>;\n };\n try {\n challengeBody = (await challengeRes.json()) as typeof challengeBody;\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n\n const challengeId = challengeBody.challenge_id;\n const clientHints = challengeBody.client_hints;\n\n // ── Drive provider-specific completion + POST /complete ────────────────────\n let completeRawBody: unknown;\n\n if (provider === 'github') {\n // ── GitHub device-flow: poll GitHub client-side, then POST /complete ──────\n const deviceCode = clientHints['device_code'] as string;\n const userCode = clientHints['user_code'] as string;\n const verificationUri = clientHints['verification_uri'] as string;\n const expiresIn = typeof clientHints['expires_in'] === 'number' ? clientHints['expires_in'] as number : 900;\n const interval = typeof clientHints['interval'] === 'number' ? clientHints['interval'] as number : 5;\n\n stdout(`Open the following URL in your browser and enter the code:\\n`);\n stdout(` URL: ${verificationUri}\\n`);\n stdout(` Code: ${userCode}\\n`);\n stdout(` (Code expires in ${Math.floor(expiresIn / 60)} minutes)\\n`);\n stdout('Waiting for authorization...\\n');\n\n let accessToken: string;\n try {\n const result = await startDeviceFlow({\n deviceCode,\n interval,\n expiresIn,\n fetchPoll: async (dc) => {\n const res = await fetchFn(GITHUB_DEVICE_FLOW_URL, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: dc,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n });\n return {\n status: res.status,\n json: () => res.json() as Promise<unknown>,\n };\n },\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n });\n accessToken = result.accessToken;\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: access denied — you declined authorization in the browser.\\n');\n exit(5);\n return;\n case 'expired_token':\n stderr('cleargate: device code expired — please re-run `cleargate join <url>`.\\n');\n exit(5);\n return;\n case 'unreachable':\n stderr('cleargate: cannot reach GitHub. Check your connection and retry.\\n');\n exit(2);\n return;\n default:\n stderr(`cleargate: GitHub device flow error: ${err.code}\\n`);\n exit(6);\n return;\n }\n }\n stderr('cleargate: unexpected error during GitHub device flow\\n');\n exit(6);\n return;\n }\n\n // POST /complete with access_token proof (OD-2 fix: server member-mode accepts access_token)\n // FLASHCARD 2026-04-18 #cli #plaintext-redact: named field access only, never log accessToken\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { access_token: accessToken } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // ── Email magic-link: prompt for OTP with 3 retries (OD-3) ──────────────\n const sentTo = typeof clientHints['sent_to'] === 'string' ? clientHints['sent_to'] as string : '(unknown)';\n const maxRetries = 3;\n\n stdout(`We sent a 6-digit code to ${sentTo}.\\n`);\n\n // CI mode: use pre-seeded code directly\n if (opts.code !== undefined) {\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: opts.code } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // Interactive: prompt up to maxRetries times\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Queue-based line reading (pattern from identity-flow.ts)\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) return Promise.resolve(lineQueue.shift()!);\n if (rlClosed) return Promise.resolve('');\n return new Promise<string>((resolve) => { lineWaiters.push(resolve); });\n }\n\n let succeeded = false;\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n stdout('Enter code: ');\n const otpCode = (await readNextLine()).trim();\n\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: otpCode } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (completeRes.ok) {\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n succeeded = true;\n break;\n }\n\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const errorCode = body.error ?? '';\n\n // Terminal errors (don't retry)\n if (completeRes.status === 410 || errorCode === 'challenge_expired') {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n if (completeRes.status === 403 || (completeRes.status >= 400 && completeRes.status < 500 && errorCode !== 'provider_error')) {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n // Retryable (502 provider_error = wrong code)\n if (attempt < maxRetries) {\n stderr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n if (!succeeded) {\n stderr(`cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.\\n`);\n exit(12);\n return;\n }\n }\n }\n\n // ── Seat the refresh token ─────────────────────────────────────────────────\n const b = completeRawBody as {\n refresh_token?: unknown;\n project_name?: unknown;\n member_role?: unknown;\n };\n\n if (typeof b.refresh_token !== 'string' || typeof b.project_name !== 'string') {\n stderr('cleargate: server returned unexpected response shape.\\n');\n exit(7);\n return;\n }\n\n // Named field access — b.refresh_token is a bare string, never logged\n const refreshToken: string = b.refresh_token;\n const projectName: string = b.project_name;\n\n try {\n const store = await (opts.createStore ?? createTokenStore)();\n await store.save(opts.profile, refreshToken);\n\n // ── Success output ─────────────────────────────────────────────────────\n stdout(`joined project '${projectName}' as '${hostname()}'\\n`);\n stdout(`refresh token saved to ${store.backend}.\\n`);\n } catch (err) {\n stderr(\n `cleargate: internal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n exit(99);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const hdr = res.headers?.get?.('retry-after');\n if (!hdr) return undefined;\n const n = parseInt(hdr, 10);\n return isNaN(n) ? undefined : n;\n}\n","/**\n * Shared identity-flow helpers for `cleargate join` and `cleargate admin login`.\n *\n * Exposes:\n * - pickProvider — flag > required_provider pin > interactive picker > error\n * - promptPicker — numbered list via node:readline (no new dep)\n * - startDeviceFlow — GitHub device-flow poll loop (admin-login + member join)\n * - promptEmailOTP — 6-digit OTP prompt with up to 3 retries (OD-3)\n * - mapProviderError — HTTP-status + error-code → { message, exitCode, retryable }\n * - IdentityFlowError, DeviceFlowError typed error classes\n *\n * Test seams: functions that read from stdin or prompt accept\n * `{ stdin: NodeJS.ReadableStream, stdout: (s: string) => void }` as an\n * options object so tests can inject a pre-seeded Readable and capture output\n * without real TTY interaction.\n *\n * No new npm dependencies — uses only node:readline (built-in).\n *\n * CR-006 EPIC-019.\n */\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error classes\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class IdentityFlowError extends Error {\n constructor(\n public readonly code: string,\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'IdentityFlowError';\n }\n}\n\nexport class DeviceFlowError extends Error {\n constructor(\n public readonly code:\n | 'access_denied'\n | 'expired_token'\n | 'not_admin'\n | 'timeout'\n | 'unreachable'\n | 'server_error',\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'DeviceFlowError';\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type Provider = 'github' | 'email';\n\nexport interface PickProviderOptions {\n /** Explicit --auth flag value (wins over everything). */\n flag?: string;\n /** Is the process running in a real TTY? */\n isTTY?: boolean;\n /** Available providers (default: ['github', 'email']). */\n available?: Provider[];\n /** Stdin stream for interactive picker (test seam). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function for interactive picker (test seam). */\n stdout?: (s: string) => void;\n}\n\nexport interface StartDeviceFlowOptions {\n /** The device_code from the challenge client_hints. */\n deviceCode: string;\n /** Poll interval in seconds (from client_hints). */\n interval: number;\n /** How long until the device code expires in seconds. */\n expiresIn: number;\n /**\n * Function to POST to the poll endpoint.\n * Returns a Response-like object with status + json().\n */\n fetchPoll: (deviceCode: string) => Promise<{ status: number; json: () => Promise<unknown> }>;\n /**\n * Sleep implementation — injected in tests to avoid real waits.\n * Defaults to real setTimeout.\n */\n sleepFn?: (ms: number) => Promise<void>;\n /**\n * Override the base interval in milliseconds (used in tests — set to 0 to skip waits).\n * When provided, overrides `interval` from the server. Bump logic still applies if sleepFn is also provided.\n */\n intervalOverrideMs?: number;\n /**\n * Grace period beyond expiresIn before declaring timeout.\n * Default: 10_000ms (10 seconds for round-trip latency).\n */\n deadlineGraceMs?: number;\n}\n\nexport interface StartDeviceFlowResult {\n /** GitHub access_token returned by device-flow polling. */\n accessToken: string;\n}\n\nexport interface PromptEmailOTPOptions {\n /** Masked email shown to user, e.g. \"a***@example.com\". */\n sentTo: string;\n /** Pre-seeded code (CI / --code flag). If provided, skips prompt entirely. */\n code?: string;\n /** Number of in-process retries. Default: 3. */\n maxRetries?: number;\n /**\n * Function that performs one POST /complete attempt with the given OTP code.\n * Returns an object indicating success or a known error code.\n */\n attemptComplete: (code: string) => Promise<{ success: boolean; errorCode?: string }>;\n /** Stdin stream (test seam — default: process.stdin). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function (test seam). */\n stdout?: (s: string) => void;\n /** Stderr function (test seam). */\n stderr?: (s: string) => void;\n}\n\nexport interface MapProviderErrorResult {\n message: string;\n exitCode: number;\n retryable: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// pickProvider\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolves which provider to use for a join flow.\n *\n * Priority: flag > interactive picker > error (non-TTY)\n * Throws IdentityFlowError('provider_required') when non-interactive and no flag.\n * Throws IdentityFlowError('provider_unknown') when flag names an unrecognised provider.\n */\nexport async function pickProvider(opts: PickProviderOptions): Promise<Provider> {\n const available: Provider[] = opts.available ?? ['github', 'email'];\n\n if (opts.flag !== undefined) {\n const flagLower = opts.flag.toLowerCase() as Provider;\n if (!available.includes(flagLower)) {\n throw new IdentityFlowError(\n 'provider_unknown',\n `cleargate: unknown provider '${opts.flag}'. Available: ${available.join(', ')}`,\n );\n }\n return flagLower;\n }\n\n // No flag — need TTY for interactive picker\n if (!opts.isTTY) {\n throw new IdentityFlowError(\n 'provider_required',\n 'cleargate: --auth required in non-interactive mode',\n );\n }\n\n // Auto-select if only one provider\n if (available.length === 1) {\n return available[0]!;\n }\n\n return promptPicker(available, opts);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptPicker\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PROVIDER_LABELS: Record<Provider, string> = {\n github: 'GitHub OAuth',\n email: 'Email magic-link',\n};\n\n/**\n * Interactive numbered-list picker using node:readline.\n * Renders \"How would you like to verify your email?\" + numbered list.\n */\nexport async function promptPicker(\n options: Provider[],\n { stdin, stdout }: { stdin?: NodeJS.ReadableStream; stdout?: (s: string) => void } = {},\n): Promise<Provider> {\n const write = stdout ?? ((s: string) => process.stdout.write(s));\n\n write('How would you like to verify your email?\\n');\n options.forEach((p, i) => {\n write(` ${i + 1}. ${PROVIDER_LABELS[p]}\\n`);\n });\n write(`Choice [1-${options.length}]: `);\n\n const inputStream = (stdin as Readable | undefined) ?? process.stdin;\n\n return new Promise<Provider>((resolve, reject) => {\n let settled = false;\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n rl.once('line', (line) => {\n settled = true;\n rl.close();\n const idx = parseInt(line.trim(), 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= options.length) {\n reject(\n new IdentityFlowError(\n 'invalid_choice',\n `cleargate: invalid choice '${line.trim()}'. Enter a number between 1 and ${options.length}.`,\n ),\n );\n return;\n }\n resolve(options[idx]!);\n });\n\n rl.once('error', (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n rl.once('close', () => {\n // Only reject if we haven't already resolved/rejected from the line event\n if (!settled) {\n settled = true;\n reject(new IdentityFlowError('provider_required', 'cleargate: no provider selected'));\n }\n });\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// startDeviceFlow\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Polls fetchPoll until the device is authorized or an error terminal state is reached.\n *\n * Used for both admin-login (polls /admin-api/v1/auth/device/poll) and\n * member-join GitHub flow (polls https://github.com/login/oauth/access_token).\n *\n * Returns { accessToken } on success.\n * Throws DeviceFlowError on terminal failure.\n */\nexport async function startDeviceFlow(opts: StartDeviceFlowOptions): Promise<StartDeviceFlowResult> {\n const sleepFn = opts.sleepFn ?? defaultSleep;\n let currentIntervalMs =\n opts.intervalOverrideMs !== undefined\n ? opts.intervalOverrideMs\n : Math.max(opts.interval, 5) * 1000;\n\n const expiresAtMs = Date.now() + opts.expiresIn * 1000;\n const deadline = expiresAtMs + (opts.deadlineGraceMs ?? 10_000);\n\n while (Date.now() < deadline) {\n await sleepFn(currentIntervalMs);\n\n let pollRes: { status: number; json: () => Promise<unknown> };\n try {\n pollRes = await opts.fetchPoll(opts.deviceCode);\n } catch {\n throw new DeviceFlowError('unreachable');\n }\n\n if (pollRes.status === 403) {\n let body: Record<string, unknown> = {};\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n // ignore parse failure\n }\n if (body['error'] === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n // not_admin\n throw new DeviceFlowError('not_admin');\n }\n\n if (pollRes.status === 410) {\n throw new DeviceFlowError('expired_token');\n }\n\n if (!pollRes.status || pollRes.status < 200 || pollRes.status >= 300) {\n if (pollRes.status >= 500 || pollRes.status < 100) {\n throw new DeviceFlowError('server_error');\n }\n }\n\n let body: Record<string, unknown>;\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n throw new DeviceFlowError('server_error');\n }\n\n // Handle GitHub's device-flow pending responses\n const errorField = body['error'];\n if (typeof errorField === 'string') {\n if (errorField === 'authorization_pending') {\n // keep polling\n continue;\n }\n if (errorField === 'slow_down') {\n // bump interval if retry_after is larger\n const retryAfter = body['interval'];\n if (typeof retryAfter === 'number') {\n const bumped = retryAfter * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n } else {\n // default slow_down bump: +5s\n currentIntervalMs += 5_000;\n }\n continue;\n }\n if (errorField === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n if (errorField === 'expired_token') {\n throw new DeviceFlowError('expired_token');\n }\n // unknown error — treat as server error\n throw new DeviceFlowError('server_error');\n }\n\n // Handle MCP server's pending response shape { pending: true, retry_after? }\n if (body['pending'] === true) {\n const shouldApplyBump =\n opts.sleepFn !== undefined || opts.intervalOverrideMs === undefined;\n if (shouldApplyBump && typeof body['retry_after'] === 'number') {\n const bumped = (body['retry_after'] as number) * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n }\n continue;\n }\n\n // Success — extract access_token (GitHub device-flow: access_token field)\n if (typeof body['access_token'] === 'string') {\n return { accessToken: body['access_token'] as string };\n }\n\n // MCP server success shape (admin_token for admin-login)\n if (typeof body['admin_token'] === 'string') {\n return { accessToken: body['admin_token'] as string };\n }\n\n throw new DeviceFlowError('server_error');\n }\n\n throw new DeviceFlowError('timeout');\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptEmailOTP\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Prompts user for a 6-digit OTP code and attempts /complete up to maxRetries (default: 3) times.\n *\n * OD-3 resolution: 3 in-process retries for interactive UX; same challenge_id is reused each time.\n * Server MagicLinkProvider treats each /complete as independent — 502 on first attempt\n * does NOT burn the challenge (verified mcp/src/routes/join.ts:244-255).\n *\n * If `code` is provided (CI / --code flag), it is used directly without any prompt or retry.\n *\n * Returns the final code string on success.\n * Throws IdentityFlowError('otp_max_retries') after maxRetries failures.\n * Throws IdentityFlowError('otp_expired') on 410 challenge_expired.\n */\nexport async function promptEmailOTP(opts: PromptEmailOTPOptions): Promise<string> {\n const write = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const writeErr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const maxRetries = opts.maxRetries ?? 3;\n\n write(`We sent a 6-digit code to ${opts.sentTo}.\\n`);\n\n // CI mode: use provided code directly (no prompt, no retry)\n if (opts.code !== undefined) {\n const result = await opts.attemptComplete(opts.code);\n if (result.success) {\n return opts.code;\n }\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n throw new IdentityFlowError(\n 'otp_failed',\n `cleargate: code didn't match. Re-run \\`cleargate join <url>\\` to try again`,\n );\n }\n\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n // Create a single readline interface that persists across all retry attempts.\n // Each readLineFromRl() call reads exactly one line from the interface.\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Collect queued lines from the rl interface\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n // Drain any pending waiters with empty string (stream ended)\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) {\n return Promise.resolve(lineQueue.shift()!);\n }\n if (rlClosed) {\n return Promise.resolve('');\n }\n return new Promise<string>((resolve) => {\n lineWaiters.push(resolve);\n });\n }\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n write('Enter code: ');\n const code = (await readNextLine()).trim();\n\n const result = await opts.attemptComplete(code);\n\n if (result.success) {\n return code;\n }\n\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n\n if (attempt < maxRetries) {\n writeErr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n throw new IdentityFlowError(\n 'otp_max_retries',\n `cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.`,\n );\n}\n\n// readLine is no longer used (replaced by shared rl in promptEmailOTP)\n// Kept as a dead-code stub for any future one-shot callers.\n\n// ─────────────────────────────────────────────────────────────────────────────\n// mapProviderError\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Maps server HTTP status + error code pairs into user-readable messages and exit codes.\n *\n * Error table per M3 §2:\n * 400 invalid_request → exit 7\n * 400 provider_not_allowed → exit 9\n * 400 provider_unknown → exit 9\n * 400 identity_proof_required → exit 11 (stale CLI upgrade hint)\n * 403 email_mismatch → exit 10\n * 404 not_found → exit 4\n * 410 invite_expired → exit 3\n * 410 invite_already_consumed → exit 3\n * 410 challenge_expired → exit 3\n * 429 → exit 8\n * 502 provider_error → exit 12\n * >=500 (other) → exit 6\n */\nexport function mapProviderError(\n httpStatus: number,\n errorCode: string,\n retryAfterSeconds?: number,\n): MapProviderErrorResult {\n if (httpStatus === 400) {\n switch (errorCode) {\n case 'provider_not_allowed':\n return {\n message:\n 'cleargate: this invite requires a different provider — re-run with `--auth <pinned>`',\n exitCode: 9,\n retryable: true,\n };\n case 'provider_unknown':\n return {\n message:\n 'cleargate: server does not have that provider registered — contact the project admin',\n exitCode: 9,\n retryable: false,\n };\n case 'identity_proof_required':\n return {\n message: 'cleargate: this CLI is out of date — please upgrade and retry (`npm i -g cleargate@latest`)',\n exitCode: 11,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invalid request to server (please file a bug)',\n exitCode: 7,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 403 && errorCode === 'email_mismatch') {\n return {\n message:\n 'cleargate: verified email does not match the invitee — ask your admin to re-issue the invite',\n exitCode: 10,\n retryable: false,\n };\n }\n\n if (httpStatus === 404) {\n return {\n message: 'cleargate: invite not found',\n exitCode: 4,\n retryable: false,\n };\n }\n\n if (httpStatus === 410) {\n switch (errorCode) {\n case 'invite_expired':\n return {\n message: 'cleargate: invite expired. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'invite_already_consumed':\n return {\n message: 'cleargate: invite already consumed. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'challenge_expired':\n return {\n message: 'cleargate: code expired. Re-run `cleargate join <url>` to start over',\n exitCode: 3,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invite no longer valid. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 429) {\n const retryHint = retryAfterSeconds !== undefined ? `${retryAfterSeconds}` : '900';\n return {\n message: `cleargate: too many requests. Retry after ${retryHint}s`,\n exitCode: 8,\n retryable: true,\n };\n }\n\n if (httpStatus === 502 && errorCode === 'provider_error') {\n return {\n message: \"cleargate: code didn't match. Try again, or restart with `cleargate join <url>`\",\n exitCode: 12,\n retryable: true,\n };\n }\n\n if (httpStatus >= 500) {\n return {\n message: `cleargate: server error ${httpStatus}`,\n exitCode: 6,\n retryable: false,\n };\n }\n\n return {\n message: `cleargate: unexpected error ${httpStatus} ${errorCode}`,\n exitCode: 7,\n retryable: false,\n };\n}\n","/**\n * stamp.ts — `cleargate stamp <file>` command handler\n *\n * Wraps M1 helpers: getCodebaseVersion + stampFrontmatter.\n * Do NOT hand-roll YAML or shell git — use the M1 helpers.\n *\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #commander #optional-key: conditionally assign `now`/`version` opts.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { getCodebaseVersion, type CodebaseVersion } from '../lib/codebase-version.js';\nimport { stampFrontmatter, type StampOptions } from '../lib/stamp-frontmatter.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\n\nexport interface StampCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: inject a fixed CodebaseVersion instead of calling getCodebaseVersion. */\n getVersion?: () => CodebaseVersion;\n}\n\n/**\n * Build a unified-diff-style preview of the frontmatter changes.\n * Prints context lines (unchanged) with a leading space,\n * removed lines with `-`, added lines with `+`.\n */\nfunction buildDiffPreview(\n filePath: string,\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const lines: string[] = [`--- ${filePath}`, `+++ ${filePath} (after stamp)`];\n\n // Use insertion order of keys — include both before and after\n const seenKeys = new Set<string>();\n for (const key of [...Object.keys(before), ...Object.keys(after)]) {\n if (seenKeys.has(key)) continue;\n seenKeys.add(key);\n const bVal = before[key];\n const aVal = after[key];\n if (bVal === aVal) {\n lines.push(` ${key}: ${String(bVal)}`);\n } else {\n if (key in before) {\n lines.push(`-${key}: ${String(bVal)}`);\n }\n if (key in after) {\n lines.push(`+${key}: ${String(aVal)}`);\n }\n }\n }\n return lines.join('\\n');\n}\n\nexport async function stampHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Verify file exists before calling helpers\n if (!fs.existsSync(absPath)) {\n process.stderr.write(`[cleargate stamp] error: file not found: ${absPath}\\n`);\n return exitFn(1);\n }\n\n const version = cli?.getVersion ? cli.getVersion() : getCodebaseVersion({ cwd });\n\n if (opts.dryRun) {\n // For dry-run: operate on a temp file copy so the real file is never written.\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-stamp-dry-'));\n try {\n const tmpFile = path.join(tmpDir, path.basename(absPath));\n fs.copyFileSync(absPath, tmpFile);\n\n // Read current frontmatter for the diff's before-side\n let before: Record<string, unknown> = {};\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n if (raw.trimStart().startsWith('---')) {\n ({ fm: before } = parseFrontmatter(raw));\n }\n } catch {\n // before stays empty if parse fails\n }\n\n // Build stamp options — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(tmpFile, stampOpts);\n\n const diff = buildDiffPreview(file, before, result.frontmatterAfter);\n stdoutFn(diff);\n if (result.reason === 'noop-archive' || result.reason === 'noop-unchanged') {\n stdoutFn(`[dry-run] no changes (${result.reason})`);\n }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n return;\n }\n\n // Real stamp — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(absPath, stampOpts);\n stdoutFn(`[stamped] ${file} (${result.reason})`);\n}\n","import { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface CodebaseVersion {\n sha: string | null;\n dirty: boolean;\n tag: string | null;\n package_version: string | null;\n version_string: string; // e.g. \"a3f2e91\", \"a3f2e91-dirty\", \"1.4.2\", \"unknown\"\n}\n\nexport type ExecFn = (cmd: string, args: string[]) => { stdout: string; code: number };\n\nexport interface CodebaseVersionOpts {\n cwd?: string;\n exec?: ExecFn;\n}\n\nfunction makeDefaultExec(cwd: string): ExecFn {\n return (cmd: string, args: string[]): { stdout: string; code: number } => {\n try {\n const stdout = execSync([cmd, ...args].join(' '), {\n cwd,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return { stdout: stdout.trim(), code: 0 };\n } catch (err: unknown) {\n const e = err as { stdout?: string | Buffer; status?: number };\n return {\n stdout: typeof e.stdout === 'string' ? e.stdout.trim() : '',\n code: typeof e.status === 'number' ? e.status : 1,\n };\n }\n };\n}\n\nfunction findPackageJson(startDir: string): string | null {\n let current = startDir;\n while (true) {\n const candidate = path.join(current, 'package.json');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n return null;\n }\n current = parent;\n }\n}\n\nexport function getCodebaseVersion(opts?: CodebaseVersionOpts): CodebaseVersion {\n const cwd = opts?.cwd ?? process.cwd();\n const execFn = opts?.exec ?? makeDefaultExec(cwd);\n\n // Try git SHA\n const shaResult = execFn('git', ['rev-parse', '--short', 'HEAD']);\n if (shaResult.code === 0 && shaResult.stdout.length > 0) {\n const sha = shaResult.stdout.trim();\n\n // Check dirty\n const statusResult = execFn('git', ['status', '--porcelain']);\n const dirty = statusResult.code === 0 && statusResult.stdout.trim().length > 0;\n\n const version_string = dirty ? `${sha}-dirty` : sha;\n\n return {\n sha,\n dirty,\n tag: null,\n package_version: null,\n version_string,\n };\n }\n\n // Fallback: find nearest package.json\n const pkgPath = findPackageJson(cwd);\n if (pkgPath !== null) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: string };\n const package_version = typeof pkg.version === 'string' ? pkg.version : null;\n if (package_version !== null) {\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version,\n version_string: package_version,\n };\n }\n } catch {\n // fall through to unknown\n }\n }\n\n // Unknown\n console.warn('[cleargate] codebase-version: could not determine version (no git, no package.json)');\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version: null,\n version_string: 'unknown',\n };\n}\n","import * as fs from 'fs/promises';\nimport type { CodebaseVersion } from './codebase-version.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface StampOptions {\n now?: () => Date;\n version?: CodebaseVersion;\n /** Default: /\\/\\.cleargate\\/delivery\\/archive\\// */\n archivePathMatcher?: (absPath: string) => boolean;\n}\n\nexport interface StampResult {\n changed: boolean;\n frontmatterBefore: Record<string, unknown>;\n frontmatterAfter: Record<string, unknown>;\n reason: 'created' | 'updated' | 'noop-archive' | 'noop-unchanged';\n}\n\n/** Write-template marker keys (epic/story/bug/CR/proposal). */\nconst WRITE_TEMPLATE_KEYS = new Set([\n 'story_id',\n 'epic_id',\n 'proposal_id',\n 'cr_id',\n 'bug_id',\n]);\n\nconst DEFAULT_ARCHIVE_MATCHER = (absPath: string): boolean =>\n /\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath);\n\nexport async function stampFrontmatter(absPath: string, opts?: StampOptions): Promise<StampResult> {\n const isArchive = (opts?.archivePathMatcher ?? DEFAULT_ARCHIVE_MATCHER)(absPath);\n if (isArchive) {\n // Read to get frontmatter for result shape, but do not write\n const raw = await fs.readFile(absPath, 'utf8');\n let fm: Record<string, unknown> = {};\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n // If parse fails, return empty frontmatter snapshot\n }\n return {\n changed: false,\n frontmatterBefore: fm,\n frontmatterAfter: fm,\n reason: 'noop-archive',\n };\n }\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n // Determine if the file has frontmatter at all\n const hasFrontmatter = raw.trimStart().startsWith('---');\n\n let fm: Record<string, unknown> = {};\n let body = raw;\n\n if (hasFrontmatter) {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n }\n\n const frontmatterBefore = { ...fm };\n\n const nowFn = opts?.now ?? (() => new Date());\n const now = nowFn();\n const nowIso = toIsoSecond(now);\n\n const version = opts?.version ?? { sha: null, dirty: false, tag: null, package_version: null, version_string: 'unknown' };\n const versionString = version.version_string;\n\n // Determine if this is a first stamp or re-stamp\n const hasCreatedAt = 'created_at' in fm && fm['created_at'] !== undefined && fm['created_at'] !== '' && fm['created_at'] !== null;\n\n // Determine if it's a write-template (needs server_pushed_at_version)\n const isWriteTemplate = WRITE_TEMPLATE_KEYS.has(Object.keys(fm).find((k) => WRITE_TEMPLATE_KEYS.has(k)) ?? '');\n\n // Build the new frontmatter:\n // 1. Preserve existing key order\n // 2. Append new keys in canonical order: created_at, updated_at, created_at_version, updated_at_version, server_pushed_at_version\n const newFm: Record<string, unknown> = {};\n\n // Copy all existing keys first (preserves order)\n for (const [k, v] of Object.entries(fm)) {\n newFm[k] = v;\n }\n\n if (!hasCreatedAt) {\n // First stamp: set all 4 fields\n // If the keys exist already (placeholder values), update them in-place order\n // If not, append at the end in canonical order\n newFm['created_at'] = nowIso;\n newFm['updated_at'] = nowIso;\n newFm['created_at_version'] = versionString;\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n } else {\n // Re-stamp: preserve created_at + created_at_version, advance updated_at + updated_at_version\n // created_at stays\n newFm['updated_at'] = nowIso;\n // created_at_version stays\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n }\n\n // Check noop-unchanged: if nothing changed, return early\n const unchanged =\n newFm['updated_at'] === fm['updated_at'] &&\n newFm['updated_at_version'] === fm['updated_at_version'] &&\n newFm['created_at'] === fm['created_at'] &&\n newFm['created_at_version'] === fm['created_at_version'];\n\n if (unchanged && hasCreatedAt) {\n return {\n changed: false,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: 'noop-unchanged',\n };\n }\n\n // Serialize and write\n const fmBlock = serializeFrontmatter(newFm);\n // Reconstruct: frontmatter block + newline + body\n // body from parseFrontmatter does NOT have a leading blank line (it strips one)\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n\n return {\n changed: true,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: hasCreatedAt ? 'updated' : 'created',\n };\n}\n","/**\n * YAML frontmatter parser backed by js-yaml with CORE_SCHEMA (YAML 1.2 core).\n *\n * Parses `---\\n<yaml>\\n---\\n<body>` into a typed frontmatter map + body string.\n * Preserves native types (null, boolean, number, string), nested maps, and\n * arrays. Uses CORE_SCHEMA so ISO-8601 timestamp strings are NOT coerced to\n * Date objects (YAML 1.1's quirk).\n *\n * Historical note: an earlier hand-rolled parser flattened indented nested\n * maps into top-level keys and stringified null/boolean scalars. See\n * BUG-001 and FLASHCARD entry `#yaml #frontmatter`.\n */\n\nimport yaml from 'js-yaml';\n\nexport function parseFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') {\n throw new Error('parseFrontmatter: input does not start with ---');\n }\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) {\n throw new Error('parseFrontmatter: missing closing ---');\n }\n\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n // strip one leading blank line if present\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n\n if (yamlText.trim() === '') {\n return { fm: {}, body };\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`parseFrontmatter: invalid YAML: ${(err as Error).message}`);\n }\n\n if (parsed === null || parsed === undefined) {\n return { fm: {}, body };\n }\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('parseFrontmatter: frontmatter is not a YAML mapping');\n }\n\n return { fm: parsed as Record<string, unknown>, body };\n}\n","/**\n * Frontmatter YAML serializer backed by js-yaml with CORE_SCHEMA.\n *\n * Emits a `---\\n<yaml>\\n---` block. Preserves key insertion order (JS\n * objects iterate non-numeric keys in insertion order by spec, and js-yaml\n * follows Object.keys), nested maps, arrays, and native scalar types.\n *\n * Partner of parse-frontmatter.ts — the two round-trip losslessly.\n */\n\nimport yaml from 'js-yaml';\n\n/**\n * Serialize a frontmatter record to a YAML block (including the --- delimiters).\n * Key order is preserved as supplied.\n */\nexport function serializeFrontmatter(fm: Record<string, unknown>): string {\n // Empty object → emit a two-delimiter block with no body\n if (Object.keys(fm).length === 0) {\n return '---\\n---';\n }\n\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n\n // js-yaml always ends with \\n; trim so we control the framing\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---`;\n}\n\n/**\n * Format a Date as ISO 8601 UTC with second precision: \"YYYY-MM-DDTHH:MM:SSZ\"\n */\nexport function toIsoSecond(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n","/**\n * init.ts — `cleargate init` command handler\n *\n * Steps:\n * 1. Validate cwd exists and is writable\n * 2. Resolve payloadDir (bundled cleargate-planning/ templates)\n * 3. copyPayload: copy scaffold files to target cwd\n * 4. mergeSettings: merge PostToolUse hook into .claude/settings.json\n * 5. injectClaudeMd: bounded-block inject into CLAUDE.md\n * 6. Bootstrap pass: if delivery/ has items, run wiki build\n * 7. Print Done\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawnSync } from 'node:child_process';\nimport { copyPayload } from '../init/copy-payload.js';\nimport { mergeSettings, type SettingsJson } from '../init/merge-settings.js';\nimport { injectClaudeMd, extractBlock } from '../init/inject-claude-md.js';\nimport { injectMcpJson } from '../init/inject-mcp-json.js';\nimport { wikiBuildHandler, type WikiBuildOptions } from './wiki-build.js';\nimport { loadPackageManifest, type ManifestFile } from '../lib/manifest.js';\nimport { promptYesNo as defaultPromptYesNo, promptEmail as defaultPromptEmail } from '../lib/prompts.js';\nimport { resolveIdentity, readParticipant, writeParticipant, type ResolveIdentityOpts } from '../lib/identity.js';\n\n/**\n * The PostToolUse hook config to merge — updated in STORY-008-06 to use\n * stamp-and-gate.sh (replaces legacy SPRINT-04 inline wiki ingest command).\n * Uses ${CLAUDE_PROJECT_DIR} so the path is project-relative at runtime.\n * mergeSettings deduplicates by exact command string — safe to re-run.\n */\nconst HOOK_ADDITION: SettingsJson = {\n hooks: {\n PreToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-edit-gate.sh',\n },\n ],\n },\n ],\n PostToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/stamp-and-gate.sh',\n },\n ],\n },\n ],\n },\n};\n\nexport interface InitOptions {\n /** Target working directory (the repo being initialised). Default: process.cwd() */\n cwd?: string;\n /** Overwrite files that differ from payload. Default: false */\n force?: boolean;\n /** Accept all defaults non-interactively (same as stdin not a TTY). */\n yes?: boolean;\n /** Test seam: path to bundled cleargate-planning/ payload directory */\n payloadDir?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: runs wiki build (default: wikiBuildHandler) */\n runWikiBuild?: (opts: WikiBuildOptions) => Promise<void>;\n /**\n * Test seam: replaces the real Y/n prompt for restore flow.\n * STORY-009-03: injectable so integration tests don't block on stdin.\n */\n promptYesNo?: (question: string, defaultYes: boolean) => Promise<boolean>;\n /**\n * Test seam: replaces loadPackageManifest() call for snapshot step.\n * STORY-009-03: allows tests to supply a known ManifestFile without a real MANIFEST.json.\n */\n readInstallManifest?: () => ManifestFile;\n /**\n * Test seam: replaces the email prompt for participant identity flow.\n * STORY-010-01: injectable so tests don't block on stdin.\n */\n promptEmail?: (question: string, defaultValue: string) => Promise<string>;\n /**\n * Test seam: identity resolver options (gitEmail, hostname, username, env overrides).\n * STORY-010-01: used to inject a deterministic git email in tests.\n */\n identityOpts?: ResolveIdentityOpts;\n /**\n * Test seam: override process.stdin.isTTY for participant prompt decision.\n * STORY-010-01: in test environment stdin is not a TTY; inject true to force interactive path.\n */\n stdinIsTTY?: boolean;\n /**\n * CR-009: pin version to stamp into hook scripts. Overrides the default of\n * reading the version from `cleargate-cli/package.json`. Supports\n * `cleargate init --pin 0.6.0-beta` and test seam injection.\n */\n pin?: string;\n /**\n * Test seam: replaces child_process.spawnSync for the resolver probe (Step 7.6).\n * Injected in tests to avoid real npx invocations.\n */\n spawnSyncFn?: typeof spawnSync;\n}\n\n/** Shape of the .cleargate/.uninstalled marker written by STORY-009-07 `uninstall`. */\ninterface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n}\n\n/** Resolve default payloadDir from the installed package structure.\n *\n * tsup bundles all modules into dist/cli.js (single entry point).\n * As a result, import.meta.url inside ANY module resolves to dist/cli.js.\n * So dirname(import.meta.url) = dist/. One level up = package root.\n * See flashcard: #tsup #bundle #import-meta.\n */\nexport function resolveDefaultPayloadDir(): string {\n const thisFile = fileURLToPath(import.meta.url);\n // dist/cli.js → dirname = dist/ → one level up = package root\n const pkgRoot = path.resolve(path.dirname(thisFile), '..');\n return path.join(pkgRoot, 'templates', 'cleargate-planning');\n}\n\n/** Glob delivery dir for .md files, excluding .gitkeep */\nfunction countDeliveryItems(cwd: string): number {\n const pendingSync = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(cwd, '.cleargate', 'delivery', 'archive');\n let count = 0;\n for (const dir of [pendingSync, archive]) {\n if (!fs.existsSync(dir)) continue;\n const entries = fs.readdirSync(dir);\n for (const f of entries) {\n if (f.endsWith('.md') && f !== '.gitkeep') count++;\n }\n }\n return count;\n}\n\n/** Write file atomically: write to tmp, then rename. */\nfunction writeAtomic(filePath: string, content: string): void {\n const tmpPath = filePath + '.tmp.' + Date.now();\n fs.writeFileSync(tmpPath, content, 'utf8');\n fs.renameSync(tmpPath, filePath);\n}\n\n/**\n * CR-009: Read the version from a package.json file at `packageJsonPath`.\n * Returns null when the file is absent or malformed.\n */\nfunction readPackageVersion(packageJsonPath: string): string | null {\n try {\n const raw = fs.readFileSync(packageJsonPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: unknown };\n if (typeof pkg.version === 'string' && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nexport async function initHandler(opts: InitOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const force = opts.force ?? false;\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const runWikiBuild = opts.runWikiBuild ?? wikiBuildHandler;\n const promptYesNoFn = opts.promptYesNo ?? defaultPromptYesNo;\n const promptEmailFn = opts.promptEmail ?? defaultPromptEmail;\n const spawnSyncFn = opts.spawnSyncFn ?? spawnSync;\n\n // Step 1: Validate cwd\n if (!fs.existsSync(cwd)) {\n stderr(`[cleargate init] ERROR: target directory does not exist: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n // Check writable by attempting to create a tmp file\n const testWritePath = path.join(cwd, `.cleargate-init-write-test-${Date.now()}`);\n try {\n fs.writeFileSync(testWritePath, '');\n fs.unlinkSync(testWritePath);\n } catch {\n stderr(`[cleargate init] ERROR: target directory is not writable: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n stdout(`[cleargate init] Target: ${cwd}\\n`);\n\n // Step 2: Resolve payloadDir\n const payloadDir = opts.payloadDir ?? resolveDefaultPayloadDir();\n\n if (!fs.existsSync(payloadDir)) {\n stderr(`[cleargate init] ERROR: payload directory not found: ${payloadDir}\\n`);\n stderr(`[cleargate init] Run \\`npm run prebuild\\` to copy the payload first.\\n`);\n exit(1);\n return;\n }\n\n // Step 3: Copy scaffold payload\n // Step 3.5 (pre-copy): Detect .uninstalled marker and prompt restore.\n // Must run before any writes so the user sees the restore prompt first.\n const uninstalledMarkerPath = path.join(cwd, '.cleargate', '.uninstalled');\n let uninstalledMarker: UninstalledMarker | null = null;\n let userChoseRestore = false;\n\n if (fs.existsSync(uninstalledMarkerPath)) {\n try {\n const raw = fs.readFileSync(uninstalledMarkerPath, 'utf8');\n uninstalledMarker = JSON.parse(raw) as UninstalledMarker;\n } catch {\n stderr(`[cleargate init] WARNING: .uninstalled marker is malformed; ignoring it.\\n`);\n }\n\n if (uninstalledMarker !== null) {\n const { uninstalled_at, prior_version, preserved } = uninstalledMarker;\n const question =\n `[cleargate init] Detected previous ClearGate install` +\n ` (uninstalled ${uninstalled_at}, prior version ${prior_version}).` +\n ` Restore preserved items? [Y/n]`;\n userChoseRestore = await promptYesNoFn(question, true);\n\n if (userChoseRestore) {\n // Blind copy: just verify each preserved file still exists on disk\n // (it was preserved as intended). Log status; never touch content.\n for (const preservedPath of preserved) {\n const absPreserved = path.isAbsolute(preservedPath)\n ? preservedPath\n : path.join(cwd, preservedPath);\n if (fs.existsSync(absPreserved)) {\n stdout(`[cleargate init] [preserved] ${preservedPath}\\n`);\n } else {\n stdout(`[cleargate init] [warn] preserved path missing on disk: ${preservedPath}\\n`);\n }\n }\n } else {\n stdout(\n `[cleargate init] discarding preservation; preserved files untouched on disk\\n`,\n );\n }\n // Marker removal happens AFTER bootstrap (Step 6) completes — tracked below.\n }\n }\n\n // CR-009: Resolve pin version for hook script substitution.\n // Priority: explicit --pin flag → package.json next to payloadDir → package.json next to dist → fallback 'latest'\n let pinVersion: string | undefined;\n if (opts.pin) {\n pinVersion = opts.pin;\n } else {\n // payloadDir is `.../templates/cleargate-planning`; package.json is at `.../package.json`\n const payloadParent = path.resolve(payloadDir, '..', '..');\n pinVersion =\n readPackageVersion(path.join(payloadParent, 'package.json')) ??\n readPackageVersion(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json')) ??\n 'latest';\n }\n\n const copyReport = copyPayload(payloadDir, cwd, { force, pinVersion });\n for (const action of copyReport.actions) {\n const verb =\n action.action === 'created'\n ? 'Created'\n : action.action === 'overwritten'\n ? 'Overwritten'\n : 'Skipped (exists)';\n stdout(`[cleargate init] ${verb} ${action.relPath}\\n`);\n }\n\n // Step 4: Merge PostToolUse hook into .claude/settings.json\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n let existingSettings: SettingsJson | null = null;\n if (fs.existsSync(settingsPath)) {\n try {\n existingSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as SettingsJson;\n } catch {\n stderr(`[cleargate init] WARNING: could not parse ${settingsPath}; treating as empty.\\n`);\n }\n }\n\n const mergedSettings = mergeSettings(existingSettings, HOOK_ADDITION);\n fs.mkdirSync(path.dirname(settingsPath), { recursive: true });\n writeAtomic(settingsPath, JSON.stringify(mergedSettings, null, 2) + '\\n');\n stdout(`[cleargate init] Updated .claude/settings.json: merged PostToolUse hook\\n`);\n\n // Step 5: Inject bounded block into CLAUDE.md\n const claudeMdPath = path.join(cwd, 'CLAUDE.md');\n const claudeMdSrcPath = path.join(payloadDir, 'CLAUDE.md');\n\n let claudeMdBlock: string;\n try {\n const claudeMdSrc = fs.readFileSync(claudeMdSrcPath, 'utf8');\n claudeMdBlock = extractBlock(claudeMdSrc);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not read CLAUDE.md block from payload: ${String(e)}\\n`);\n claudeMdBlock = '<!-- CLEARGATE:START -->\\n<!-- CLEARGATE:END -->';\n }\n\n const existingClaudeMd = fs.existsSync(claudeMdPath)\n ? fs.readFileSync(claudeMdPath, 'utf8')\n : null;\n\n const newClaudeMd = injectClaudeMd(existingClaudeMd, claudeMdBlock);\n writeAtomic(claudeMdPath, newClaudeMd);\n\n if (existingClaudeMd === null) {\n stdout(`[cleargate init] Created CLAUDE.md (with bounded block)\\n`);\n } else if (existingClaudeMd !== newClaudeMd) {\n stdout(`[cleargate init] Updated CLAUDE.md (bounded block injected/replaced)\\n`);\n } else {\n stdout(`[cleargate init] CLAUDE.md unchanged (block already up to date)\\n`);\n }\n\n // Step 5b (BUG-017 + BUG-019 + post-0.8.0): register cleargate in `.mcp.json`\n // as a stdio MCP server. Use `npx -y cleargate@<pin> mcp serve` so users\n // without a global install (i.e. anyone who ran `npx cleargate init`) still\n // get a working spawn. Pin to the cleargate version that wrote the entry,\n // matching the CR-009 hook resolver pattern.\n try {\n const action = injectMcpJson(cwd, pinVersion ?? 'latest');\n if (action === 'created') {\n stdout(\n `[cleargate init] Created .mcp.json (cleargate MCP server registered) — restart Claude Code to load it.\\n`,\n );\n } else if (action === 'updated') {\n stdout(\n `[cleargate init] Updated .mcp.json (cleargate MCP server entry merged) — restart Claude Code to pick up changes.\\n`,\n );\n } else {\n stdout(`[cleargate init] .mcp.json unchanged (cleargate entry already present)\\n`);\n }\n } catch (e) {\n stderr(`[cleargate init] WARNING: ${String(e instanceof Error ? e.message : e)}\\n`);\n }\n\n // Step 6: Bootstrap pass\n const itemCount = countDeliveryItems(cwd);\n if (itemCount > 0) {\n stdout(`[cleargate init] Bootstrap: running wiki build (${itemCount} items found)...\\n`);\n await runWikiBuild({ cwd, now });\n stdout(`[cleargate init] Bootstrap: ran wiki build (${itemCount} items ingested)\\n`);\n } else {\n stdout(`[cleargate init] Bootstrap: no items to ingest, skipping build\\n`);\n }\n\n // Step 7: Write install snapshot atomically to .cleargate/.install-manifest.json.\n // Must be the FINAL step after all scaffold files are written (blueprint §1.2).\n const cleargateDir = path.join(cwd, '.cleargate');\n fs.mkdirSync(cleargateDir, { recursive: true });\n\n const snapshotPath = path.join(cleargateDir, '.install-manifest.json');\n try {\n const readManifest = opts.readInstallManifest ?? (() => loadPackageManifest({ packageRoot: payloadDir }));\n const pkgManifest = readManifest();\n const snapshot: ManifestFile = {\n ...pkgManifest,\n installed_at: now(),\n };\n writeAtomic(snapshotPath, JSON.stringify(snapshot, null, 2) + '\\n');\n stdout(`[cleargate init] Wrote install snapshot: .cleargate/.install-manifest.json\\n`);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not write install snapshot: ${String(e)}\\n`);\n }\n\n // Remove .uninstalled marker AFTER bootstrap + snapshot complete (whether user chose Y or N).\n // This prevents repeated prompting on subsequent init runs.\n if (uninstalledMarker !== null && fs.existsSync(uninstalledMarkerPath)) {\n try {\n fs.unlinkSync(uninstalledMarkerPath);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not remove .uninstalled marker: ${String(e)}\\n`);\n }\n }\n\n // Step 7.6 (CR-009): Resolver probe — run the three-branch resolver and print\n // a visible green/red status line. Converts \"invisible silent no-op at first\n // hook fire\" into \"loud failure at install time, when the user is watching\".\n {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n type ResolverBranch = { cmd: string; args: string[] } | null;\n\n // Mirror the bash resolver order: dist first (dogfood), then PATH, then npx.\n let branch: ResolverBranch = null;\n let branchLabel = '';\n\n if (fs.existsSync(distCliPath)) {\n branch = { cmd: 'node', args: [distCliPath, '--version'] };\n branchLabel = `local dist (${distCliPath})`;\n } else {\n // Try `cleargate --version` via PATH\n const whichResult = spawnSyncFn('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n branch = { cmd: 'cleargate', args: ['--version'] };\n branchLabel = 'PATH (global install)';\n } else {\n // Fall back to npx invocation\n branch = { cmd: 'npx', args: ['-y', `cleargate@${pinVersion}`, '--version'] };\n branchLabel = `npx cleargate@${pinVersion} (cold-start ~600ms first call)`;\n }\n }\n\n if (branch !== null) {\n const probeResult = spawnSyncFn(branch.cmd, branch.args, {\n encoding: 'utf8',\n timeout: 15000,\n });\n\n if (probeResult.status === 0) {\n stdout(`[cleargate init] \\u{1F7E2} cleargate CLI resolved via ${branchLabel}\\n`);\n } else {\n // Resolver chain exhausted — the hooks will no-op until cleargate is reachable.\n // Per BUG-015 (2026-04-27): convert from exit(1) to warn-not-block. The probe is a\n // best-effort signal; transient registry issues (CI race conditions, network blips)\n // shouldn't hard-fail init. Hooks will surface their own resolver-failure banners\n // at runtime if the issue persists. User can run `cleargate doctor` to investigate.\n stdout(\n `[cleargate init] \\u{1F7E1} cleargate CLI: not resolvable in this environment.\\n` +\n `[cleargate init] Attempted: ${branchLabel}\\n` +\n `[cleargate init] This is a warning, not a fatal error. Hooks will no-op until resolved.\\n` +\n `[cleargate init] Fix: npm i -g cleargate@${pinVersion} or npx cleargate@${pinVersion} doctor\\n`,\n );\n // Continue init. The resolver-status was emitted; hooks will surface their own\n // failure banners at runtime if needed.\n }\n }\n }\n\n // Step 7.5: Participant identity\n // Skip if .cleargate/.participant.json already exists (idempotent re-init).\n const existingParticipant = readParticipant(cwd);\n if (existingParticipant === null) {\n // Resolve git email as default (no env / host fallback during init — init needs a concrete prompt).\n const identityOpts = opts.identityOpts ?? {};\n\n // Resolve just the git rung: call resolveIdentity with env={} to skip env rung,\n // then check the source.\n const gitOnlyIdentity = resolveIdentity(cwd, {\n ...identityOpts,\n env: {}, // force skip env rung\n });\n const gitEmail =\n gitOnlyIdentity.source === 'git' ? gitOnlyIdentity.email : null;\n\n const stdinIsTTY = opts.stdinIsTTY ?? process.stdin.isTTY ?? false;\n const isNonInteractive = opts.yes === true || !stdinIsTTY;\n\n if (isNonInteractive) {\n // Non-interactive: use git email; if unavailable use host fallback via resolveIdentity\n const finalEmail =\n gitEmail ??\n resolveIdentity(cwd, identityOpts).email;\n\n await writeParticipant(cwd, finalEmail, 'inferred', now);\n stdout(`[cleargate init] Participant identity: ${finalEmail} (inferred)\\n`);\n } else {\n // Interactive: prompt for email.\n // BUG-007: the prior prompt reused the `[cleargate init]` info-log prefix\n // and was followed by a newline, so it visually merged with preceding log\n // lines and the cursor sat on a blank line below — users walked away\n // believing install had finished. Fix:\n // 1. Drop the `[cleargate init]` prefix on the prompt itself so it\n // reads as an interactive question, not a status message.\n // 2. Print a blank separator line above so the eye catches the change.\n // 3. Reject GitHub `users.noreply.github.com` git emails as a default —\n // they're unrouteable identities; users who blindly press Enter end\n // up with a participant identity that can't receive invites.\n const isNoreply = gitEmail !== null && /@users\\.noreply\\.github\\.com$/i.test(gitEmail);\n const defaultEmail = (gitEmail !== null && !isNoreply) ? gitEmail : 'user@localhost';\n stdout('\\n');\n const question = `Participant email (press Enter for default) [${defaultEmail}]:`;\n const answer = await promptEmailFn(question, defaultEmail);\n await writeParticipant(cwd, answer, 'prompted', now);\n stdout(`[cleargate init] Participant identity: ${answer} (prompted)\\n`);\n }\n }\n\n // Step 8: Done\n stdout(\n `[cleargate init] Done. Read CLAUDE.md and .cleargate/knowledge/cleargate-protocol.md to learn the protocol.\\n`,\n );\n\n void now; // suppress unused warning if not used after this\n}\n","/**\n * copy-payload.ts — recursively copy cleargate-planning/ payload to target cwd.\n *\n * Handles overwrite policy:\n * - by default: skip files that already exist with identical content\n * - force=true: overwrite all\n *\n * Does NOT prompt interactively; skip-or-overwrite is determined by `force`.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface CopyReport {\n created: number;\n skipped: number;\n overwritten: number;\n /** Per-file action lines for verbose printing */\n actions: Array<{ action: 'created' | 'skipped' | 'overwritten'; relPath: string }>;\n}\n\nexport interface CopyPayloadOptions {\n force: boolean;\n /**\n * CR-009: When set, substitute `__CLEARGATE_VERSION__` placeholder in hook scripts\n * with this version string. Applies to `.claude/hooks/stamp-and-gate.sh` and\n * `.claude/hooks/session-start.sh`. Use a sed-friendly format: `0.5.0`.\n */\n pinVersion?: string;\n}\n\n/** Hook files that carry the `__CLEARGATE_VERSION__` placeholder (CR-009). */\nconst PIN_PLACEHOLDER = '__CLEARGATE_VERSION__';\nconst HOOK_FILES_WITH_PIN = new Set([\n '.claude/hooks/stamp-and-gate.sh',\n '.claude/hooks/session-start.sh',\n]);\n\n/**\n * Files that copyPayload must NOT copy verbatim. The init pipeline owns these\n * via a downstream step that strips canonical-source preamble or otherwise\n * transforms content. Listing here ensures `listFilesRecursive` skips them.\n *\n * - CLAUDE.md (BUG-016): top-level CLAUDE.md in cleargate-planning/ carries a\n * meta-doc preamble describing the bounded-block contract. Step 5 of init\n * (`injectClaudeMd`) writes only `extractBlock(src)`. Copying the raw source\n * here would leave the preamble in the user repo because step 5 only\n * replaces the inner block region.\n */\nconst SKIP_FILES = new Set<string>(['CLAUDE.md']);\n\n/**\n * Recursively enumerate files under `dir`.\n * Returns paths relative to `dir`.\n */\nfunction listFilesRecursive(dir: string): string[] {\n const results: string[] = [];\n function walk(current: string, rel: string): void {\n const entries = fs.readdirSync(current, { withFileTypes: true });\n for (const entry of entries) {\n const entryRel = rel ? `${rel}/${entry.name}` : entry.name;\n const entryAbs = path.join(current, entry.name);\n if (entry.isDirectory()) {\n walk(entryAbs, entryRel);\n } else {\n results.push(entryRel);\n }\n }\n }\n walk(dir, '');\n return results;\n}\n\n/**\n * Copy all files from `payloadDir` to `targetCwd`, preserving directory structure.\n * Dotfiles and dot-directories (e.g. `.claude/`, `.cleargate/`) are preserved.\n */\nexport function copyPayload(\n payloadDir: string,\n targetCwd: string,\n opts: CopyPayloadOptions,\n): CopyReport {\n const report: CopyReport = { created: 0, skipped: 0, overwritten: 0, actions: [] };\n\n if (!fs.existsSync(payloadDir)) {\n throw new Error(`copyPayload: payloadDir does not exist: ${payloadDir}`);\n }\n\n const files = listFilesRecursive(payloadDir).filter((r) => !SKIP_FILES.has(r));\n\n for (const relPath of files) {\n const srcPath = path.join(payloadDir, relPath);\n const dstPath = path.join(targetCwd, relPath);\n\n // Ensure target directory exists\n fs.mkdirSync(path.dirname(dstPath), { recursive: true });\n\n let srcContent: Buffer | string = fs.readFileSync(srcPath);\n\n // CR-009: substitute __CLEARGATE_VERSION__ placeholder in hook scripts\n if (opts.pinVersion && HOOK_FILES_WITH_PIN.has(relPath)) {\n const text = srcContent.toString('utf8').replaceAll(PIN_PLACEHOLDER, opts.pinVersion);\n srcContent = text;\n }\n\n // Compare: convert srcContent to Buffer for comparison when it's a string\n const srcBuffer = typeof srcContent === 'string' ? Buffer.from(srcContent, 'utf8') : srcContent;\n\n // BUG-018: preserve executable bit. Source `.sh` files are 0o755 in the bundled\n // payload but `fs.writeFileSync` defaults to 0o644 — SessionStart fired\n // \"Permission denied\". Read source mode and propagate any +x bits.\n const srcMode = fs.statSync(srcPath).mode;\n const needsExec = (srcMode & 0o111) !== 0 || relPath.endsWith('.sh');\n\n if (fs.existsSync(dstPath)) {\n const dstContent = fs.readFileSync(dstPath);\n if (srcBuffer.equals(dstContent)) {\n // Identical — skip the write but still re-assert exec bit; the user may\n // have lost it via copy/chmod outside our control.\n if (needsExec && process.platform !== 'win32') {\n fs.chmodSync(dstPath, 0o755);\n }\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n if (!opts.force) {\n // Different content, no force — skip\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n // Different + force — overwrite\n fs.writeFileSync(dstPath, srcBuffer);\n if (needsExec && process.platform !== 'win32') {\n fs.chmodSync(dstPath, 0o755);\n }\n report.overwritten++;\n report.actions.push({ action: 'overwritten', relPath });\n } else {\n // New file\n fs.writeFileSync(dstPath, srcBuffer);\n if (needsExec && process.platform !== 'win32') {\n fs.chmodSync(dstPath, 0o755);\n }\n report.created++;\n report.actions.push({ action: 'created', relPath });\n }\n }\n\n return report;\n}\n","/**\n * merge-settings.ts — JSON merge for .claude/settings.json\n *\n * Algorithm (from M4 blueprint):\n * - if existing is null: return addition\n * - otherwise deep-clone existing and merge addition.hooks into result.hooks\n * - For each event (e.g. PostToolUse):\n * - find matching entry by `matcher` field\n * - if absent: push entire new entry\n * - if present: de-dup merge inner hooks[] by exact `command` string match\n * - Preserves all other top-level keys (SubagentStop, permissions, etc.)\n */\n\nexport interface HookCommand {\n type: string;\n command: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n [key: string]: unknown;\n}\n\nexport interface HooksConfig {\n [event: string]: HookEntry[];\n}\n\nexport interface SettingsJson {\n hooks?: HooksConfig;\n [key: string]: unknown;\n}\n\n/**\n * Deep-clone a plain JSON-serializable object.\n */\nfunction deepClone<T>(obj: T): T {\n return JSON.parse(JSON.stringify(obj)) as T;\n}\n\n/**\n * Merge `addition` hook config into `existing` settings.\n * Returns a new merged object; does not mutate either argument.\n *\n * @param existing - parsed .claude/settings.json content, or null if file absent\n * @param addition - the hook config to merge in (must have `hooks` key)\n */\nexport function mergeSettings(\n existing: SettingsJson | null,\n addition: SettingsJson,\n): SettingsJson {\n if (existing === null) {\n return deepClone(addition);\n }\n\n const result: SettingsJson = deepClone(existing);\n\n // Ensure result.hooks exists\n if (!result.hooks) {\n result.hooks = {};\n }\n\n // Merge each event from addition\n for (const [eventName, eventArray] of Object.entries(addition.hooks ?? {})) {\n if (!result.hooks[eventName]) {\n result.hooks[eventName] = [];\n }\n\n for (const newEntry of eventArray) {\n const matchingIdx = result.hooks[eventName].findIndex(\n (e) => e.matcher === newEntry.matcher,\n );\n\n if (matchingIdx === -1) {\n // No matching entry — push entire new entry\n result.hooks[eventName].push(deepClone(newEntry));\n } else {\n // Matcher exists — merge inner hooks[] by de-dup on `command`\n const existingEntry = result.hooks[eventName][matchingIdx];\n const existingInner: HookCommand[] = Array.isArray(existingEntry.hooks)\n ? (existingEntry.hooks as HookCommand[])\n : [];\n\n for (const newInner of newEntry.hooks ?? []) {\n if (!existingInner.some((h) => h.command === newInner.command)) {\n existingInner.push(deepClone(newInner) as HookCommand);\n }\n }\n\n result.hooks[eventName][matchingIdx] = {\n ...existingEntry,\n hooks: existingInner,\n };\n }\n }\n }\n\n return result;\n}\n","/**\n * inject-claude-md.ts — bounded-block injection for CLAUDE.md\n *\n * Block format: <!-- CLEARGATE:START -->\\n<content>\\n<!-- CLEARGATE:END -->\n * Detection regex: /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/ (greedy, see below)\n *\n * Rules:\n * - If existing === null: create file with block as full content (+ trailing newline)\n * - If existing matches: replace the bounded block in place (idempotent)\n * - If existing no match: append block with 2 leading newlines (preserve user content above)\n */\n\n// Greedy match: from first <!-- CLEARGATE:START --> to LAST <!-- CLEARGATE:END -->.\n// Greedy is correct here because:\n// (a) cleargate-planning/CLAUDE.md body text mentions both markers inline as code references,\n// and non-greedy would stop at the inline END before the real one.\n// (b) We assume at most one cleargate block per file (idempotency requires it).\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/;\n\n/**\n * Extract the bounded block from a source file (e.g. cleargate-planning/CLAUDE.md).\n * Returns the text from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive.\n * Throws if the markers are not found.\n */\nexport function extractBlock(sourceContent: string): string {\n const match = BLOCK_REGEX.exec(sourceContent);\n if (!match) {\n throw new Error('inject-claude-md: CLEARGATE:START/END markers not found in source content');\n }\n return match[0];\n}\n\n/**\n * Inject or update the bounded block in an existing CLAUDE.md.\n *\n * @param existing - current content of CLAUDE.md, or null if file doesn't exist\n * @param block - the full block to inject, from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive\n * @returns - new file content (ready to write)\n */\nexport function injectClaudeMd(existing: string | null, block: string): string {\n if (existing === null) {\n // Create new file with block as full content\n return block + '\\n';\n }\n\n if (BLOCK_REGEX.test(existing)) {\n // Replace existing block in place\n return existing.replace(BLOCK_REGEX, block);\n }\n\n // Append block with 2 leading newlines\n return existing.trimEnd() + '\\n\\n' + block + '\\n';\n}\n","/**\n * inject-mcp-json.ts — write or merge `.mcp.json` so Claude Code registers the\n * cleargate MCP server. Called from `init` Step 7. (BUG-017)\n *\n * Behavior:\n * - Greenfield: writes `{ mcpServers: { cleargate: { type, url } } }`.\n * - Existing without `mcpServers`: adds `mcpServers.cleargate`.\n * - Existing with other servers: leaves them alone, sets/replaces `mcpServers.cleargate`.\n * - Idempotent: re-running on identical state produces byte-identical output.\n *\n * NOTE: this writes the HTTP transport entry. Claude Code's HTTP MCP transport\n * cannot drive auth against the cleargate server today (BUG-019). The 0.7.0\n * shipment is knowingly transitional — the long-term fix is a stdio shim\n * (`cleargate mcp serve`) that this same file will switch to once shipped.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface McpServerEntry {\n type?: 'http' | 'sse' | 'stdio';\n url?: string;\n command?: string;\n args?: string[];\n headers?: Record<string, string>;\n}\n\nexport interface McpJsonShape {\n mcpServers?: Record<string, McpServerEntry>;\n [k: string]: unknown;\n}\n\n/**\n * Pure merge: returns the new file content given the previous parse and the\n * desired cleargate entry. Stable JSON formatting (2-space indent + trailing\n * newline) for byte-equality on idempotent re-runs.\n */\nexport function mergeMcpJson(existing: McpJsonShape | null, entry: McpServerEntry): string {\n const next: McpJsonShape = existing ? { ...existing } : {};\n const servers: Record<string, McpServerEntry> = { ...(next.mcpServers ?? {}) };\n servers.cleargate = entry;\n next.mcpServers = servers;\n return JSON.stringify(next, null, 2) + '\\n';\n}\n\n/**\n * Build the stdio entry for a given pinned cleargate version.\n *\n * BUG-019 + post-0.8.0 fix: prior 0.8.0 used `command: \"cleargate\"` which\n * required a global install. Most users `npx cleargate init` — no global\n * binary on PATH — so Claude Code's spawn failed. Switching to `npx -y\n * cleargate@<pin>` works for both populations (cached after first hit).\n * Pinning the version is the CR-009 resolver pattern: `.mcp.json` doesn't\n * float past the cleargate version that wrote it.\n */\nexport function buildStdioEntry(pinVersion: string): McpServerEntry {\n return {\n command: 'npx',\n args: ['-y', `cleargate@${pinVersion}`, 'mcp', 'serve'],\n };\n}\n\n/**\n * Default stdio entry exported for tests. Production callers should pass an\n * explicit pin (init reads pkg.version).\n */\nexport const STDIO_ENTRY_DEFAULT: McpServerEntry = buildStdioEntry('latest');\n\n/**\n * Filesystem-side entry point. Reads `<cwd>/.mcp.json` if present, merges,\n * writes back. Returns one of {created, updated, unchanged} for caller logging.\n *\n * @param cwd target directory\n * @param pinVersion cleargate version to pin in the npx invocation; the\n * init pipeline passes pkg.version. Defaults to `latest`\n * for callers that don't have a version handy (tests).\n */\nexport function injectMcpJson(\n cwd: string,\n pinVersion: string = 'latest',\n): 'created' | 'updated' | 'unchanged' {\n const dst = path.join(cwd, '.mcp.json');\n const entry: McpServerEntry = buildStdioEntry(pinVersion);\n\n let existing: McpJsonShape | null = null;\n let existingRaw: string | null = null;\n if (fs.existsSync(dst)) {\n existingRaw = fs.readFileSync(dst, 'utf8');\n try {\n existing = JSON.parse(existingRaw) as McpJsonShape;\n } catch {\n // Malformed — surface to caller via stderr; safest move is to refuse\n // overwrite. Caller decides whether to abort or continue with a warning.\n throw new Error(\n `inject-mcp-json: ${dst} is not valid JSON; refusing to overwrite. Fix or remove the file and re-run init.`,\n );\n }\n }\n\n const next = mergeMcpJson(existing, entry);\n\n if (existingRaw !== null && existingRaw === next) {\n return 'unchanged';\n }\n\n fs.writeFileSync(dst, next);\n return existingRaw === null ? 'created' : 'updated';\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\n\nexport interface WikiBuildOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp for last_ingest field (defaults to new Date().toISOString()) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n}\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\n/** Terminal statuses — items with these statuses go to the Archive section. */\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n/**\n * Rollup threshold: if an active epic has >= ROLLUP_THRESHOLD active stories,\n * collapse them into a single summary line instead of individual rows.\n */\nconst ROLLUP_THRESHOLD = 3;\n\n/**\n * Active index bucket order: epics → sprints → proposals → crs → bugs → orphan stories.\n * Topics always skipped (written by query --persist only).\n */\nconst ACTIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\nexport async function wikiBuildHandler(opts: WikiBuildOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const templateDir = opts.templateDir;\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`wiki build: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n // Ensure wiki directory structure exists\n for (const bucket of BUCKET_ORDER) {\n fs.mkdirSync(path.join(wikiRoot, bucket), { recursive: true });\n }\n\n // Step 2: scan raw items\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Step 3: write per-item wiki pages\n const timestamp = now();\n let pagesWritten = 0;\n\n for (const item of items) {\n const sha = getGitSha(item.rawPath, gitRunner) ?? '';\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n\n const wikiPage: WikiPage = {\n type: item.type,\n id: item.id,\n parent,\n children,\n status: String(item.fm['status'] ?? ''),\n remote_id: String(item.fm['remote_id'] ?? ''),\n raw_path: item.rawPath,\n last_ingest: timestamp,\n last_ingest_commit: sha,\n repo: item.repo,\n };\n\n const body = buildPageBody(item, wikiPage);\n const content = serializePage(wikiPage, body);\n\n const pageDir = path.join(wikiRoot, item.bucket);\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(path.join(pageDir, `${item.id}.md`), content, 'utf8');\n pagesWritten++;\n }\n\n // Step 4: build index.md\n const indexContent = buildIndex(items);\n fs.writeFileSync(path.join(wikiRoot, 'index.md'), indexContent, 'utf8');\n\n // Step 5: build log.md\n const logContent = buildLog(items, timestamp);\n fs.writeFileSync(path.join(wikiRoot, 'log.md'), logContent, 'utf8');\n\n // Step 6: write synthesis pages\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n\n // Step 7: report\n stdout(`wiki build: OK (${pagesWritten} pages written)\\n`);\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: RawItem, page: WikiPage): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.').slice(0, 200);\n\n const blastParts: string[] = [];\n if (page.parent) blastParts.push(page.parent);\n for (const child of page.children) blastParts.push(child);\n\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction buildIndex(items: RawItem[]): string {\n const header = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n ];\n\n // Empty-delivery case\n if (items.length === 0) {\n return [\n ...header,\n '## Active',\n '',\n '_No active items._',\n '',\n '## Archive',\n '',\n '_No archived items._',\n '',\n ].join('\\n');\n }\n\n // 1. Partition items into active and archived\n const active: RawItem[] = [];\n const archived: RawItem[] = [];\n for (const item of items) {\n const status = String(item.fm['status'] ?? '');\n if (TERMINAL_STATUSES.has(status)) {\n archived.push(item);\n } else {\n active.push(item);\n }\n }\n\n // 2. Build storiesByEpic: active stories grouped by parent epic id.\n // Only include stories whose parent epic is itself active.\n const activeEpicIds = new Set(\n active.filter((i) => i.bucket === 'epics').map((i) => i.id),\n );\n const storiesByEpic = new Map<string, RawItem[]>();\n\n for (const item of active) {\n if (item.bucket !== 'stories') continue;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n // Strip [[ ]] wrappers if present\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n if (epicId && activeEpicIds.has(epicId)) {\n const list = storiesByEpic.get(epicId) ?? [];\n list.push(item);\n storiesByEpic.set(epicId, list);\n }\n }\n\n // Orphan active stories: no parent_epic_ref OR parent epic not active\n const orphanStories = active.filter((item) => {\n if (item.bucket !== 'stories') return false;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n return !epicId || !activeEpicIds.has(epicId);\n });\n\n // 3. Emit ## Active section\n const activeLines: string[] = ['## Active', ''];\n\n for (const bucket of ACTIVE_BUCKET_ORDER) {\n if (bucket === 'stories') {\n // Orphan stories only\n const sorted = orphanStories.slice().sort((a, b) => a.id.localeCompare(b.id));\n for (const item of sorted) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n } else if (bucket === 'epics') {\n const epicItems = active\n .filter((i) => i.bucket === 'epics')\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const epic of epicItems) {\n const status = String(epic.fm['status'] ?? '');\n activeLines.push(`- [[${epic.id}]] (${epic.type}) — ${status}`);\n\n // 4. Emit story rollup or individual rows under the epic\n const epicStories = (storiesByEpic.get(epic.id) ?? [])\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n if (epicStories.length >= ROLLUP_THRESHOLD) {\n // Build status breakdown: count per status, sort by count desc then status asc\n const counts = new Map<string, number>();\n for (const s of epicStories) {\n const st = String(s.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n const breakdown = [...counts.entries()]\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n // Extract epic numeric prefix for rollup label (e.g. EPIC-014 → 014)\n const epicNum = epic.id.replace(/^EPIC-/, '');\n activeLines.push(` - STORY-${epicNum}-xx (${epicStories.length} stories) — ${breakdown}`);\n } else {\n for (const story of epicStories) {\n const st = String(story.fm['status'] ?? '');\n activeLines.push(` - [[${story.id}]] (${story.type}) — ${st}`);\n }\n }\n }\n } else {\n const bucketItems = active\n .filter((i) => i.bucket === bucket)\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const item of bucketItems) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n }\n }\n\n activeLines.push('');\n\n // 5. Emit ## Archive section — per-bucket summary only\n const archiveLines: string[] = ['## Archive', ''];\n // Bucket order for archive summary (excluding topics and stories — stories\n // don't get their own archive summary line per plan)\n const ARCHIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\n for (const bucket of ARCHIVE_BUCKET_ORDER) {\n const bucketArchived = archived.filter((i) => i.bucket === bucket);\n if (bucketArchived.length === 0) continue;\n\n // Count per status\n const counts = new Map<string, number>();\n for (const item of bucketArchived) {\n const st = String(item.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n // Sort by count desc then status asc, omit zero-count statuses\n const parts = [...counts.entries()]\n .filter(([, n]) => n > 0)\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n\n const label = BUCKET_LABELS[bucket] ?? bucket;\n archiveLines.push(`- ${label}: ${parts} · [expand](archive/${bucket}.md)`);\n }\n\n archiveLines.push('');\n\n return [...header, ...activeLines, ...archiveLines].join('\\n');\n}\n\nfunction buildLog(items: RawItem[], timestamp: string): string {\n if (items.length === 0) {\n return '# Wiki Event Log\\n\\n';\n }\n\n const entries = items.map((item) =>\n [\n `- timestamp: \"${timestamp}\"`,\n ` actor: \"cleargate wiki build\"`,\n ` action: \"create\"`,\n ` target: \"${item.id}\"`,\n ` path: \"${item.rawPath}\"`,\n ].join('\\n'),\n );\n\n return ['# Wiki Event Log', '', ...entries, ''].join('\\n');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { deriveBucket } from './derive-bucket.js';\nimport { deriveRepo } from './derive-repo.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface RawItem {\n /** Absolute path on disk */\n absPath: string;\n /** Path relative to repo root */\n rawPath: string;\n id: string;\n bucket: string;\n type: WikiPageType;\n repo: RepoTag;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/**\n * Scan pending-sync/ + archive/ under deliveryRoot for markdown work items.\n * Returns a sorted (by id) list of parsed raw items.\n */\nexport function scanRawItems(deliveryRoot: string, repoRoot: string): RawItem[] {\n const results: RawItem[] = [];\n\n for (const subdir of ['pending-sync', 'archive']) {\n const dir = path.join(deliveryRoot, subdir);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { recursive: true, encoding: 'utf8' }) as string[];\n for (const rel of entries) {\n if (!rel.endsWith('.md')) continue;\n if (rel.includes('~') || rel.startsWith('.')) continue;\n\n const absPath = path.join(dir, rel);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n // Compute rawPath relative to repo root\n const rawPath = path.relative(repoRoot, absPath).replace(/\\\\/g, '/');\n\n // Check exclusions\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) continue;\n\n // Derive bucket info from filename\n const filename = path.basename(absPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch {\n // Not a recognized work-item filename — skip silently\n continue;\n }\n\n // Derive repo from path\n let repo: RepoTag;\n try {\n repo = deriveRepo(rawPath);\n } catch {\n continue;\n }\n\n // Parse frontmatter\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n // Malformed frontmatter — skip\n continue;\n }\n\n results.push({\n absPath,\n rawPath,\n id: bucketInfo.id,\n bucket: bucketInfo.bucket,\n type: bucketInfo.type,\n repo,\n fm,\n body,\n });\n }\n }\n\n // Sort deterministically by id (alphanumeric ascending)\n results.sort((a, b) => a.id.localeCompare(b.id));\n return results;\n}\n","import type { WikiPageType } from './page-schema.js';\n\nexport interface BucketInfo {\n type: WikiPageType;\n id: string;\n bucket: string;\n}\n\nconst PREFIX_MAP: Array<{ prefix: string; type: WikiPageType; bucket: string }> = [\n { prefix: 'EPIC-', type: 'epic', bucket: 'epics' },\n { prefix: 'STORY-', type: 'story', bucket: 'stories' },\n { prefix: 'SPRINT-', type: 'sprint', bucket: 'sprints' },\n { prefix: 'PROPOSAL-', type: 'proposal', bucket: 'proposals' },\n { prefix: 'CR-', type: 'cr', bucket: 'crs' },\n { prefix: 'BUG-', type: 'bug', bucket: 'bugs' },\n];\n\n/**\n * Derive bucket, type, and id from a filename stem.\n * Filename `STORY-042-01_name.md` → `{ type: 'story', id: 'STORY-042-01', bucket: 'stories' }`.\n */\nexport function deriveBucket(filename: string): BucketInfo {\n // Strip path if any\n const base = filename.includes('/') ? filename.split('/').pop()! : filename;\n // Remove .md suffix\n const stem = base.endsWith('.md') ? base.slice(0, -3) : base;\n // id = everything before the first `_`\n const underscoreIdx = stem.indexOf('_');\n const id = underscoreIdx === -1 ? stem : stem.slice(0, underscoreIdx);\n\n for (const { prefix, type, bucket } of PREFIX_MAP) {\n if (id.startsWith(prefix)) {\n return { type, id, bucket };\n }\n }\n\n throw new Error(`deriveBucket: cannot determine bucket for filename: ${filename}`);\n}\n","import type { RepoTag } from './page-schema.js';\n\n/**\n * A1 helper: derive the `repo` tag from a raw file path prefix.\n * Mapping is per §10.4 field notes.\n */\nexport function deriveRepo(rawPath: string): RepoTag {\n if (rawPath.startsWith('cleargate-cli/')) return 'cli';\n if (rawPath.startsWith('mcp/')) return 'mcp';\n if (rawPath.startsWith('.cleargate/') || rawPath.startsWith('cleargate-planning/')) return 'planning';\n throw new Error(`cannot derive repo for path: ${rawPath}`);\n}\n","import { spawnSync } from 'node:child_process';\n\nexport type GitRunner = (cmd: string, args: string[]) => string;\n\n/**\n * A2 helper: return the git SHA of the last commit touching rawPath.\n * Returns null when the file is untracked (empty stdout, exit 0).\n * Accepts an optional `runner` test seam.\n */\nexport function getGitSha(rawPath: string, runner?: GitRunner): string | null {\n const run = runner ?? defaultRunner;\n const out = run('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n return out.length > 0 ? out : null;\n}\n\nfunction defaultRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n return result.stdout ?? '';\n}\n","/**\n * §10.4 Wiki Page Schema — exactly nine frontmatter fields.\n * Lint will flag any extra or missing fields.\n */\n\nexport type WikiPageType = 'epic' | 'story' | 'sprint' | 'proposal' | 'cr' | 'bug' | 'topic';\nexport type RepoTag = 'cli' | 'mcp' | 'planning';\n\n/** The nine-field frontmatter shape every wiki page must satisfy. */\nexport interface WikiPage {\n type: WikiPageType;\n id: string;\n parent: string; // \"[[EPIC-042]]\" or \"\" if none\n children: string[]; // [\"[[STORY-042-01]]\", ...]\n status: string;\n remote_id: string;\n raw_path: string;\n last_ingest: string; // ISO 8601 UTC\n last_ingest_commit: string; // git SHA or \"\"\n repo: RepoTag;\n}\n\n/** Serialise a WikiPage frontmatter + body into a markdown string. */\nexport function serializePage(page: WikiPage, body: string): string {\n const childrenYaml =\n page.children.length === 0\n ? '[]'\n : '\\n' + page.children.map((c) => ` - \"${c}\"`).join('\\n');\n\n const fm = [\n '---',\n `type: ${page.type}`,\n `id: \"${page.id}\"`,\n `parent: \"${page.parent}\"`,\n `children: ${childrenYaml}`,\n `status: \"${page.status}\"`,\n `remote_id: \"${page.remote_id}\"`,\n `raw_path: \"${page.raw_path}\"`,\n `last_ingest: \"${page.last_ingest}\"`,\n `last_ingest_commit: \"${page.last_ingest_commit}\"`,\n `repo: \"${page.repo}\"`,\n '---',\n ].join('\\n');\n\n return `${fm}\\n\\n${body}`;\n}\n\n/** Parse a serialised wiki page back into a WikiPage. Throws on schema violations. */\nexport function parsePage(raw: string): WikiPage {\n const { fm } = parseFmRaw(raw);\n\n const type = fm['type'] as WikiPageType;\n const id = String(fm['id'] ?? '');\n const parent = String(fm['parent'] ?? '');\n const rawChildren = fm['children'];\n const children: string[] = Array.isArray(rawChildren)\n ? (rawChildren as unknown[]).map(String)\n : [];\n const status = String(fm['status'] ?? '');\n const remote_id = String(fm['remote_id'] ?? '');\n const raw_path = String(fm['raw_path'] ?? '');\n const last_ingest = String(fm['last_ingest'] ?? '');\n const last_ingest_commit = String(fm['last_ingest_commit'] ?? '');\n const repo = fm['repo'] as RepoTag;\n\n return { type, id, parent, children, status, remote_id, raw_path, last_ingest, last_ingest_commit, repo };\n}\n\nfunction parseFmRaw(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') throw new Error('parsePage: missing opening ---');\n let close = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { close = i; break; }\n }\n if (close === -1) throw new Error('parsePage: missing closing ---');\n const fmLines = lines.slice(1, close);\n const body = lines.slice(close + 1).join('\\n').replace(/^\\n/, '');\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '[]') { fm[key] = []; continue; }\n if (val === '') { fm[key] = []; continue; }\n // inline list check\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1);\n fm[key] = inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return { fm, body };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the active-sprint synthesis page.\n * Loads template from templates/synthesis/active-sprint.md.\n * Partitions sprints by activated_at / completed_at frontmatter values.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'active-sprint.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n\n // Partition sprints:\n // - active: activated_at is set (non-null, non-empty) AND completed_at is not set\n // - completed: completed_at is set\n // - planned: neither activated_at nor completed_at is set\n const active = sprints.filter((s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n const completed = sprints.filter((s) => isSet(s.fm['completed_at']));\n const planned = sprints.filter((s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n\n const data: Record<string, unknown> = {\n active: active.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_active: active.length === 0 ? [{}] : [],\n planned: planned.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_planned: planned.length === 0 ? [{}] : [],\n completed: completed.slice(0, 3).map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_completed: completed.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // tsup bundles all modules into dist/cli.js.\n // When running the built bundle: import.meta.url = file://.../cleargate-cli/dist/cli.js\n // __dirname = cleargate-cli/dist/\n // ../templates/synthesis = cleargate-cli/templates/synthesis ✓ (source)\n // AND dist/templates/synthesis is also available (copied by onSuccess) ✓\n //\n // When vitest runs source (test seam): templateDir is always passed explicitly,\n // so this default is only used for the built/production case.\n //\n // Strategy: go one level up from the file containing this code (works for both\n // the dist/ bundle and npm-published dist/ layout), then into templates/synthesis.\n // For dist/cli.js: one up = package root → templates/synthesis. ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * Tiny Mustache-lite template renderer.\n * Supports:\n * {{var}} — variable substitution (missing → empty string)\n * {{#section}}...{{/section}} — array iteration; inner {{field}} resolves\n * against the current array element\n *\n * Anything more complex throws an Error.\n * No external dependencies.\n */\n\n/** Render a template string with the given data context. */\nexport function renderTemplate(template: string, data: Record<string, unknown>): string {\n // Validate: no nested sections or unsupported tags\n // We only support {{var}}, {{#section}}...{{/section}}\n const tagRe = /\\{\\{([^}]+)\\}\\}/g;\n const matches = [...template.matchAll(tagRe)].map((m) => m[1].trim());\n for (const tag of matches) {\n if (tag.startsWith('^') || tag.startsWith('>') || tag.startsWith('!') || tag.startsWith('=')) {\n throw new Error(`renderTemplate: unsupported tag type: {{${tag}}}`);\n }\n }\n\n return renderSection(template, data);\n}\n\nfunction renderSection(template: string, ctx: Record<string, unknown>): string {\n // Process {{#section}}...{{/section}} blocks first\n const sectionRe = /\\{\\{#(\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}/g;\n\n let result = template.replace(sectionRe, (_match, key: string, inner: string) => {\n const val = ctx[key];\n if (!Array.isArray(val)) {\n // Non-array truthy: render inner once with same ctx; falsy: skip\n if (!val) return '';\n return renderSection(inner, ctx);\n }\n if (val.length === 0) return '';\n return val\n .map((item: unknown) => {\n const itemCtx =\n item !== null && typeof item === 'object'\n ? (item as Record<string, unknown>)\n : { '.': item };\n return renderSection(inner, itemCtx);\n })\n .join('');\n });\n\n // Then substitute remaining {{var}} tokens\n result = result.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key: string) => {\n const val = ctx[key];\n if (val === undefined || val === null) return '';\n return String(val);\n });\n\n return result;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the open-gates (blocked items) synthesis page.\n * Loads template from templates/synthesis/open-gates.md.\n *\n * Three gate buckets (matching real corpus textual statuses):\n * Gate 1 — proposals with approved: false OR status: \"Draft\" / \"Approved\" (not yet shipped)\n * Gate 2 — stories with ambiguity starting with 🟡 or 🔴\n * Gate 3 — any item with status: \"Ready\" AND remote_id empty / null\n *\n * NOTE: The previous implementation filtered on status.includes('🔴') which matched\n * zero items in the real corpus (actual statuses are textual: Draft, Ready, Planned,\n * Active, Completed, Approved). This is the corpus-shape fix for STORY-002-09.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'open-gates.md'), 'utf8');\n\n // Gate 1: proposals pending approval\n const gate1 = state.filter((i) => {\n if (i.bucket !== 'proposals') return false;\n const status = String(i.fm['status'] ?? '');\n const approved = i.fm['approved'];\n // Proposals that are Draft or explicitly not approved\n return status === 'Draft' || approved === false || approved === 'false';\n });\n\n // Gate 2: stories with elevated ambiguity (🟡 Medium or 🔴 High)\n const gate2 = state.filter((i) => {\n if (i.bucket !== 'stories') return false;\n const ambiguity = String(i.fm['ambiguity'] ?? '');\n return ambiguity.startsWith('🟡') || ambiguity.startsWith('🔴');\n });\n\n // Gate 3: items that are Ready but not yet pushed (remote_id empty or null)\n const gate3 = state.filter((i) => {\n const status = String(i.fm['status'] ?? '');\n if (status !== 'Ready') return false;\n const remoteId = i.fm['remote_id'];\n return remoteId === null || remoteId === undefined || String(remoteId).trim() === '';\n });\n\n const data: Record<string, unknown> = {\n gate1: gate1.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate1: gate1.length === 0 ? [{}] : [],\n gate2: gate2.map((i) => ({ id: i.id, ambiguity: String(i.fm['ambiguity'] ?? '') })),\n no_gate2: gate2.length === 0 ? [{}] : [],\n gate3: gate3.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate3: gate3.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the product-state synthesis page.\n * Loads template from templates/synthesis/product-state.md.\n *\n * Shipped = items in archive/ subdir (rawPath contains '/archive/')\n * Active = status is Active, In Progress, or 🟢-prefixed\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'product-state.md'), 'utf8');\n\n function countBucket(bucket: string) {\n return state.filter((i) => i.bucket === bucket);\n }\n\n function isShipped(item: RawItem) {\n return item.rawPath.includes('/archive/');\n }\n\n function isActive(item: RawItem) {\n const status = String(item.fm['status'] ?? '');\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n }\n\n const buckets = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs'];\n\n function countFor(bucket: string, predicate: (i: RawItem) => boolean): number {\n return countBucket(bucket).filter(predicate).length;\n }\n\n const epics = countBucket('epics');\n const activeEpicsList = epics.filter(isActive);\n const shippedItems = state.filter(isShipped);\n\n const data: Record<string, unknown> = {\n // Totals\n total_epics: epics.length,\n total_stories: countBucket('stories').length,\n total_sprints: countBucket('sprints').length,\n total_proposals: countBucket('proposals').length,\n total_crs: countBucket('crs').length,\n total_bugs: countBucket('bugs').length,\n\n // Active counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`active_${b}`, countFor(b, isActive)])),\n\n // Shipped counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`shipped_${b}`, countFor(b, isShipped)])),\n\n // Active epics list\n active_epics_list: activeEpicsList.map((i) => ({\n id: i.id,\n status: String(i.fm['status'] ?? ''),\n })),\n no_active_epics: activeEpicsList.length === 0 ? [{}] : [],\n\n // Shipped items list\n shipped_items: shippedItems.map((i) => ({\n id: i.id,\n bucket: i.bucket,\n status: String(i.fm['status'] ?? ''),\n })),\n no_shipped: shippedItems.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the roadmap synthesis page.\n * Loads template from templates/synthesis/roadmap.md.\n *\n * Sprint buckets (by activated_at / completed_at):\n * in-flight: activated_at set AND completed_at not set\n * planned: neither set\n * shipped: completed_at set\n *\n * Epic buckets (by status):\n * active: Active / In Progress / 🟢-prefixed\n * planned: Ready / Planned / Draft\n * shipped: Completed / Approved\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'roadmap.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n const epics = state.filter((i) => i.bucket === 'epics');\n\n // Sprint partitions\n const inFlightSprints = sprints.filter(\n (s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const plannedSprints = sprints.filter(\n (s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const shippedSprints = sprints.filter((s) => isSet(s.fm['completed_at']));\n\n // Epic partitions\n const activeEpics = epics.filter((e) => isActiveStatus(String(e.fm['status'] ?? '')));\n const plannedEpics = epics.filter((e) => isPlannedStatus(String(e.fm['status'] ?? '')));\n const shippedEpics = epics.filter((e) => isShippedStatus(String(e.fm['status'] ?? '')));\n\n const data: Record<string, unknown> = {\n in_flight_sprints: inFlightSprints.map((s) => ({\n id: s.id,\n activated_at: String(s.fm['activated_at'] ?? ''),\n })),\n no_in_flight_sprints: inFlightSprints.length === 0 ? [{}] : [],\n\n planned_sprints: plannedSprints.map((s) => ({\n id: s.id,\n status: String(s.fm['status'] ?? ''),\n })),\n no_planned_sprints: plannedSprints.length === 0 ? [{}] : [],\n\n shipped_sprints: shippedSprints.map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_shipped_sprints: shippedSprints.length === 0 ? [{}] : [],\n\n active_epics: activeEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_active_epics: activeEpics.length === 0 ? [{}] : [],\n\n planned_epics: plannedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_planned_epics: plannedEpics.length === 0 ? [{}] : [],\n\n shipped_epics: shippedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_shipped_epics: shippedEpics.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction isActiveStatus(status: string): boolean {\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n}\n\nfunction isPlannedStatus(status: string): boolean {\n return status === 'Ready' || status === 'Planned' || status === 'Draft';\n}\n\nfunction isShippedStatus(status: string): boolean {\n return status === 'Completed' || status === 'Approved';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * manifest.ts — STORY-009-01\n *\n * Scaffold manifest loading, drift classification, and atomic drift-state writing.\n * Node built-ins only: fs/promises, path.\n *\n * MANIFEST.json does not exist until STORY-009-02 runs `npm run build`.\n * loadPackageManifest throws a clear error when the file is absent — not raw ENOENT.\n */\n\nimport { readFile, writeFile, rename, mkdir } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { hashNormalized } from './sha256.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type Tier =\n | 'protocol'\n | 'template'\n | 'agent'\n | 'hook'\n | 'skill'\n | 'cli-config'\n | 'user-artifact'\n | 'derived';\n\nexport type DriftState =\n | 'clean'\n | 'user-modified'\n | 'upstream-changed'\n | 'both-changed'\n | 'untracked';\n\nexport interface ManifestEntry {\n path: string;\n sha256: string | null;\n tier: Tier;\n overwrite_policy: 'always' | 'merge-3way' | 'skip' | 'preserve' | 'pin-aware';\n preserve_on_uninstall: boolean;\n}\n\nexport interface ManifestFile {\n cleargate_version: string;\n generated_at: string;\n files: ManifestEntry[];\n /**\n * Present only in `.cleargate/.install-manifest.json` (the install snapshot).\n * Stamped by `cleargate init` as the FINAL step (STORY-009-03).\n * Not present in the package-shipped MANIFEST.json.\n */\n installed_at?: string;\n}\n\nexport interface DriftMapEntry {\n state: DriftState;\n entry: ManifestEntry;\n install_sha: string | null;\n current_sha: string | null;\n package_sha: string | null;\n}\n\nexport interface DriftMap {\n [filePath: string]: DriftMapEntry;\n}\n\n/**\n * The on-disk shape of `.cleargate/.drift-state.json`.\n * Wraps the DriftMap with a `last_refreshed` timestamp so the daily-throttle\n * logic in `cleargate doctor --check-scaffold` can skip re-computation.\n */\nexport interface DriftStateFile {\n last_refreshed: string;\n drift: DriftMap;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\nexport interface LoadPackageManifestOpts {\n /**\n * Override the root directory where MANIFEST.json is resolved.\n * Default: resolved via import.meta.url (1 level up from dist/ in prod,\n * or cleargate-planning/ in dev).\n *\n * This seam is mandatory per FLASHCARD #tsup #bundle #import-meta — the\n * bundle collapses import.meta.url to the bundle file so default resolution\n * must never be relied upon in tests.\n */\n packageRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction resolveDefaultPackageRoot(): string {\n // In production (dist/cli.js), import.meta.url points to dist/cli.js.\n // MANIFEST.json is copied to dist/ by the build step — so 0 levels up.\n // In dev, import.meta.url is cleargate-cli/src/lib/manifest.ts — 3 levels up\n // to cleargate-cli/, then ../cleargate-planning/ for the fixture source.\n // Rather than guessing, we prefer the dist/ sibling first; fall back to the\n // source-tree dev path.\n const here = new URL('.', import.meta.url).pathname;\n\n // Try: same directory (dist/ scenario)\n const distCandidate = path.join(here, 'MANIFEST.json');\n if (existsSync(distCandidate)) {\n return here;\n }\n\n // Try: 1 level up (also dist/ scenario when emitted as dist/lib/manifest.js)\n const oneLevelUp = path.join(here, '..', 'MANIFEST.json');\n if (existsSync(oneLevelUp)) {\n return path.join(here, '..');\n }\n\n // Dev fallback: from src/lib walk up to repo root, then cleargate-planning/\n // src/lib → src → cleargate-cli → repo-root → cleargate-planning\n const devCandidate = path.join(here, '..', '..', '..', 'cleargate-planning', 'MANIFEST.json');\n if (existsSync(devCandidate)) {\n return path.join(here, '..', '..', '..', 'cleargate-planning');\n }\n\n // Cannot determine — caller will get a clear error from loadPackageManifest\n return here;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Load MANIFEST.json from the installed package root.\n *\n * Uses `opts.packageRoot` (required in tests) or default resolution.\n * Throws a descriptive error when the file is absent rather than a raw ENOENT.\n */\nexport function loadPackageManifest(opts?: LoadPackageManifestOpts): ManifestFile {\n const packageRoot = opts?.packageRoot ?? resolveDefaultPackageRoot();\n const manifestPath = path.join(packageRoot, 'MANIFEST.json');\n\n if (!existsSync(manifestPath)) {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n let raw: string;\n try {\n // Synchronous read — callers treat loadPackageManifest as synchronous (startup-time).\n raw = readFileSync(manifestPath, 'utf-8');\n } catch {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n return JSON.parse(raw) as ManifestFile;\n}\n\n/**\n * Load the install-time snapshot from `<projectRoot>/.cleargate/.install-manifest.json`.\n * Returns null if the file does not exist (first install or pre-manifest era).\n */\nexport async function loadInstallSnapshot(projectRoot: string): Promise<ManifestFile | null> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n try {\n const raw = await readFile(snapshotPath, 'utf-8');\n return JSON.parse(raw) as ManifestFile;\n } catch {\n return null;\n }\n}\n\n/**\n * Compute the SHA256 of a tracked file in the current working tree.\n * Returns null when the file does not exist on disk.\n */\nexport async function computeCurrentSha(\n file: ManifestEntry,\n projectRoot: string\n): Promise<string | null> {\n const filePath = path.join(projectRoot, file.path);\n try {\n const raw = await readFile(filePath);\n return hashNormalized(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Classify the drift state of a single tracked file.\n *\n * Decision table (PROP-006 §2.4):\n *\n * | Tier | Result |\n * |---------------|-------------|\n * | user-artifact | untracked |\n *\n * | pkgSha | installSha | currentSha | Result |\n * |--------|------------|------------|-------------------|\n * | any | any | null | untracked |\n * | A | A | A | clean |\n * | A | A | B (≠A) | user-modified |\n * | B (≠A) | A | A | upstream-changed |\n * | all differ pairwise | both-changed |\n */\nexport function classify(\n pkgSha: string | null,\n installSha: string | null,\n currentSha: string | null,\n tier: Tier\n): DriftState {\n // user-artifact short-circuit (EPIC-009 §6 Q8)\n if (tier === 'user-artifact') {\n return 'untracked';\n }\n\n // Missing current file\n if (currentSha === null) {\n return 'untracked';\n }\n\n const installEqualsPackage = installSha === pkgSha;\n const currentEqualsInstall = currentSha === installSha;\n\n if (installEqualsPackage && currentEqualsInstall) {\n // install == current == package\n return 'clean';\n }\n\n if (installEqualsPackage && !currentEqualsInstall) {\n // install == package, current != install => user modified\n return 'user-modified';\n }\n\n if (!installEqualsPackage && currentEqualsInstall) {\n // install == current, package != install => upstream changed\n return 'upstream-changed';\n }\n\n // All three differ pairwise\n return 'both-changed';\n}\n\n/**\n * Options for writeDriftState.\n */\nexport interface WriteDriftStateOpts {\n /**\n * ISO-8601 timestamp to record as `last_refreshed` in the output file.\n * When omitted, the current time is used (new Date().toISOString()).\n * This seam is mandatory for deterministic tests.\n */\n lastRefreshed?: string;\n}\n\n/**\n * Atomically write the drift-state map to `<projectRoot>/.cleargate/.drift-state.json`.\n *\n * The on-disk format is wrapped: `{ last_refreshed: string, drift: DriftMap }`.\n * This allows the daily-throttle logic in `cleargate doctor --check-scaffold`\n * to read the timestamp without re-computing all SHAs.\n *\n * Uses write-temp-then-rename (atomic on POSIX; best-effort on Windows).\n * Ensures parent directory exists before writing.\n *\n * STORY-009-04 extended the signature from `(projectRoot, DriftMap)` to\n * `(projectRoot, DriftMap, opts?)` — callers passing only two args continue to work.\n */\nexport async function writeDriftState(\n projectRoot: string,\n state: DriftMap,\n opts?: WriteDriftStateOpts\n): Promise<void> {\n const cleargatDir = path.join(projectRoot, '.cleargate');\n const finalPath = path.join(cleargatDir, '.drift-state.json');\n const tmpPath = `${finalPath}.tmp`;\n\n const lastRefreshed = opts?.lastRefreshed ?? new Date().toISOString();\n const fileContent: DriftStateFile = { last_refreshed: lastRefreshed, drift: state };\n\n await mkdir(cleargatDir, { recursive: true });\n await writeFile(tmpPath, JSON.stringify(fileContent, null, 2) + '\\n', 'utf-8');\n await rename(tmpPath, finalPath);\n}\n\n/**\n * Read the drift-state file written by `writeDriftState`.\n * Returns null when the file does not exist or is malformed.\n */\nexport async function readDriftState(projectRoot: string): Promise<DriftStateFile | null> {\n const driftPath = path.join(projectRoot, '.cleargate', '.drift-state.json');\n try {\n const raw = await readFile(driftPath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n // Accept both the new wrapped format {last_refreshed, drift} and the old flat format\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'last_refreshed' in parsed &&\n 'drift' in parsed\n ) {\n return parsed as DriftStateFile;\n }\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * sha256.ts — STORY-009-01\n *\n * Deterministic SHA256 hasher with cross-platform content normalization.\n * Node built-ins only: crypto, fs/promises, path.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Hash normalized content (string or Buffer) — 64 hex chars.\n *\n * Normalization steps applied in order:\n * 1. Convert Buffer to UTF-8 string.\n * 2. Strip leading BOM (U+FEFF).\n * 3. Normalize CRLF → LF.\n * 4. Enforce trailing newline (append `\\n` if missing).\n */\nexport function hashNormalized(content: string | Buffer): string {\n let text: string =\n Buffer.isBuffer(content) ? content.toString('utf-8') : content;\n\n // 1. Strip leading BOM\n if (text.startsWith('\\ufeff')) {\n text = text.slice(1);\n }\n\n // 2. CRLF → LF\n text = text.replace(/\\r\\n/g, '\\n');\n\n // 3. Enforce trailing newline\n if (!text.endsWith('\\n')) {\n text += '\\n';\n }\n\n return createHash('sha256').update(text, 'utf-8').digest('hex');\n}\n\n/**\n * Read a file, normalize its content, and return the SHA256 hex digest.\n */\nexport async function hashFile(filePath: string): Promise<string> {\n const raw = await readFile(filePath);\n return hashNormalized(raw);\n}\n\n/**\n * Return the first 8 hex characters of a full SHA256 digest for human-readable output.\n */\nexport function shortHash(full: string): string {\n return full.slice(0, 8);\n}\n","/**\n * prompts.ts — minimal readline-based prompt helpers\n *\n * STORY-009-03: created here (cited in story §3 as \"existing\"; verified absent).\n * STORY-010-01: added promptEmail for participant identity flow.\n * Used by init.ts restore flow and future commands that need interactive input.\n */\nimport * as readline from 'node:readline';\n\nexport interface PromptOptions {\n /** Override stdin for testing */\n stdin?: NodeJS.ReadableStream;\n /** Override stdout write for testing */\n stdout?: (s: string) => void;\n}\n\n/**\n * Prompt the user with a yes/no question.\n *\n * @param question The question text to display (without [Y/n] suffix — caller includes it)\n * @param defaultYes Whether Enter with no input means yes\n * @param opts Test seams for stdin/stdout\n * @returns true for yes, false for no\n */\nexport async function promptYesNo(\n question: string,\n defaultYes: boolean,\n opts?: PromptOptions,\n): Promise<boolean> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) keeps the cursor inline with the prompt so\n // the user's typed input is visible adjacent to the question — matches every\n // other CLI prompt convention. See BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<boolean>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim().toLowerCase();\n if (trimmed === '') {\n resolve(defaultYes);\n } else if (trimmed === 'y' || trimmed === 'yes') {\n resolve(true);\n } else {\n resolve(false);\n }\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — treat as default\n resolve(defaultYes);\n }\n });\n });\n}\n\n/**\n * Prompt the user for a text value with an optional default.\n *\n * @param question The question text to display\n * @param defaultValue The value used when the user presses Enter without typing\n * @param opts Test seams for stdin/stdout\n * @returns The entered string, or `defaultValue` if Enter is pressed with no input\n */\nexport async function promptEmail(\n question: string,\n defaultValue: string,\n opts?: PromptOptions,\n): Promise<string> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) — see promptYesNo above and BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<string>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim();\n resolve(trimmed === '' ? defaultValue : trimmed);\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — use default\n resolve(defaultValue);\n }\n });\n });\n}\n","/**\n * identity.ts — participant identity resolver.\n *\n * STORY-010-01: resolveIdentity precedence ladder + writeParticipant atomic write.\n *\n * Resolution order (highest-priority first):\n * 1. .cleargate/.participant.json\n * 2. CLEARGATE_USER env var\n * 3. git config user.email (via child_process.spawnSync — NOT simple-git; see flashcard #cli #simple-git #deps)\n * 4. \"{username}@{hostname}\" host fallback\n *\n * All external sources (env, git, host) are injectable as opts for test hermetics.\n * Do NOT reach into process.env / os.hostname() directly in the happy path.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as fsPromises from 'node:fs/promises';\nimport * as os from 'node:os';\nimport { spawnSync } from 'node:child_process';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type IdentitySource = 'participant-json' | 'env' | 'git' | 'host';\n\nexport interface Identity {\n email: string;\n source: IdentitySource;\n}\n\nexport interface ParticipantFile {\n email: string;\n set_at: string;\n source: 'prompted' | 'inferred';\n}\n\n// ── Read participant file ─────────────────────────────────────────────────────\n\n/**\n * Read .cleargate/.participant.json.\n * Returns null on ENOENT or malformed JSON — caller decides what to do.\n */\nexport function readParticipant(projectRoot: string): ParticipantFile | null {\n const filePath = path.join(projectRoot, '.cleargate', '.participant.json');\n try {\n const raw = fs.readFileSync(filePath, 'utf8');\n return JSON.parse(raw) as ParticipantFile;\n } catch {\n return null;\n }\n}\n\n// ── Write participant file ────────────────────────────────────────────────────\n\n/**\n * Write .cleargate/.participant.json atomically (tmp + rename).\n * Creates .cleargate/ directory if it does not exist.\n */\nexport async function writeParticipant(\n projectRoot: string,\n email: string,\n source: 'prompted' | 'inferred',\n now: () => string = () => new Date().toISOString(),\n): Promise<void> {\n const cleargateDir = path.join(projectRoot, '.cleargate');\n await fsPromises.mkdir(cleargateDir, { recursive: true });\n\n const filePath = path.join(cleargateDir, '.participant.json');\n const tmpPath = filePath + '.tmp.' + Date.now();\n\n const content: ParticipantFile = {\n email,\n set_at: now(),\n source,\n };\n\n await fsPromises.writeFile(tmpPath, JSON.stringify(content, null, 2) + '\\n', 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Resolve identity ──────────────────────────────────────────────────────────\n\nexport interface ResolveIdentityOpts {\n /** Override process.env for test hermetics */\n env?: NodeJS.ProcessEnv;\n /** Override git config user.email resolution. Return null when git unavailable. */\n gitEmail?: () => string | null;\n /** Override os.hostname() */\n hostname?: () => string;\n /** Override os.userInfo().username */\n username?: () => string;\n /** Override new Date().toISOString() (unused here; reserved for callers) */\n now?: () => string;\n}\n\n/**\n * Resolve caller identity via precedence ladder.\n * All external lookups go through opts test seams — never reach process.env\n * or os.hostname() directly so tests can assert precedence without env pollution.\n */\nexport function resolveIdentity(\n projectRoot: string,\n opts: ResolveIdentityOpts = {},\n): Identity {\n // 1. participant-json\n const participant = readParticipant(projectRoot);\n if (participant !== null && participant.email) {\n return { email: participant.email, source: 'participant-json' };\n }\n\n // 2. env\n const envValue = (opts.env ?? process.env)['CLEARGATE_USER'];\n if (envValue && envValue.trim()) {\n return { email: envValue.trim(), source: 'env' };\n }\n\n // 3. git\n const gitEmailFn =\n opts.gitEmail ??\n (() => {\n const result = spawnSync('git', ['config', 'user.email'], {\n encoding: 'utf8',\n timeout: 3000,\n });\n if (result.status === 0 && result.stdout) {\n const trimmed = result.stdout.trim();\n if (trimmed) return trimmed;\n }\n return null;\n });\n\n const gitEmail = gitEmailFn();\n if (gitEmail && gitEmail.trim()) {\n return { email: gitEmail.trim(), source: 'git' };\n }\n\n // 4. host fallback\n const hostnameFn = opts.hostname ?? (() => os.hostname());\n const usernameFn =\n opts.username ??\n (() => {\n try {\n return os.userInfo().username;\n } catch {\n return 'user';\n }\n });\n\n const hostname = hostnameFn();\n const username = usernameFn();\n return { email: `${username}@${hostname}`, source: 'host' };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { deriveBucket } from '../wiki/derive-bucket.js';\nimport { deriveRepo } from '../wiki/derive-repo.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, parsePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\n\nexport interface WikiIngestOptions {\n /** Absolute path to the raw delivery file to ingest */\n rawPath: string;\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: replaces fs.rename (for atomic index.md write test) */\n rename?: (src: string, dst: string) => void;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\nexport async function wikiIngestHandler(opts: WikiIngestOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const rename = opts.rename ?? fs.renameSync;\n const templateDir = opts.templateDir;\n\n const rawPath = opts.rawPath;\n\n // Resolve rawPath: if relative, resolve against cwd\n const absRawPath = path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n // Compute relative path from cwd (repo root)\n const relRawPath = path.relative(cwd, absRawPath).replace(/\\\\/g, '/');\n\n // Step 1: Validate path resolves under <cwd>/.cleargate/delivery/\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const deliveryRootNorm = deliveryRoot.replace(/\\\\/g, '/');\n const absDeliveryRoot = deliveryRoot;\n\n // Check: absRawPath must be under absDeliveryRoot\n const relToDelivery = path.relative(absDeliveryRoot, absRawPath);\n if (relToDelivery.startsWith('..') || path.isAbsolute(relToDelivery)) {\n stderr(`wiki ingest: ${rawPath} not under .cleargate/delivery/\\n`);\n exit(2);\n return;\n }\n\n void deliveryRootNorm; // suppress lint warning\n\n // Step 2: Exclusion check (defense-in-depth)\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => relRawPath.startsWith(excl));\n if (isExcluded) {\n stdout(`wiki ingest: ${rawPath} excluded (skip)\\n`);\n exit(0);\n return;\n }\n\n // Step 3: Derive bucket + id + type + repo\n const filename = path.basename(absRawPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch (e) {\n stderr(`wiki ingest: cannot determine bucket for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let repo: ReturnType<typeof deriveRepo>;\n try {\n repo = deriveRepo(relRawPath);\n } catch (e) {\n stderr(`wiki ingest: cannot derive repo for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n const { type, id, bucket } = bucketInfo;\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const pageDir = path.join(wikiRoot, bucket);\n const pagePath = path.join(pageDir, `${id}.md`);\n\n // Read and parse the raw file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absRawPath, 'utf8');\n } catch (e) {\n stderr(`wiki ingest: cannot read ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch (e) {\n stderr(`wiki ingest: malformed frontmatter in ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // Step 4: Idempotency guard (A2)\n const currentSha = getGitSha(absRawPath, gitRunner) ?? '';\n const pageExists = fs.existsSync(pagePath);\n\n if (pageExists && currentSha !== '') {\n let isNoOp = false;\n try {\n const existingPageContent = fs.readFileSync(pagePath, 'utf8');\n const existingPage = parsePage(existingPageContent);\n\n if (existingPage.last_ingest_commit === currentSha) {\n // Check if raw file content matches what git shows for that SHA\n const contentUnchanged = checkContentUnchanged(absRawPath, currentSha, relRawPath, gitRunner);\n if (contentUnchanged) {\n isNoOp = true;\n }\n }\n } catch {\n // If we can't parse the existing page, proceed with ingest\n }\n\n if (isNoOp) {\n stdout(`wiki ingest: ${id} unchanged (no-op)\\n`);\n exit(0);\n return;\n }\n }\n\n // Determine action\n const action = pageExists ? 'update' : 'create';\n\n // Step 5: Build new WikiPage and write it\n const parent = buildParentRef(fm);\n const children = buildChildrenRefs(fm);\n const timestamp = now();\n\n const wikiPage: WikiPage = {\n type,\n id,\n parent,\n children,\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: relRawPath,\n last_ingest: timestamp,\n last_ingest_commit: currentSha,\n repo,\n };\n\n const pageBody = buildPageBody({ id, fm, body });\n const pageContent = serializePage(wikiPage, pageBody);\n\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(pagePath, pageContent, 'utf8');\n\n // Step 6: Append one log entry to wiki/log.md\n appendLogEntry(wikiRoot, { timestamp, action, id, relRawPath });\n\n // Step 7: Update wiki/index.md (atomic write-temp-then-rename)\n updateIndex(wikiRoot, { id, type, status: wikiPage.status, relRawPath, rename });\n\n // Step 8: Recompile affected synthesis pages (all four — M3 over-recompiles)\n recompileSynthesis(wikiRoot, cwd, templateDir);\n\n // Step 9: Print result\n stdout(`wiki ingest: ${action} ${bucket}/${id}.md\\n`);\n}\n\nfunction checkContentUnchanged(\n absRawPath: string,\n sha: string,\n relRawPath: string,\n gitRunner?: GitRunner,\n): boolean {\n try {\n const run = gitRunner ?? defaultGitRunner;\n // git show <sha>:<relRawPath> returns file content at that commit\n const gitContent = run('git', ['show', `${sha}:${relRawPath}`]);\n // Non-zero exit is handled by runner returning empty string (defaultRunner returns stdout)\n // If empty string returned from show when sha is valid, treat as changed\n if (!gitContent && gitContent !== '') return false;\n const currentContent = fs.readFileSync(absRawPath, 'utf8');\n return gitContent === currentContent;\n } catch {\n return false;\n }\n}\n\nfunction defaultGitRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n if (result.status !== 0) return '\\0__NONZERO__'; // sentinel for non-zero exit\n return result.stdout ?? '';\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: { id: string; fm: Record<string, unknown>; body: string }): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(\n item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.',\n ).slice(0, 200);\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n const blastParts: string[] = [];\n if (parent) blastParts.push(parent);\n for (const child of children) blastParts.push(child);\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction appendLogEntry(\n wikiRoot: string,\n entry: { timestamp: string; action: string; id: string; relRawPath: string },\n): void {\n const logPath = path.join(wikiRoot, 'log.md');\n const logEntry = [\n `- timestamp: \"${entry.timestamp}\"`,\n ` actor: \"cleargate wiki ingest\"`,\n ` action: \"${entry.action}\"`,\n ` target: \"${entry.id}\"`,\n ` path: \"${entry.relRawPath}\"`,\n ].join('\\n');\n\n if (fs.existsSync(logPath)) {\n const existing = fs.readFileSync(logPath, 'utf8');\n // Append to existing log\n const newContent = existing.trimEnd() + '\\n' + logEntry + '\\n';\n fs.writeFileSync(logPath, newContent, 'utf8');\n } else {\n fs.mkdirSync(wikiRoot, { recursive: true });\n fs.writeFileSync(logPath, `# Wiki Event Log\\n\\n${logEntry}\\n`, 'utf8');\n }\n}\n\nfunction updateIndex(\n wikiRoot: string,\n opts: {\n id: string;\n type: string;\n status: string;\n relRawPath: string;\n rename: (src: string, dst: string) => void;\n },\n): void {\n const indexPath = path.join(wikiRoot, 'index.md');\n const tmpPath = `${indexPath}.tmp`;\n\n const newRow = `| [[${opts.id}]] | ${opts.type} | ${opts.status} | ${opts.relRawPath} |`;\n\n let content: string;\n if (fs.existsSync(indexPath)) {\n content = fs.readFileSync(indexPath, 'utf8');\n // Check if a row with this id already exists; if so, replace it\n const idPattern = `[[${opts.id}]]`;\n const lines = content.split('\\n');\n let replaced = false;\n const newLines = lines.map((line) => {\n if (line.includes(idPattern) && line.startsWith('|')) {\n replaced = true;\n return newRow;\n }\n return line;\n });\n\n if (replaced) {\n content = newLines.join('\\n');\n } else {\n // Insert into the correct bucket section\n content = insertIntoSection(content, opts.id, newRow);\n }\n } else {\n // Build a minimal index.md with the item\n content = buildMinimalIndex(opts.id, opts.type, opts.status, opts.relRawPath);\n }\n\n fs.writeFileSync(tmpPath, content, 'utf8');\n opts.rename(tmpPath, indexPath);\n}\n\nfunction insertIntoSection(content: string, id: string, newRow: string): string {\n // Determine which bucket section to insert into\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n const sectionHeader = `## ${label}`;\n\n const lines = content.split('\\n');\n let sectionStart = -1;\n let nextSectionStart = -1;\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === sectionHeader) {\n sectionStart = i;\n // Find next section\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j].startsWith('## ')) {\n nextSectionStart = j;\n break;\n }\n }\n break;\n }\n }\n\n if (sectionStart === -1) {\n // Section doesn't exist — append new section at end\n const sectionContent = [\n '',\n sectionHeader,\n '',\n newRow,\n '',\n ].join('\\n');\n return content.trimEnd() + sectionContent;\n }\n\n // Find insertion point: after any existing rows in this section, sorted by id\n const sectionEnd = nextSectionStart === -1 ? lines.length : nextSectionStart;\n const sectionLines = lines.slice(sectionStart + 1, sectionEnd);\n\n // Find existing row entries and insert in sorted order\n let insertAt = -1;\n for (let i = 0; i < sectionLines.length; i++) {\n const line = sectionLines[i];\n if (line.startsWith('|') && !line.startsWith('|---|')) {\n // Extract the id from the row: | [[ID]] | ...\n const match = /\\|\\s*\\[\\[([^\\]]+)\\]\\]/.exec(line);\n if (match) {\n const rowId = match[1];\n if (id.localeCompare(rowId) <= 0) {\n insertAt = sectionStart + 1 + i;\n break;\n }\n }\n }\n }\n\n if (insertAt === -1) {\n // Append before the next section or at end of section\n // Find last row in section\n let lastRowIdx = sectionStart + 1;\n for (let i = sectionStart + 1; i < sectionEnd; i++) {\n if (lines[i].startsWith('|')) {\n lastRowIdx = i + 1;\n }\n }\n lines.splice(lastRowIdx, 0, newRow);\n } else {\n lines.splice(insertAt, 0, newRow);\n }\n\n return lines.join('\\n');\n}\n\nfunction getBucketFromId(id: string): string {\n if (id.startsWith('EPIC-')) return 'epics';\n if (id.startsWith('STORY-')) return 'stories';\n if (id.startsWith('SPRINT-')) return 'sprints';\n if (id.startsWith('PROPOSAL-')) return 'proposals';\n if (id.startsWith('CR-')) return 'crs';\n if (id.startsWith('BUG-')) return 'bugs';\n return 'topics';\n}\n\nfunction buildMinimalIndex(id: string, type: string, status: string, relRawPath: string): string {\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n\n const lines: string[] = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n '| ID | Type | Status | Raw Path |',\n '|---|---|---|---|',\n ];\n\n for (const b of BUCKET_ORDER) {\n if (b === 'topics') continue;\n lines.push('', `## ${BUCKET_LABELS[b]}`, '');\n if (b === bucket) {\n lines.push(`| [[${id}]] | ${type} | ${status} | ${relRawPath} |`);\n } else {\n lines.push('_No items._');\n }\n }\n lines.push('');\n\n void label; // suppress\n return lines.join('\\n');\n}\n\nfunction recompileSynthesis(wikiRoot: string, cwd: string, templateDir?: string): void {\n // Recompile all four synthesis pages\n // Gather current state from wiki pages to pass to recipes\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n let items: RawItem[] = [];\n if (fs.existsSync(deliveryRoot)) {\n try {\n items = scanRawItems(deliveryRoot, cwd);\n } catch {\n // If scan fails, pass empty state — synthesis pages will reflect empty\n }\n }\n\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n}\n","/**\n * STORY-002-08: cleargate wiki lint\n *\n * Scans .cleargate/wiki/ pages against their raw source files.\n * Enforcement mode (default): exits non-zero on any drift finding.\n * Suggest mode (--suggest): exits 0, prefixes flags with [advisory].\n *\n * Output format matches cleargate-wiki-lint subagent def lines 220-227:\n * <category>: <primary-path> -> <secondary-path-or-detail> (<optional context>)\n * Summary line always last:\n * lint: <OK|FAIL> (N pages checked, M findings)\n */\n\nimport * as path from 'node:path';\nimport { loadWikiPages } from '../wiki/load-wiki.js';\nimport type { GitRunner } from '../wiki/git-sha.js';\nimport {\n checkOrphan,\n checkRepoMismatch,\n checkStaleCommit,\n checkMissingIngest,\n checkBrokenBacklinks,\n checkInvalidatedCitations,\n checkExcludedPathIngested,\n checkPaginationNeeded,\n checkGateFailure,\n checkGateStaleness,\n checkIndexBudget,\n discoverPlainTextMentions,\n type LintFinding,\n} from '../wiki/lint-checks.js';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\n\nexport interface WikiLintOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to git-sha calls */\n gitRunner?: GitRunner;\n /** Lint mode: 'enforce' (default, exits 1 on findings) or 'suggest' (always exits 0) */\n mode?: 'enforce' | 'suggest';\n}\n\nexport async function wikiLintHandler(opts: WikiLintOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n // stderr seam is wired but lint outputs everything to stdout per subagent def\n opts.stderr;\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const mode = opts.mode ?? 'enforce';\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const repoRoot = cwd;\n\n // Step 1: load all wiki pages (single discovery pass)\n let pages = loadWikiPages(wikiRoot);\n\n const findings: LintFinding[] = [];\n\n // Meta-check: pagination-needed (fires on bucket > 50 entries)\n const paginationFindings = checkPaginationNeeded(pages);\n findings.push(...paginationFindings);\n\n // Step 2: per-page checks (O(n))\n for (const page of pages) {\n // (a) orphan\n const orphan = checkOrphan(page, repoRoot);\n if (orphan) findings.push(orphan);\n\n // (b) repo-mismatch\n const repoMismatch = checkRepoMismatch(page, repoRoot);\n if (repoMismatch) findings.push(repoMismatch);\n\n // (c) stale-commit\n const staleCommit = checkStaleCommit(page, repoRoot, gitRunner);\n if (staleCommit) findings.push(staleCommit);\n\n // (d) missing-ingest\n const missingIngest = checkMissingIngest(page, repoRoot);\n if (missingIngest) findings.push(missingIngest);\n\n // (e) excluded-path-ingested\n const excludedPath = checkExcludedPathIngested(page, repoRoot);\n if (excludedPath) findings.push(excludedPath);\n\n // (f) gate-failure — enforcing for Epic/Story/CR/Bug\n const gateFail = checkGateFailure(page, repoRoot);\n if (gateFail) findings.push(gateFail);\n\n // (g) gate-stale — applies to ALL types\n const gateStale = checkGateStaleness(page, repoRoot);\n if (gateStale) findings.push(gateStale);\n }\n\n // Step 3: single index cross-check pass — broken backlinks\n const backlinkFindings = checkBrokenBacklinks(pages, repoRoot);\n findings.push(...backlinkFindings);\n\n // Step 4: topic-page invalidated-citation check\n const citationFindings = checkInvalidatedCitations(pages, repoRoot);\n findings.push(...citationFindings);\n\n // Step 4.5: index token-budget check (STORY-015-03)\n const wikiConfig = loadWikiConfig(cwd);\n const indexBudget = checkIndexBudget(repoRoot, wikiConfig.wiki.index_token_ceiling);\n\n // In suggest mode, always emit the usage line unconditionally (before advisory loop)\n if (mode === 'suggest' && indexBudget.tokens !== undefined) {\n const tokens = indexBudget.tokens;\n const ceiling = indexBudget.ceiling!;\n const pct = Math.round((tokens / ceiling) * 100);\n stdout(`index token usage: ${tokens} / ${ceiling} (${pct}%)\\n`);\n }\n\n // In enforce mode, push finding into findings array (will be emitted below)\n if (mode === 'enforce' && indexBudget.finding !== null) {\n findings.push(indexBudget.finding);\n }\n\n const pageCount = pages.length;\n const findingCount = findings.length;\n\n // Step 5: emit results\n if (mode === 'suggest') {\n // Advisory mode: prefix flags with [advisory] + do Karpathy discovery pass\n for (const finding of findings) {\n stdout(`[advisory] ${finding.line}\\n`);\n }\n\n // Karpathy discovery pass\n const suggestions = discoverPlainTextMentions(pages, repoRoot);\n for (const suggestion of suggestions) {\n stdout(`${suggestion}\\n`);\n }\n\n stdout(`lint: OK (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(0);\n return;\n }\n\n // Enforce mode\n for (const finding of findings) {\n stdout(`${finding.line}\\n`);\n }\n\n if (findingCount > 0) {\n stdout(`lint: FAIL (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(1);\n } else {\n stdout(`lint: OK (${pageCount} pages checked, 0 findings)\\n`);\n exit(0);\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport type { WikiPage } from './page-schema.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface LoadedWikiPage {\n absPath: string;\n page: WikiPage;\n body: string;\n}\n\nconst BUCKET_DIRS = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'];\n\n/**\n * Glob+Read every wiki page from wikiRoot once.\n * Shared by wiki-lint and wiki-query (STORY-002-07 and STORY-002-08).\n */\nexport function loadWikiPages(wikiRoot: string): LoadedWikiPage[] {\n const results: LoadedWikiPage[] = [];\n\n for (const bucket of BUCKET_DIRS) {\n const dir = path.join(wikiRoot, bucket);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { encoding: 'utf8' }) as string[];\n for (const filename of entries) {\n if (!filename.endsWith('.md')) continue;\n const absPath = path.join(dir, filename);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n continue;\n }\n\n const page: WikiPage = {\n type: (fm['type'] as WikiPageType) ?? 'epic',\n id: String(fm['id'] ?? ''),\n parent: String(fm['parent'] ?? ''),\n children: Array.isArray(fm['children'])\n ? (fm['children'] as unknown[]).map(String)\n : [],\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: String(fm['raw_path'] ?? ''),\n last_ingest: String(fm['last_ingest'] ?? ''),\n last_ingest_commit: String(fm['last_ingest_commit'] ?? ''),\n repo: (fm['repo'] as RepoTag) ?? 'planning',\n };\n\n results.push({ absPath, page, body });\n }\n }\n\n return results;\n}\n","/**\n * Pure-function lint check implementations.\n * One exported function per category so they can be unit-tested in isolation.\n * Categories must use these exact strings (subagent + CLI agree):\n * orphan, repo-mismatch, stale-commit, missing-ingest, broken-backlink,\n * invalidated-citation, excluded-path-ingested, pagination-needed, index-budget\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport type { GitRunner } from './git-sha.js';\nimport type { LoadedWikiPage } from './load-wiki.js';\nimport { deriveRepo } from './derive-repo.js';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { detectWorkItemTypeFromFm } from '../lib/work-item-type.js';\n\nexport interface LintFinding {\n category: string;\n line: string;\n}\n\n/** §10.3 excluded directories — wiki pages must not exist for raw files under these. */\nconst EXCLUDED_DIRS = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/** Maximum entries per bucket before pagination-needed fires. */\nconst MAX_BUCKET_ENTRIES = 50;\n\n/**\n * Check (a): Orphan — wiki page's raw_path doesn't exist on disk.\n * Skips pages whose raw_path is under an excluded directory (caught by check 7).\n */\nexport function checkOrphan(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n // Don't flag orphan for excluded paths — that's caught by excluded-path-ingested\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'orphan',\n line: `orphan: ${relPage} -> missing ${rawPath} (raw missing)`,\n };\n }\n return null;\n}\n\n/**\n * Check (b): repo-mismatch — stored repo field doesn't match raw_path prefix.\n */\nexport function checkRepoMismatch(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n let derivedRepo: string;\n try {\n derivedRepo = deriveRepo(rawPath);\n } catch {\n return null; // Can't derive — not our responsibility to flag here\n }\n\n if (page.page.repo !== derivedRepo) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'repo-mismatch',\n line: `repo-mismatch: ${relPage} declares repo:${page.page.repo} but raw_path implies repo:${derivedRepo}`,\n };\n }\n return null;\n}\n\n/**\n * Check (c): stale-commit — stored last_ingest_commit differs from current git HEAD SHA.\n * Empty stored SHA is tolerated (untracked file).\n */\nexport function checkStaleCommit(\n page: LoadedWikiPage,\n repoRoot: string,\n gitRunner?: GitRunner,\n): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const storedSha = page.page.last_ingest_commit;\n // If stored SHA is empty, file was untracked at ingest time — tolerate\n if (!storedSha) return null;\n\n // Run git log -1\n let currentSha: string;\n if (gitRunner) {\n currentSha = gitRunner('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n } else {\n const result = spawnSync('git', ['log', '-1', '--format=%H', '--', rawPath], {\n encoding: 'utf8',\n cwd: repoRoot,\n });\n currentSha = (result.stdout ?? '').trim();\n }\n\n if (!currentSha) return null; // untracked now — don't flag\n if (storedSha !== currentSha) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'stale-commit',\n line: `stale-commit: ${relPage} at ${storedSha}, current ${currentSha}`,\n };\n }\n return null;\n}\n\n/**\n * Check (d): missing-ingest — raw file mtime newer than wiki page mtime.\n */\nexport function checkMissingIngest(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null; // orphan check handles this\n\n const rawStat = fs.statSync(absRaw);\n const pageStat = fs.statSync(page.absPath);\n\n // Use > 2s gap to avoid HFS+ mtime resolution flakiness (per blueprint gotcha)\n const rawMtimeMs = rawStat.mtimeMs;\n const pageMtimeMs = pageStat.mtimeMs;\n\n if (rawMtimeMs - pageMtimeMs > 2000) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n const rawMtime = rawStat.mtime.toISOString();\n const pageMtime = pageStat.mtime.toISOString();\n return {\n category: 'missing-ingest',\n line: `missing-ingest: ${rawPath} newer than ${relPage} (raw mtime: ${rawMtime}, page mtime: ${pageMtime})`,\n };\n }\n return null;\n}\n\n/**\n * Check (backlink): broken backlink — child's parent exists but parent doesn't list the child.\n * O(n) linear scan over collected parent declarations.\n */\nexport function checkBrokenBacklinks(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n // Build a map of id -> page for O(1) lookup\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n for (const childPage of pages) {\n const parentRef = childPage.page.parent;\n if (!parentRef) continue;\n\n // Extract parent ID from \"[[PARENT-ID]]\" form\n const match = parentRef.match(/\\[\\[(.+?)\\]\\]/);\n if (!match) continue;\n const parentId = match[1];\n\n const parentPage = byId.get(parentId);\n if (!parentPage) {\n // Parent page missing — flag\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n continue;\n }\n\n // Check that parent's children list contains [[childId]]\n const childId = childPage.page.id;\n const childRef = `[[${childId}]]`;\n const parentHasChild = parentPage.page.children.some(\n (c) => c === childRef || c === childId,\n );\n\n if (!parentHasChild) {\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n }\n }\n\n return findings;\n}\n\n/**\n * Check: invalidated-citation — topic page cites a cancelled or missing item.\n */\nexport function checkInvalidatedCitations(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n const topicPages = pages.filter((p) => p.page.type === 'topic');\n\n for (const topicPage of topicPages) {\n // WikiPage doesn't have a cites field — re-parse raw frontmatter to get it.\n const relTopic = path.relative(wikiRoot, topicPage.absPath).replace(/\\\\/g, '/');\n\n let citesList: string[] = [];\n try {\n const raw = fs.readFileSync(topicPage.absPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n const rawCites = fm['cites'];\n if (Array.isArray(rawCites)) {\n citesList = (rawCites as unknown[]).map(String);\n }\n } catch {\n continue;\n }\n\n for (const cite of citesList) {\n const match = cite.match(/\\[\\[(.+?)\\]\\]/);\n const id = match ? match[1] : cite;\n\n const citedPage = byId.get(id);\n if (!citedPage) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (missing)`,\n });\n continue;\n }\n\n const status = citedPage.page.status;\n if (status === 'cancelled' || status.toLowerCase().includes('cancelled')) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (cancelled)`,\n });\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Check: excluded-path-ingested — wiki page exists for a raw file under an excluded directory.\n */\nexport function checkExcludedPathIngested(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'excluded-path-ingested',\n line: `excluded-path-ingested: ${relPage} (raw_path ${rawPath} is under an excluded directory)`,\n };\n }\n return null;\n}\n\n/**\n * Meta-check: pagination-needed — fires if any bucket has more than 50 entries.\n */\nexport function checkPaginationNeeded(pages: LoadedWikiPage[]): LintFinding[] {\n // Count by bucket (derived from absPath directory name)\n const bucketCounts = new Map<string, number>();\n for (const p of pages) {\n const bucket = path.basename(path.dirname(p.absPath));\n bucketCounts.set(bucket, (bucketCounts.get(bucket) ?? 0) + 1);\n }\n\n const findings: LintFinding[] = [];\n for (const [bucket, count] of bucketCounts) {\n if (count > MAX_BUCKET_ENTRIES) {\n findings.push({\n category: 'pagination-needed',\n line: `pagination-needed: ${bucket} (${count} entries, max ${MAX_BUCKET_ENTRIES} per bucket)`,\n });\n }\n }\n return findings;\n}\n\n/** Work-item types that trigger enforcing gate-failure lint (not advisory). */\nconst ENFORCING_TYPES = new Set(['epic', 'story', 'cr', 'bug']);\n\n/** Status values considered \"ready\" (🟢-candidate). */\nconst READY_STATUSES = new Set(['Ready', 'Active']);\n\n/**\n * Parse the cached_gate_result from a raw frontmatter record.\n * parseFrontmatter stores nested YAML objects as opaque strings starting with '{'.\n * This helper resolves either form into a plain object or null.\n */\nfunction parseCachedGateResult(\n raw: unknown,\n): { pass: unknown; failing_criteria: unknown; last_gate_check: unknown } | null {\n if (!raw || raw === null) return null;\n\n // Opaque string form — inline flow YAML written by writeCachedGate\n if (typeof raw === 'string') {\n if (!raw.startsWith('{')) return null;\n try {\n const parsed = yaml.load(raw);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n } catch {\n return null;\n }\n }\n\n // Already-parsed object form (future-proofing)\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n const p = raw as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n }\n\n return null;\n}\n\n/**\n * Check: gate-failure — 🟢-candidate Epic/Story/CR/Bug with cached_gate_result.pass === false.\n * Reads the raw work-item file (not the wiki page).\n * Proposal / Sprint / Initiative are advisory only → returns null (no enforcing block).\n */\nexport function checkGateFailure(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr || cgr.pass !== false) return null;\n\n // Check if the work-item type is enforcing\n const wiType = detectWorkItemTypeFromFm(rawFm);\n if (!wiType || !ENFORCING_TYPES.has(wiType)) return null;\n\n // Check if this is a 🟢-candidate (status Ready/Active or ambiguity 🟢 Low)\n const status = String(rawFm['status'] ?? '');\n const ambiguity = String(rawFm['ambiguity'] ?? '');\n const isReadyCandidate = READY_STATUSES.has(status) || ambiguity === '🟢 Low';\n if (!isReadyCandidate) return null;\n\n // Collect failing criteria IDs\n const failingCriteria = cgr.failing_criteria;\n const criteriaIds: string[] = [];\n if (Array.isArray(failingCriteria)) {\n for (const criterion of failingCriteria as unknown[]) {\n if (criterion && typeof criterion === 'object' && 'id' in (criterion as object)) {\n criteriaIds.push(String((criterion as Record<string, unknown>)['id']));\n } else if (typeof criterion === 'string') {\n criteriaIds.push(criterion);\n }\n }\n }\n\n const criteriaStr = criteriaIds.length > 0 ? criteriaIds.join(', ') : 'unknown';\n return {\n category: 'gate-failure',\n line: `gate-failure: ${rawPath} failed criteria: ${criteriaStr}`,\n };\n}\n\n/**\n * Check: gate-stale — cached_gate_result.last_gate_check < updated_at (ISO-8601 lexical compare).\n * Applies to ALL work-item types (including Proposal/Sprint/Initiative).\n * Reads the raw work-item file (not the wiki page).\n */\nexport function checkGateStaleness(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr) return null;\n\n const lastGateCheck = cgr.last_gate_check;\n if (!lastGateCheck || lastGateCheck === null) return null;\n\n const updatedAt = rawFm['updated_at'];\n if (!updatedAt) return null;\n\n const lastCheckStr = String(lastGateCheck);\n const updatedAtStr = String(updatedAt);\n\n // ISO-8601 lexical compare: if last_gate_check < updated_at → stale\n if (lastCheckStr < updatedAtStr) {\n return {\n category: 'gate-stale',\n line: `gate-stale: ${rawPath} last_gate_check=${lastCheckStr} < updated_at=${updatedAtStr}`,\n };\n }\n return null;\n}\n\n/**\n * Karpathy discovery pass: scan page bodies for plain-text ID mentions\n * (not wrapped in [[]]). Emit suggest lines.\n */\nexport function discoverPlainTextMentions(pages: LoadedWikiPage[], repoRoot: string): string[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, boolean>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, true);\n }\n\n const suggestions: string[] = [];\n const ID_PATTERN = /\\b((?:EPIC|STORY|SPRINT|PROPOSAL|CR|BUG)-[\\w-]+)\\b/g;\n const LINK_PATTERN = /\\[\\[[\\w-]+\\]\\]/g;\n\n for (const page of pages) {\n const relPage = path.relative(wikiRoot, page.absPath).replace(/\\\\/g, '/');\n // Find all [[...]] wrapped references to exclude\n const wrappedRefs = new Set<string>();\n for (const m of page.body.matchAll(LINK_PATTERN)) {\n const inner = m[0].slice(2, -2);\n wrappedRefs.add(inner);\n }\n\n // Find plain-text ID mentions\n for (const m of page.body.matchAll(ID_PATTERN)) {\n const mentionedId = m[1];\n if (!byId.has(mentionedId)) continue;\n if (wrappedRefs.has(mentionedId)) continue;\n if (mentionedId === page.page.id) continue; // self-reference\n suggestions.push(`suggest: ${relPage} mentions ${mentionedId} in plain text, consider [[${mentionedId}]] wrap`);\n }\n }\n\n return suggestions;\n}\n\n/**\n * Check: index-budget — wiki/index.md approximate token count exceeds configured ceiling.\n * Token heuristic: Math.round(bytes / 4). Returns null when index.md is absent.\n * This check is a structural check, not a per-page check, so it takes repoRoot directly.\n */\nexport interface IndexBudgetResult {\n /** Present when tokens > ceiling. Push this into findings array. */\n finding: LintFinding | null;\n /** Always populated when index.md exists; undefined when file absent. */\n tokens?: number;\n ceiling?: number;\n}\n\nexport function checkIndexBudget(repoRoot: string, indexTokenCeiling: number): IndexBudgetResult {\n const indexPath = path.join(repoRoot, '.cleargate', 'wiki', 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n return { finding: null };\n }\n\n const bytes = fs.statSync(indexPath).size;\n const tokens = Math.round(bytes / 4);\n const ceiling = indexTokenCeiling;\n\n if (tokens > ceiling) {\n return {\n finding: {\n category: 'index-budget',\n line: `index-budget: wiki/index.md exceeds token ceiling: ${tokens} > ${ceiling}. Shard or prune (see EPIC-015).`,\n },\n tokens,\n ceiling,\n };\n }\n\n return { finding: null, tokens, ceiling };\n}\n","/**\n * work-item-type.ts — Shared work-item type detection utility.\n *\n * STORY-008-03: extracted here for STORY-008-05 to import without duplication.\n * Maps frontmatter ID keys and filename patterns to canonical work-item types.\n */\n\nexport type WorkItemType = 'story' | 'epic' | 'proposal' | 'cr' | 'bug';\n\n/**\n * Frontmatter key → work-item type mapping.\n * Keys are checked in order; first match wins.\n */\nconst FM_KEY_MAP: Array<{ key: string; type: WorkItemType }> = [\n { key: 'story_id', type: 'story' },\n { key: 'epic_id', type: 'epic' },\n { key: 'proposal_id', type: 'proposal' },\n { key: 'cr_id', type: 'cr' },\n { key: 'bug_id', type: 'bug' },\n];\n\n/**\n * Filename / ID prefix → work-item type mapping.\n */\nconst PREFIX_MAP: Array<{ prefix: string; type: WorkItemType }> = [\n { prefix: 'STORY-', type: 'story' },\n { prefix: 'EPIC-', type: 'epic' },\n { prefix: 'PROPOSAL-', type: 'proposal' },\n { prefix: 'CR-', type: 'cr' },\n { prefix: 'BUG-', type: 'bug' },\n];\n\n/**\n * Detect the work-item type from a parsed frontmatter record.\n * Returns null if no recognized ID key is found.\n */\nexport function detectWorkItemTypeFromFm(\n fm: Record<string, unknown>,\n): WorkItemType | null {\n for (const { key, type } of FM_KEY_MAP) {\n if (fm[key] !== undefined && fm[key] !== null && fm[key] !== '') {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Detect the work-item type from an ID string or file path.\n * Matches against the uppercase prefix (STORY-, EPIC-, etc.).\n * Returns null if no prefix matches.\n */\nexport function detectWorkItemType(idOrPath: string): WorkItemType | null {\n const upper = idOrPath.toUpperCase();\n // Strip leading directory path components\n const basename = upper.split('/').pop() ?? upper;\n for (const { prefix, type } of PREFIX_MAP) {\n if (basename.includes(prefix)) {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Canonical transitions per work-item type.\n * Epic has 2; all others have 1.\n */\nexport const WORK_ITEM_TRANSITIONS: Record<WorkItemType, string[]> = {\n proposal: ['ready-for-decomposition'],\n epic: ['ready-for-decomposition', 'ready-for-coding'],\n story: ['ready-for-execution'],\n cr: ['ready-to-apply'],\n bug: ['ready-for-fix'],\n};\n","/**\n * STORY-015-03: Per-repo wiki configuration loader.\n * STORY-018-03: Extended to include `gates` map.\n *\n * Reads `.cleargate/config.yml` from the repo root.\n * Single responsibility: surface `wiki.index_token_ceiling` and `gates` map.\n * Missing file → defaults. Malformed YAML → throws with file path in message.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\n\nexport interface GatesConfig {\n precommit?: string;\n test?: string;\n typecheck?: string;\n lint?: string;\n}\n\nexport interface WikiConfig {\n wiki: {\n index_token_ceiling: number;\n };\n gates: GatesConfig;\n}\n\nconst DEFAULT_INDEX_TOKEN_CEILING = 8000;\n\n/**\n * Load per-repo wiki config from `<repoRoot>/.cleargate/config.yml`.\n * Returns defaults when file is absent.\n * Throws a descriptive error on malformed YAML.\n */\nexport function loadWikiConfig(repoRoot: string): WikiConfig {\n const configPath = path.join(repoRoot, '.cleargate', 'config.yml');\n\n if (!fs.existsSync(configPath)) {\n return { wiki: { index_token_ceiling: DEFAULT_INDEX_TOKEN_CEILING }, gates: {} };\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch (err) {\n throw new Error(`Failed to read ${configPath}: ${String(err)}`);\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(raw, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`Malformed YAML in ${configPath}: ${String(err)}`);\n }\n\n const ceiling = extractCeiling(parsed);\n const gates = extractGates(parsed);\n\n return {\n wiki: {\n index_token_ceiling: ceiling,\n },\n gates,\n };\n}\n\nfunction extractCeiling(parsed: unknown): number {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const root = parsed as Record<string, unknown>;\n const wiki = root['wiki'];\n\n if (wiki === null || wiki === undefined || typeof wiki !== 'object' || Array.isArray(wiki)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const wikiObj = wiki as Record<string, unknown>;\n const ceiling = wikiObj['index_token_ceiling'];\n\n if (typeof ceiling === 'number' && Number.isFinite(ceiling) && ceiling > 0) {\n return ceiling;\n }\n\n return DEFAULT_INDEX_TOKEN_CEILING;\n}\n\nfunction extractGates(parsed: unknown): GatesConfig {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {};\n }\n\n const root = parsed as Record<string, unknown>;\n const gates = root['gates'];\n\n if (gates === null || gates === undefined || typeof gates !== 'object' || Array.isArray(gates)) {\n return {};\n }\n\n const gatesObj = gates as Record<string, unknown>;\n const result: GatesConfig = {};\n\n if (typeof gatesObj['precommit'] === 'string') result.precommit = gatesObj['precommit'];\n if (typeof gatesObj['test'] === 'string') result.test = gatesObj['test'];\n if (typeof gatesObj['typecheck'] === 'string') result.typecheck = gatesObj['typecheck'];\n if (typeof gatesObj['lint'] === 'string') result.lint = gatesObj['lint'];\n\n return result;\n}\n","/**\n * STORY-002-08: cleargate wiki query [--persist]\n *\n * Read-only by default: grep .cleargate/wiki/index.md for query terms,\n * return matching [[ID]] list with one-line excerpts to stdout. Exit 0.\n *\n * --persist: compute slug, write wiki/topics/<slug>.md with frontmatter\n * type: topic, id, created_by, created_at, cites. Append to wiki/index.md\n * ## Topics section.\n *\n * NOTE: CLI synthesis is grep-and-list. For NL synthesis with the\n * cleargate-wiki-query subagent, invoke from a Claude Code session.\n * This diverges from PROPOSAL-002 §2.2 intentionally for testability and\n * offline/scripted use.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface WikiQueryOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: frozen ISO timestamp (defaults to new Date().toISOString()) */\n now?: () => string;\n /** The query string */\n query: string;\n /** If true, write result as a topic page under wiki/topics/ */\n persist?: boolean;\n}\n\n/**\n * Compute a slug from a query string.\n * Lowercase, replace spaces and punctuation with hyphens,\n * strip consecutive hyphens, truncate to ≤40 chars.\n */\nexport function computeSlug(query: string): string {\n return query\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-') // non-alphanumeric → hyphen\n .replace(/^-+|-+$/g, '') // strip leading/trailing hyphens\n .replace(/-{2,}/g, '-') // collapse consecutive hyphens\n .slice(0, 40)\n .replace(/-+$/, ''); // strip trailing hyphens after truncation\n}\n\n/**\n * Parse index.md and extract matching IDs for the given query terms.\n * Returns array of { id, line } matching lines.\n */\nfunction searchIndex(indexContent: string, query: string): Array<{ id: string; excerpt: string }> {\n const terms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 0);\n\n const results: Array<{ id: string; excerpt: string }> = [];\n const seenIds = new Set<string>();\n\n for (const line of indexContent.split('\\n')) {\n const lower = line.toLowerCase();\n const matchesAll = terms.every((term) => lower.includes(term));\n if (!matchesAll) continue;\n\n // Extract [[ID]] from line\n const match = line.match(/\\[\\[([^\\]]+)\\]\\]/);\n if (!match) continue;\n const id = match[1];\n if (seenIds.has(id)) continue;\n seenIds.add(id);\n\n results.push({ id, excerpt: line.trim() });\n }\n\n return results;\n}\n\nexport async function wikiQueryHandler(opts: WikiQueryOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const now = opts.now ?? (() => new Date().toISOString());\n const query = opts.query;\n const persist = opts.persist ?? false;\n\n void stderr; // suppress unused warning\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const indexPath = path.join(wikiRoot, 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n stdout(`wiki query: no index.md found at ${indexPath}\\n`);\n stdout(`Run \\`cleargate wiki build\\` first.\\n`);\n exit(1);\n return;\n }\n\n const indexContent = fs.readFileSync(indexPath, 'utf8');\n const matches = searchIndex(indexContent, query);\n\n if (matches.length === 0) {\n stdout(`wiki query: no matches for \"${query}\"\\n`);\n exit(0);\n return;\n }\n\n // Build output body\n const bodyLines: string[] = [\n `# Query: ${query}`,\n '',\n `Found ${matches.length} match(es):`,\n '',\n ];\n\n for (const { id, excerpt } of matches) {\n bodyLines.push(`- [[${id}]] — ${excerpt}`);\n }\n bodyLines.push('');\n\n const body = bodyLines.join('\\n');\n\n // Output to stdout (read-only mode: always output to stdout)\n stdout(body);\n\n if (!persist) {\n exit(0);\n return;\n }\n\n // Persist mode: write topic page\n const slug = computeSlug(query);\n const topicsDir = path.join(wikiRoot, 'topics');\n fs.mkdirSync(topicsDir, { recursive: true });\n\n const citesArray = matches.map(({ id }) => `\"[[${id}]]\"`);\n const createdAt = now();\n\n // Build topic page frontmatter\n const frontmatter = [\n '---',\n `type: topic`,\n `id: \"${slug}\"`,\n `created_by: \"cleargate-wiki-query\"`,\n `created_at: \"${createdAt}\"`,\n `cites: [${citesArray.join(', ')}]`,\n '---',\n ].join('\\n');\n\n const topicContent = `${frontmatter}\\n\\n${body}`;\n const topicPath = path.join(topicsDir, `${slug}.md`);\n\n // Overwrite if exists (slug collision → overwrite per subagent def line 136)\n fs.writeFileSync(topicPath, topicContent, 'utf8');\n\n // Update wiki/index.md Topics section\n updateIndexTopicsSection(indexPath, slug, query, createdAt);\n\n exit(0);\n}\n\n/**\n * Append one row to the ## Topics section of wiki/index.md.\n * Creates the section header if absent.\n */\nfunction updateIndexTopicsSection(\n indexPath: string,\n slug: string,\n query: string,\n createdAt: string,\n): void {\n let content = fs.readFileSync(indexPath, 'utf8');\n\n const row = `| ${slug} | ${query} | ${createdAt} |`;\n\n if (content.includes('## Topics')) {\n // Append after the last line of the Topics section (end of file or before next ##)\n // Find the Topics section and append the row at the end\n const topicsIdx = content.indexOf('## Topics');\n const afterTopics = content.slice(topicsIdx);\n\n // Find the next ## section or end of file\n const nextSectionMatch = afterTopics.slice('## Topics'.length).match(/\\n## /);\n if (nextSectionMatch && nextSectionMatch.index !== undefined) {\n const insertPos = topicsIdx + '## Topics'.length + nextSectionMatch.index;\n content = content.slice(0, insertPos) + `\\n${row}` + content.slice(insertPos);\n } else {\n // Topics is the last section — append at end\n content = content.trimEnd() + `\\n${row}\\n`;\n }\n } else {\n // Create Topics section at end of file\n content = content.trimEnd() + `\\n\\n## Topics\\n\\n${row}\\n`;\n }\n\n fs.writeFileSync(indexPath, content, 'utf8');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline';\nimport { scanRawItems } from '../wiki/scan.js';\n\nexport interface WikiAuditStatusOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp (not used for output; reserved for future use) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Apply safe status corrections to frontmatter */\n fix?: boolean;\n /** Required together with --fix to confirm writes in non-interactive mode */\n yes?: boolean;\n /** Suppress diff output */\n quiet?: boolean;\n /** Test seam: override isTTY detection */\n isTTY?: boolean;\n /** Test seam: override readline for confirmation prompt */\n promptReader?: () => Promise<string>;\n}\n\nconst TERMINAL = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\ninterface DriftItem {\n id: string;\n rawPath: string;\n absPath: string;\n bucket: string;\n currentStatus: string;\n rule: 'A' | 'B' | 'C';\n /** Suggested new status (undefined for Rule B — file move needed) */\n suggestedStatus?: string;\n /** Human-readable description for the report */\n description: string;\n}\n\nexport async function wikiAuditStatusHandler(opts: WikiAuditStatusOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => { process.stdout.write(s); });\n const stderr = opts.stderr ?? ((s: string) => { process.stderr.write(s); });\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`audit-status: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Build a lookup of epic id → child stories (with terminal status check)\n const storiesByEpic = new Map<string, typeof items>();\n for (const item of items) {\n if (item.bucket !== 'stories') continue;\n const epicRef = String(item.fm['parent_epic_ref'] ?? '').replace(/^\\[\\[|\\]\\]$/g, '');\n if (!epicRef) continue;\n if (!storiesByEpic.has(epicRef)) storiesByEpic.set(epicRef, []);\n storiesByEpic.get(epicRef)!.push(item);\n }\n\n const driftItems: DriftItem[] = [];\n\n for (const item of items) {\n const currentStatus = String(item.fm['status'] ?? '');\n const isTerminal = TERMINAL.has(currentStatus);\n const inArchive = item.rawPath.includes('/archive/');\n const inPendingSync = item.rawPath.includes('/pending-sync/');\n\n // Rule A: in archive/ but status is non-terminal\n if (inArchive && !isTerminal) {\n // Determine suggested fix: Completed if all child stories terminal (epics/sprints), else Abandoned\n let suggestedStatus = 'Abandoned';\n if (item.bucket === 'epics' || item.bucket === 'sprints') {\n const childStories = storiesByEpic.get(item.id) ?? [];\n if (childStories.length > 0 && childStories.every((s) => TERMINAL.has(String(s.fm['status'] ?? '')))) {\n suggestedStatus = 'Completed';\n }\n }\n\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'A',\n suggestedStatus,\n description: `Rule A — archived with non-terminal status '${currentStatus}'`,\n });\n }\n\n // Rule B: in pending-sync/ but status is terminal\n if (inPendingSync && isTerminal) {\n const archivePath = item.rawPath.replace('/pending-sync/', '/archive/');\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'B',\n // No suggestedStatus — Rule B requires file move, not status change\n description: `Rule B — pending-sync with terminal status '${currentStatus}'; run: git mv ${item.rawPath} ${archivePath.replace(/\\/[^/]+$/, '/')}`,\n });\n }\n\n // Rule C: sprint file, non-terminal status, all children of its epics are terminal\n if (item.bucket === 'sprints' && !isTerminal) {\n const epicRefs = item.fm['epics'];\n if (!epicRefs) continue; // No epics key → Rule C does not fire\n const epicsArr = Array.isArray(epicRefs) ? epicRefs : [epicRefs];\n if (epicsArr.length === 0) continue;\n\n let totalChildren = 0;\n let terminalChildren = 0;\n\n for (const epicRef of epicsArr) {\n const epicId = String(epicRef).replace(/^\\[\\[|\\]\\]$/g, '');\n const children = storiesByEpic.get(epicId) ?? [];\n totalChildren += children.length;\n terminalChildren += children.filter((s) => TERMINAL.has(String(s.fm['status'] ?? ''))).length;\n }\n\n if (totalChildren > 0 && totalChildren === terminalChildren) {\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'C',\n suggestedStatus: 'Completed',\n description: `Rule C — ${terminalChildren}/${totalChildren} child stories terminal; suggest Completed`,\n });\n }\n }\n }\n\n // Print report\n if (driftItems.length === 0) {\n stdout('audit-status: clean (0 drift)\\n');\n exit(0);\n return;\n }\n\n for (const d of driftItems) {\n if (d.rule === 'B') {\n // Rule B: emit git mv hint\n const archivePath = d.rawPath.replace('/pending-sync/', '/archive/');\n const archiveDir = archivePath.replace(/\\/[^/]+$/, '/');\n stdout(`${d.id}: ${d.description}\\n`);\n stdout(` git mv ${d.rawPath} ${archiveDir}\\n`);\n } else {\n stdout(`${d.id}: ${d.description}\\n`);\n }\n }\n\n if (!opts.fix) {\n exit(1);\n return;\n }\n\n // --fix mode\n const fixable = driftItems.filter((d) => d.rule !== 'B' && d.suggestedStatus !== undefined);\n const ruleB = driftItems.filter((d) => d.rule === 'B');\n\n if (ruleB.length > 0) {\n for (const d of ruleB) {\n stdout(` (skipping ${d.id}: Rule B requires manual file move, not a status change)\\n`);\n }\n }\n\n if (fixable.length === 0) {\n stdout('audit-status: no auto-fixable items (Rule B items require manual git mv)\\n');\n exit(0);\n return;\n }\n\n // Confirmation\n if (!opts.yes) {\n if (!isTTY) {\n stderr('audit-status: --fix requires --yes in non-interactive mode\\n');\n exit(2);\n return;\n }\n\n // TTY: prompt\n let answer: string;\n if (opts.promptReader) {\n answer = await opts.promptReader();\n } else {\n answer = await new Promise<string>((resolve) => {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n rl.question(`apply ${fixable.length} changes? [y/N] `, (ans) => {\n rl.close();\n resolve(ans);\n });\n });\n }\n\n if (!answer.trim().toLowerCase().startsWith('y')) {\n stdout('aborted\\n');\n exit(2);\n return;\n }\n }\n\n // Apply fixes via line-surgery regex (do NOT round-trip through parseFrontmatter)\n for (const d of fixable) {\n const rawText = fs.readFileSync(d.absPath, 'utf8');\n const updated = applyStatusFix(rawText, d.suggestedStatus!);\n\n if (!opts.quiet) {\n stdout(`--- ${d.rawPath}\\n`);\n stdout(`+++ ${d.rawPath}\\n`);\n stdout(`@@ status change @@\\n`);\n // Show the old and new status line\n const oldLine = rawText.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n const newLine = updated.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n stdout(`-${oldLine}\\n`);\n stdout(`+${newLine}\\n`);\n }\n\n fs.writeFileSync(d.absPath, updated, 'utf8');\n }\n\n stdout(`audit-status: applied ${fixable.length} fix(es)\\n`);\n exit(0);\n}\n\n/**\n * Replace the first `status:` line inside the first `---` YAML front-matter block.\n * Everything else is byte-identical (no round-trip through parseFrontmatter).\n */\nfunction applyStatusFix(rawText: string, newStatus: string): string {\n // Find the closing --- of the frontmatter\n const lines = rawText.split('\\n');\n if (lines[0] !== '---') return rawText; // no frontmatter — leave untouched\n\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return rawText; // malformed — leave untouched\n\n // Replace only the first `status:` line within the frontmatter block\n let replaced = false;\n for (let i = 1; i < closeIdx; i++) {\n if (!replaced && /^status:/.test(lines[i])) {\n lines[i] = `status: \"${newStatus}\"`;\n replaced = true;\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * doctor.ts — STORY-009-04 + STORY-008-06\n *\n * `cleargate doctor` base command + `--check-scaffold` / `--session-start` / `--pricing` modes.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { computeUsd, type DraftTokensInput } from '../lib/pricing.js';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n readDriftState,\n type DriftMap,\n type DriftMapEntry,\n type DriftState,\n} from '../lib/manifest.js';\nimport { shortHash } from '../lib/sha256.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface DoctorCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override the package root for loadPackageManifest (test seam). */\n packageRoot?: string;\n}\n\n/**\n * STORY-014-01: Accumulator passed by reference through all mode handlers.\n * Top-level doctorHandler reads at the end and exits per §3.2 pseudocode:\n * configError → exit(2), blocker → exit(1), else → exit(0).\n */\nexport interface DoctorOutcome {\n configError: boolean;\n blocker: boolean;\n}\n\n/**\n * Flags for `cleargate doctor`.\n *\n * Reserved keys for 008-06 (M3): sessionStart, pricing.\n * All flags are mutually exclusive — selectMode throws when >1 is set.\n */\nexport interface DoctorFlags {\n checkScaffold?: boolean;\n /** Hidden flag: used by the M3 session-start hook; enables daily throttle. */\n sessionStartMode?: boolean;\n verbose?: boolean;\n /** --session-start: emit blocked pending-sync items summary */\n sessionStart?: boolean;\n /** --pricing: compute USD estimate for a work item */\n pricing?: boolean;\n /** File path passed to --pricing <file> */\n pricingFile?: string;\n /** CR-008: --can-edit <file>: exits 0 if allowed, 1 if would-block */\n canEdit?: boolean;\n /** CR-008: the file path argument for --can-edit */\n canEditFile?: string;\n}\n\nexport type DoctorMode = 'check-scaffold' | 'session-start' | 'pricing' | 'hook-health' | 'can-edit';\n\n// ─── Mode dispatcher ──────────────────────────────────────────────────────────\n\n/**\n * Determine which doctor mode to run based on flags.\n *\n * Throws when multiple mutually-exclusive mode flags are set.\n * Returns 'hook-health' when no mode flag is set (default).\n *\n * Exported so STORY-008-06 can add cases without re-editing the switch.\n */\nexport function selectMode(flags: DoctorFlags): DoctorMode {\n const modes: DoctorMode[] = [];\n if (flags.checkScaffold) modes.push('check-scaffold');\n if (flags.sessionStart) modes.push('session-start');\n if (flags.pricing) modes.push('pricing');\n if (flags.canEdit) modes.push('can-edit');\n\n if (modes.length > 1) {\n throw new Error(\n `cleargate doctor: mutually exclusive flags set: ${modes.join(', ')}. Use only one mode flag at a time.`\n );\n }\n\n if (modes.length === 1) {\n return modes[0]!;\n }\n\n return 'hook-health';\n}\n\n// ─── Hook-health default mode ─────────────────────────────────────────────────\n\nconst HOOK_LOG_24H_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Parse a single gate-check.log line.\n * Format: [ISO_TS] stamp=N gate=N ingest=N file=<path>\n * Returns null if the line does not match.\n */\nexport interface HookLogEntry {\n ts: string;\n stamp: number;\n gate: number;\n ingest: number;\n file: string;\n}\n\nexport function parseHookLogLine(line: string): HookLogEntry | null {\n // [2026-04-19T12:00:00Z] stamp=0 gate=1 ingest=0 file=/some/path\n const m = line.match(\n /^\\[([^\\]]+)\\]\\s+stamp=(\\d+)\\s+gate=(\\d+)\\s+ingest=(\\d+)\\s+file=(.+)$/\n );\n if (!m) return null;\n return {\n ts: m[1]!,\n stamp: parseInt(m[2]!, 10),\n gate: parseInt(m[3]!, 10),\n ingest: parseInt(m[4]!, 10),\n file: m[5]!.trim(),\n };\n}\n\nfunction runHookHealth(\n stdout: (s: string) => void,\n cwd: string,\n now?: Date,\n outcome?: DoctorOutcome\n): void {\n // STORY-014-01: config-error — missing .cleargate/ directory\n const cleargateDir = path.join(cwd, '.cleargate');\n if (!fs.existsSync(cleargateDir)) {\n stdout('cleargate misconfigured: no .cleargate/ found. Run: cleargate init');\n if (outcome) outcome.configError = true;\n return;\n }\n\n // STORY-014-01: config-error — missing cleargate-planning/MANIFEST.json\n const manifestPath = path.join(cwd, 'cleargate-planning', 'MANIFEST.json');\n if (!fs.existsSync(manifestPath)) {\n stdout(`cleargate misconfigured: cleargate-planning/MANIFEST.json not found. Run: cleargate init`);\n if (outcome) outcome.configError = true;\n // Do not return — continue with remaining checks\n }\n\n // Minimal hook-config report: check that .claude/settings.json has the\n // SubagentStop hook wired (if the .claude directory exists).\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n if (!fs.existsSync(settingsPath)) {\n stdout('[doctor] No .claude/settings.json found — hook config unavailable.');\n return;\n }\n\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as unknown;\n const hasHooks =\n typeof settings === 'object' &&\n settings !== null &&\n 'hooks' in settings;\n if (hasHooks) {\n stdout('[doctor] Hook config present in .claude/settings.json.');\n } else {\n stdout('[doctor] .claude/settings.json found but no hooks key — SubagentStop hook not wired.');\n }\n } catch {\n stdout('[doctor] .claude/settings.json is not valid JSON — cannot verify hook config.');\n }\n\n // Scan gate-check.log for recent failures\n const logPath = path.join(cwd, '.cleargate', 'hook-log', 'gate-check.log');\n if (!fs.existsSync(logPath)) {\n return;\n }\n\n let logContent: string;\n try {\n logContent = fs.readFileSync(logPath, 'utf-8');\n } catch {\n return;\n }\n\n const nowMs = (now ?? new Date()).getTime();\n const lines = logContent.split('\\n').filter((l) => l.trim().length > 0);\n\n for (const line of lines) {\n const entry = parseHookLogLine(line);\n if (!entry) continue;\n\n const entryMs = new Date(entry.ts).getTime();\n if (isNaN(entryMs)) continue;\n\n // Only consider entries within the last 24h\n if (nowMs - entryMs > HOOK_LOG_24H_MS) continue;\n\n // A failure means ANY step exit code is non-zero\n const isFailing = entry.stamp !== 0 || entry.gate !== 0 || entry.ingest !== 0;\n if (!isFailing) continue;\n\n stdout(\n `\\u26a0 hook failure at ${entry.ts}: stamp=${entry.stamp} gate=${entry.gate} ingest=${entry.ingest} file=${entry.file}`\n );\n }\n}\n\n// ─── Check-scaffold mode ──────────────────────────────────────────────────────\n\nconst TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Throttle decision: returns true when the cache is fresh enough to skip\n * re-computation.\n *\n * Fresh = `now - lastRefreshed < 24h`.\n * Throttle only applies when `sessionStartMode` is set (non-interactive invocation).\n * Exported for unit tests.\n */\nexport function shouldUseCache(\n lastRefreshed: string,\n now: Date,\n sessionStartMode: boolean\n): boolean {\n if (!sessionStartMode) {\n // Interactive mode always recomputes.\n return false;\n }\n const age = now.getTime() - new Date(lastRefreshed).getTime();\n return age < TWENTY_FOUR_HOURS_MS;\n}\n\n/**\n * Format a single non-clean file line for verbose output.\n * `<path> <state> (<installed>→<current> vs <package>)`\n * with 6-char short hashes.\n */\nexport function formatVerboseLine(\n filePath: string,\n entry: DriftMapEntry\n): string {\n const inst = entry.install_sha ? shortHash(entry.install_sha).slice(0, 6) : 'null';\n const curr = entry.current_sha ? shortHash(entry.current_sha).slice(0, 6) : 'null';\n const pkg = entry.package_sha ? shortHash(entry.package_sha).slice(0, 6) : 'null';\n return ` ${filePath} ${entry.state} (${inst}→${curr} vs ${pkg})`;\n}\n\ntype CountsByState = Record<Exclude<DriftState, 'untracked'>, number> & { untracked: number };\n\nfunction zeroCounts(): CountsByState {\n return {\n 'clean': 0,\n 'user-modified': 0,\n 'upstream-changed': 0,\n 'both-changed': 0,\n 'untracked': 0,\n };\n}\n\nasync function runCheckScaffold(\n flags: DoctorFlags,\n cli: DoctorCliOptions,\n cwd: string,\n now: Date,\n stdout: (s: string) => void,\n _stderr: (s: string) => void\n): Promise<void> {\n // 1. Check daily throttle when in session-start mode\n const sessionStartMode = flags.sessionStartMode ?? false;\n const existingState = await readDriftState(cwd);\n\n if (existingState && shouldUseCache(existingState.last_refreshed, now, sessionStartMode)) {\n // Reuse cached result — emit summary from cached data\n emitSummary(existingState.drift, flags.verbose ?? false, stdout);\n return;\n }\n\n // 2. Load manifests\n const pkgManifest = loadPackageManifest({ packageRoot: cli.packageRoot });\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // 3. Compute SHAs + classify\n const driftMap: DriftMap = {};\n\n await Promise.all(\n pkgManifest.files.map(async (entry) => {\n // Silently skip user-artifact tier (EPIC-009 §6 Q8)\n if (entry.tier === 'user-artifact') {\n return;\n }\n\n const currentSha = await computeCurrentSha(entry, cwd);\n const installSha =\n installSnapshot?.files.find((f) => f.path === entry.path)?.sha256 ?? null;\n const pkgSha = entry.sha256;\n const state = classify(pkgSha, installSha, currentSha, entry.tier);\n\n driftMap[entry.path] = {\n state,\n entry,\n install_sha: installSha,\n current_sha: currentSha,\n package_sha: pkgSha,\n };\n })\n );\n\n // 4. Write drift state atomically\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n // 5. Emit summary\n emitSummary(driftMap, flags.verbose ?? false, stdout);\n}\n\nfunction emitSummary(\n driftMap: DriftMap,\n verbose: boolean,\n stdout: (s: string) => void\n): void {\n const counts = zeroCounts();\n for (const entry of Object.values(driftMap)) {\n counts[entry.state]++;\n }\n\n stdout(\n `Scaffold drift: ${counts['user-modified']} user-modified, ` +\n `${counts['upstream-changed']} upstream-changed, ` +\n `${counts['both-changed']} both-changed, ` +\n `${counts['clean']} clean`\n );\n\n if (counts['upstream-changed'] > 0 || counts['both-changed'] > 0) {\n stdout('Run cleargate upgrade to review.');\n }\n\n if (verbose) {\n for (const [filePath, entry] of Object.entries(driftMap)) {\n if (entry.state !== 'clean' && entry.state !== 'untracked') {\n stdout(formatVerboseLine(filePath, entry));\n }\n }\n }\n}\n\n// ─── Session-start mode ───────────────────────────────────────────────────────\n\nconst SESSION_START_MAX_ITEMS = 10;\nconst SESSION_START_MAX_CHARS = 400;\n\ninterface BlockedItem {\n id: string;\n firstCriterionId: string;\n}\n\n/**\n * Coerce `cached_gate_result` into a typed shape.\n * Accepts both the current native-object form (parseFrontmatter via js-yaml)\n * and the legacy JSON-in-a-string form (pre-BUG-001 files).\n */\nfunction parseCachedGateResult(\n raw: unknown\n): { pass: boolean | null; failing_criteria: Array<{ id: string }> } | null {\n if (raw == null) return null;\n\n let parsed: { pass?: boolean | null; failing_criteria?: Array<{ id: string }> } | null = null;\n\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n parsed = raw as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } else if (typeof raw === 'string') {\n try {\n parsed = JSON.parse(raw) as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } catch {\n return null;\n }\n } else {\n return null;\n }\n\n return {\n pass: parsed.pass ?? null,\n failing_criteria: parsed.failing_criteria ?? [],\n };\n}\n\n/**\n * CR-009: Probe the three-branch resolver chain at runtime and emit one\n * `cleargate CLI: <branch> — <one-line>` status line to stdout.\n * This line is emitted ALWAYS (success or failure) so Claude sees which resolver\n * the hooks will use before any hook fires.\n */\nexport function emitResolverStatusLine(\n cwd: string,\n stdout: (s: string) => void\n): void {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n if (fs.existsSync(distCliPath)) {\n stdout(`cleargate CLI: local dist — ${distCliPath}`);\n return;\n }\n\n // Check PATH\n const whichResult = spawnSync('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n stdout('cleargate CLI: PATH (global install) — cleargate');\n return;\n }\n\n // Try to read the pinned version from the live hook script\n let pinVersion = 'unknown';\n const hookPath = path.join(cwd, '.claude', 'hooks', 'stamp-and-gate.sh');\n if (fs.existsSync(hookPath)) {\n try {\n const hookContent = fs.readFileSync(hookPath, 'utf-8');\n // Pattern: # cleargate-pin: 0.5.0\n const pinMatch = hookContent.match(/^#\\s*cleargate-pin:\\s*(\\S+)\\s*$/m);\n if (pinMatch?.[1]) {\n pinVersion = pinMatch[1];\n } else {\n // Fallback: look for npx -y \"@cleargate/cli@X.Y.Z\"\n const npxMatch = hookContent.match(/@cleargate\\/cli@([^\\s\"']+)/);\n if (npxMatch?.[1]) pinVersion = npxMatch[1];\n }\n } catch {\n // ignore\n }\n }\n\n if (pinVersion === 'unknown') {\n stdout('cleargate CLI: \\u{1F534} not resolvable — hooks will no-op. Fix: npm i -g cleargate or npx cleargate doctor');\n } else {\n stdout(`cleargate CLI: npx @cleargate/cli@${pinVersion} (cold-start ~600ms first call)`);\n }\n}\n\n/**\n * CR-008: planning-first reminder block text.\n * Emitted when pending-sync has zero approved stories AND no sprint-active sentinel.\n */\nexport const PLANNING_FIRST_REMINDER = `Triage first, draft second:\nBefore any Edit/Write that creates user-facing code, you must:\n (1) classify the request (Epic / Story / CR / Bug),\n (2) draft a work item under .cleargate/delivery/pending-sync/ from .cleargate/templates/,\n (3) halt at Gate 1 (Proposal approval) for human sign-off.\nBypass this only if the user has explicitly waived planning in this conversation.`;\n\nexport async function runSessionStart(\n cwd: string,\n stdout: (s: string) => void,\n outcome?: DoctorOutcome\n): Promise<void> {\n // CR-009: emit resolver-status line ALWAYS (before the blocked-items list).\n // STORY-014-01: if resolver is \"not resolvable\", set configError.\n const resolverLines: string[] = [];\n emitResolverStatusLine(cwd, (line) => {\n stdout(line);\n resolverLines.push(line);\n });\n // Check if resolver completely failed (🔴 not resolvable branch)\n if (outcome && resolverLines.some((l) => l.includes('\\u{1F534}'))) {\n outcome.configError = true;\n }\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // Directory doesn't exist or unreadable — nothing to report\n return;\n }\n\n const blocked: BlockedItem[] = [];\n let hasApprovedStory = false;\n\n for (const filePath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n // CR-008: track approved stories for planning-first gate\n if (fm['approved'] === true) {\n hasApprovedStory = true;\n }\n\n const gate = parseCachedGateResult(fm['cached_gate_result']);\n if (!gate || gate.pass !== false) continue;\n\n // Determine item ID from frontmatter\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id', 'sprint_id'];\n let itemId = '';\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim()) {\n itemId = val.trim();\n break;\n }\n }\n if (!itemId) {\n // Fallback: use filename stem\n itemId = path.basename(filePath, '.md');\n }\n\n const firstCriterionId =\n gate.failing_criteria.length > 0 ? (gate.failing_criteria[0]?.id ?? '') : '';\n\n blocked.push({ id: itemId, firstCriterionId });\n }\n\n // CR-008: check sprint-active sentinel\n const activesentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n const sprintActive = fs.existsSync(activesentinel);\n\n // CR-008: emit planning-first reminder when no approved stories AND no active sprint\n const shouldRemind = !hasApprovedStory && !sprintActive;\n if (shouldRemind) {\n stdout(PLANNING_FIRST_REMINDER);\n if (blocked.length > 0) {\n // Separator before blocked items\n stdout('');\n }\n }\n\n if (blocked.length === 0) {\n return;\n }\n\n // STORY-014-01: blocked items present → set blocker flag\n if (outcome) outcome.blocker = true;\n\n const overflow = blocked.length > SESSION_START_MAX_ITEMS\n ? blocked.length - SESSION_START_MAX_ITEMS\n : 0;\n const visible = blocked.slice(0, SESSION_START_MAX_ITEMS);\n\n const lines: string[] = [`${blocked.length} items blocked:`];\n for (const item of visible) {\n const line = item.firstCriterionId\n ? ` ${item.id}: ${item.firstCriterionId}`\n : ` ${item.id}`;\n lines.push(line);\n }\n if (overflow > 0) {\n lines.push(`…and ${overflow} more — run cleargate doctor for full list`);\n }\n\n let output = lines.join('\\n');\n\n // Cap at SESSION_START_MAX_CHARS (100-token proxy)\n if (output.length > SESSION_START_MAX_CHARS) {\n output = output.slice(0, SESSION_START_MAX_CHARS - 3) + '...';\n }\n\n stdout(output);\n}\n\n// ─── Pricing mode ─────────────────────────────────────────────────────────────\n\nexport async function runPricing(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n if (!filePath) {\n // STORY-014-01: missing <file> argument is a config/input error → exit(2)\n stderr('cleargate doctor --pricing: missing <file> argument');\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);\n\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf-8');\n } catch {\n // STORY-014-01: cannot read file is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot read file: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n if (!raw.trimStart().startsWith('---')) {\n // STORY-014-01: file has no frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: file has no frontmatter: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n // STORY-014-01: cannot parse frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot parse frontmatter in: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const draftTokensRaw = fm['draft_tokens'];\n if (!draftTokensRaw) {\n // STORY-014-01: draft_tokens unpopulated is a blocker (content exists, state incomplete) → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let draftTokens: DraftTokensInput & { model: string | null };\n if (typeof draftTokensRaw === 'object' && !Array.isArray(draftTokensRaw)) {\n draftTokens = draftTokensRaw as DraftTokensInput & { model: string | null };\n } else if (typeof draftTokensRaw === 'string') {\n try {\n draftTokens = JSON.parse(draftTokensRaw) as DraftTokensInput & { model: string | null };\n } catch {\n // STORY-014-01: unparseable draft_tokens is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n } else {\n // STORY-014-01: unexpected type is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n // Check if tokens are actually populated\n if (\n draftTokens.input === null &&\n draftTokens.output === null &&\n draftTokens.cache_read === null &&\n draftTokens.cache_creation === null\n ) {\n // STORY-014-01: all null values → blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n const { usd, unknownModel } = computeUsd(draftTokens);\n const model = draftTokens.model ?? 'unknown';\n\n if (unknownModel) {\n stderr(`cleargate doctor --pricing: unknown model '${model}' — no pricing data available`);\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n const fileName = path.basename(absPath);\n\n stdout(\n `${fileName}: ${model} — input:${input} output:${output} cache_read:${cacheRead} cache_creation:${cacheCreation} ≈ $${usd.toFixed(4)}`\n );\n}\n\n// ─── Can-edit mode (CR-008 Phase B) ──────────────────────────────────────────\n\n/**\n * CR-008: reasons why an edit would be blocked.\n */\nexport type CanEditBlockReason = 'no_approved_stories' | 'file_not_in_implementation_files';\n\n/**\n * CR-008: result of the can-edit check.\n */\nexport interface CanEditResult {\n allowed: boolean;\n reason?: CanEditBlockReason;\n}\n\n/**\n * CR-008: simple glob-style match.\n * Supports `*` (any characters except `/`) and `**` (any characters including `/`).\n */\nexport function globMatch(pattern: string, filePath: string): boolean {\n // Normalise separators\n const normalPattern = pattern.replace(/\\\\/g, '/');\n const normalFile = filePath.replace(/\\\\/g, '/');\n\n // Escape regex specials except * and ?\n const regexStr = normalPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '\u0000') // placeholder for **\n .replace(/\\*/g, '[^/]*')\n .replace(/\u0000/g, '.*');\n\n const re = new RegExp(`^${regexStr}$`);\n return re.test(normalFile);\n}\n\n/**\n * CR-008: check whether editing `filePath` is permitted.\n *\n * Logic:\n * 1. If sprint-active sentinel exists → always allowed.\n * 2. Read pending-sync/*.md; for each with approved: true:\n * a. If no implementation_files field → treat as \"any approved story → allow\".\n * b. If implementation_files present → glob-match filePath against each pattern.\n * 3. If zero approved stories → block with reason 'no_approved_stories'.\n * 4. If approved stories exist but filePath not covered → block with 'file_not_in_implementation_files'.\n */\nexport async function runCanEdit(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n // Sprint-active sentinel → always allow\n const activeSentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n if (fs.existsSync(activeSentinel)) {\n stdout('allowed: sprint active');\n return;\n }\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // No pending-sync dir → no approved stories → blocker → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let hasApprovedStory = false;\n let coveredByStory = false;\n\n for (const storyPath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(storyPath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n if (fm['approved'] !== true) continue;\n\n hasApprovedStory = true;\n\n const implFilesRaw = fm['implementation_files'];\n if (implFilesRaw === undefined || implFilesRaw === null) {\n // No implementation_files field → any approved story covers any file\n coveredByStory = true;\n break;\n }\n\n if (Array.isArray(implFilesRaw)) {\n for (const pattern of implFilesRaw) {\n if (typeof pattern !== 'string') continue;\n if (globMatch(pattern, filePath)) {\n coveredByStory = true;\n break;\n }\n }\n }\n\n if (coveredByStory) break;\n }\n\n if (!hasApprovedStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n if (!coveredByStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: file_not_in_implementation_files');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n stdout('allowed');\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function doctorHandler(\n flags: DoctorFlags,\n cli?: DoctorCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n\n // STORY-014-01: outcome accumulator — each mode pushes booleans here.\n // At the end, we exit per §3.2 pseudocode:\n // configError → exit(2), blocker → exit(1), else → exit(0).\n const outcome: DoctorOutcome = { configError: false, blocker: false };\n\n // Track whether exit() was already called by a mode (e.g. runPricing early error)\n // so we don't double-exit from the final outcome block.\n let exitedEarly = false;\n const wrappedExit = (code: number): never => {\n exitedEarly = true;\n return exit(code);\n };\n\n let mode: DoctorMode;\n try {\n mode = selectMode(flags);\n } catch (err) {\n // STORY-014-01: mutually exclusive flags is a config error → exit(2)\n stderr((err as Error).message);\n exit(2);\n return;\n }\n\n switch (mode) {\n case 'check-scaffold':\n await runCheckScaffold(flags, cli ?? {}, cwd, now, stdout, stderr);\n break;\n\n case 'hook-health':\n runHookHealth(stdout, cwd, now, outcome);\n break;\n\n case 'session-start':\n await runSessionStart(cwd, stdout, outcome);\n break;\n\n case 'pricing':\n await runPricing(flags.pricingFile ?? '', cwd, stdout, stderr, wrappedExit, outcome);\n break;\n\n case 'can-edit':\n await runCanEdit(flags.canEditFile ?? '', cwd, stdout, wrappedExit, outcome);\n break;\n\n default: {\n const exhaustiveCheck: never = mode;\n stderr(`cleargate doctor: unknown mode '${String(exhaustiveCheck)}'`);\n // STORY-014-01: unknown mode is a config error → exit(2)\n exit(2);\n return;\n }\n }\n\n // STORY-014-01: §3.2 pseudocode exit-code computation.\n // If a mode called exit() early (e.g. runPricing on bad input), don't double-exit.\n if (exitedEarly) return;\n\n if (outcome.configError) {\n exit(2);\n } else if (outcome.blocker) {\n exit(1);\n } else {\n exit(0);\n }\n}\n","/**\n * pricing.ts — STORY-008-06\n *\n * USD pricing table for Claude models (per 1M tokens).\n * Numbers from Anthropic public pricing as of 2026-04-19.\n * No network. No config file. Numbers live in source.\n */\n\nexport interface ModelPricing {\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n}\n\n/**\n * Pricing table: USD per 1,000,000 tokens.\n *\n * claude-opus-4-7: $15 input / $75 output / $1.50 cache_read / $18.75 cache_creation\n * claude-sonnet-4-5: $3 input / $15 output / $0.30 cache_read / $3.75 cache_creation\n * claude-haiku-4-5: $0.80 input / $4 output / $0.08 cache_read / $1 cache_creation\n */\nexport const PRICING_TABLE: Record<string, ModelPricing> = {\n 'claude-opus-4-7': {\n input: 15.0,\n output: 75.0,\n cache_read: 1.5,\n cache_creation: 18.75,\n },\n 'claude-sonnet-4-5': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-sonnet-4-6': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-haiku-4-5': {\n input: 0.8,\n output: 4.0,\n cache_read: 0.08,\n cache_creation: 1.0,\n },\n};\n\nexport interface DraftTokensInput {\n input: number | null;\n output: number | null;\n cache_read: number | null;\n cache_creation: number | null;\n model: string | null;\n}\n\nexport interface ComputeUsdResult {\n usd: number;\n unknownModel: boolean;\n}\n\n/**\n * Compute USD cost from draft_tokens and model.\n *\n * If modelOverride is provided, it takes precedence over draftTokens.model.\n * Unknown model → {usd: 0, unknownModel: true}.\n * All token counts default to 0 if null.\n */\nexport function computeUsd(\n draftTokens: DraftTokensInput,\n modelOverride?: string\n): ComputeUsdResult {\n const model = modelOverride ?? draftTokens.model ?? '';\n const pricing = PRICING_TABLE[model];\n\n if (!pricing) {\n return { usd: 0, unknownModel: true };\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n\n const usd =\n (input * pricing.input +\n output * pricing.output +\n cacheRead * pricing.cache_read +\n cacheCreation * pricing.cache_creation) /\n 1_000_000;\n\n return { usd, unknownModel: false };\n}\n","/**\n * gate.ts — `cleargate gate check|explain|qa|arch` command handlers.\n *\n * STORY-008-03: Wires readiness-predicates.evaluate() + frontmatter-cache into\n * two Commander subcommands (check + explain).\n *\n * STORY-013-08: Extends with gate qa|arch subcommands that shell out via\n * run_script.sh to pre_gate_runner.sh. Both are v1-inert.\n *\n * FLASHCARD #cli #commander #optional-key: opts.transition may be undefined — strip key.\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout`, `stderr` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * Output is agent-facing: only ❌ / ⚠ / ✅ emoji; no ANSI color codes.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { evaluate } from '../lib/readiness-predicates.js';\nimport type { ParsedDoc } from '../lib/readiness-predicates.js';\nimport { readCachedGate, writeCachedGate } from '../lib/frontmatter-cache.js';\nimport type { CachedGate } from '../lib/frontmatter-cache.js';\nimport {\n detectWorkItemTypeFromFm,\n WORK_ITEM_TRANSITIONS,\n} from '../lib/work-item-type.js';\nimport type { WorkItemType } from '../lib/work-item-type.js';\nimport { toIsoSecond } from '../lib/frontmatter-yaml.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to readiness-gates.md (test seam). */\n gatesDocPath?: string;\n /** Override path to wiki index (test seam). */\n wikiIndexPath?: string;\n}\n\n// ─── Internal gate-block shape ────────────────────────────────────────────────\n\ninterface GateCriterion {\n id: string;\n check: string;\n}\n\ninterface GateBlock {\n work_item_type: string;\n transition: string;\n severity: 'advisory' | 'enforcing';\n criteria: GateCriterion[];\n}\n\n// ─── Gate document loader ─────────────────────────────────────────────────────\n\n/**\n * Load and parse all fenced ```yaml blocks from readiness-gates.md.\n * Each block's yaml.load() returns an array — unwrap [0] per FLASHCARD.\n */\nfunction loadGateBlocks(gatesDocPath: string): GateBlock[] {\n const raw = fs.readFileSync(gatesDocPath, 'utf8');\n const blocks: GateBlock[] = [];\n\n // Match all fenced ```yaml ... ``` blocks\n const fenceRe = /^```yaml\\n([\\s\\S]*?)^```/gm;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(raw)) !== null) {\n const yamlContent = match[1]!;\n const parsed = yaml.load(yamlContent);\n // Per FLASHCARD: readiness-gates.md fenced yaml blocks are YAML lists; unwrap [0]\n const block = Array.isArray(parsed) ? parsed[0] : parsed;\n if (\n block &&\n typeof block === 'object' &&\n 'work_item_type' in block &&\n 'transition' in block &&\n 'severity' in block &&\n 'criteria' in block\n ) {\n blocks.push(block as GateBlock);\n }\n }\n return blocks;\n}\n\n/**\n * Find the gate block matching a work-item type + transition.\n */\nfunction findGate(\n blocks: GateBlock[],\n type: WorkItemType,\n transition: string,\n): GateBlock | null {\n return blocks.find(\n (b) => b.work_item_type === type && b.transition === transition,\n ) ?? null;\n}\n\n/**\n * Infer the default transition for a work-item type given the current cached gate state.\n * - If cached_gate_result is absent or failing → return first transition.\n * - If cached_gate_result.pass === true and there's a next transition → return next.\n * - Otherwise return first transition.\n */\nfunction inferTransition(\n type: WorkItemType,\n cachedGate: CachedGate | null,\n): string {\n const transitions = WORK_ITEM_TRANSITIONS[type];\n if (!cachedGate || !cachedGate.pass) {\n return transitions[0]!;\n }\n // Find next unpassed transition\n // We don't know which transition was last checked from cache alone;\n // for Epic: if cached pass=true, assume first is done → pick second.\n // For types with only one transition: always return that one.\n if (transitions.length === 1) {\n return transitions[0]!;\n }\n // Multi-transition (Epic): if cached gate passes, infer next\n return transitions[1]!;\n}\n\n// ─── gateCheckHandler ─────────────────────────────────────────────────────────\n\nexport async function gateCheckHandler(\n file: string,\n opts: { verbose?: boolean; transition?: string },\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n const nowFn = cli?.now ?? (() => new Date());\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Parse the document\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch (err) {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Detect work-item type from frontmatter\n const detectedType = detectWorkItemTypeFromFm(fm);\n if (!detectedType) {\n stderrFn(`[cleargate gate] error: unable to detect work-item type from frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Load gates document\n const projectRoot = cwd;\n const gatesDocPath = cli?.gatesDocPath\n ?? path.join(projectRoot, '.cleargate', 'knowledge', 'readiness-gates.md');\n\n if (!fs.existsSync(gatesDocPath)) {\n stderrFn(`[cleargate gate] error: readiness-gates.md not found at: ${gatesDocPath}`);\n return exitFn(1);\n }\n\n let gateBlocks: GateBlock[];\n try {\n gateBlocks = loadGateBlocks(gatesDocPath);\n } catch (err) {\n stderrFn(`[cleargate gate] error: failed to parse readiness-gates.md: ${String(err)}`);\n return exitFn(1);\n }\n\n // Read current cached gate for transition inference\n const cachedGate = await readCachedGate(absPath);\n\n // Determine transition\n const transition = opts.transition ?? inferTransition(detectedType, cachedGate);\n\n // Find the matching gate\n const gate = findGate(gateBlocks, detectedType, transition);\n if (!gate) {\n stderrFn(\n `[cleargate gate] error: no gate definition found for ${detectedType}.${transition}`,\n );\n return exitFn(1);\n }\n\n const wikiIndexPath = cli?.wikiIndexPath;\n const parsedDoc: ParsedDoc = { fm, body, absPath };\n const evalOpts = { projectRoot, ...(wikiIndexPath ? { wikiIndexPath } : {}) };\n\n // Evaluate each criterion\n const failingCriteria: { id: string; detail: string }[] = [];\n const allResults: Array<{ id: string; pass: boolean; detail: string }> = [];\n\n for (const criterion of gate.criteria) {\n let result: { pass: boolean; detail: string };\n try {\n result = evaluate(criterion.check, parsedDoc, evalOpts);\n } catch (err) {\n result = { pass: false, detail: `predicate error: ${String(err)}` };\n }\n allResults.push({ id: criterion.id, ...result });\n if (!result.pass) {\n failingCriteria.push({ id: criterion.id, detail: result.detail });\n }\n }\n\n const overallPass = failingCriteria.length === 0;\n const lastGateCheck = toIsoSecond(nowFn());\n\n // Write cached gate result\n const cacheResult: CachedGate = {\n pass: overallPass,\n failing_criteria: failingCriteria,\n last_gate_check: lastGateCheck,\n };\n await writeCachedGate(absPath, cacheResult, { now: nowFn });\n\n // Format and emit output\n const isAdvisory = gate.severity === 'advisory';\n const headerLine = `Gate: ${detectedType}.${transition} (${gate.severity})`;\n stdoutFn(headerLine);\n\n if (overallPass) {\n stdoutFn(`\\u2705 ${detectedType}.${transition} passed (${gate.criteria.length} criteria)`);\n } else {\n for (const r of allResults) {\n if (!r.pass) {\n if (isAdvisory) {\n stdoutFn(`\\u26A0 ${r.id}: ${r.detail} (advisory)`);\n } else {\n stdoutFn(`\\u274C ${r.id}: ${r.detail}`);\n }\n }\n if (opts.verbose) {\n // In verbose mode, emit full detail per criterion\n stdoutFn(` [${r.pass ? 'pass' : 'fail'}] ${r.id}: ${r.detail}`);\n }\n }\n }\n\n // Severity-based exit routing\n if (!overallPass && !isAdvisory) {\n return exitFn(1);\n }\n // advisory or pass → exit 0 (implicit return)\n}\n\n// ─── gateExplainHandler ───────────────────────────────────────────────────────\n\nexport async function gateExplainHandler(\n file: string,\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Read cached gate result — read-only, no evaluate calls\n const cached = await readCachedGate(absPath);\n\n if (!cached) {\n stdoutFn('no gate check cached; run: cleargate gate check <file>');\n return;\n }\n\n // Parse frontmatter to get type info (read-only — no writes)\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n const detectedType = detectWorkItemTypeFromFm(fm) ?? 'unknown';\n\n // Render ≤50-LLM-token summary\n const failingIds = cached.failing_criteria.map((c) => c.id).join(', ');\n const statusStr = cached.pass ? 'pass' : 'fail';\n const summary = failingIds\n ? `${detectedType}: ${statusStr} at ${cached.last_gate_check}; ${cached.failing_criteria.length} failing: ${failingIds}`\n : `${detectedType}: ${statusStr} at ${cached.last_gate_check}`;\n\n stdoutFn(summary);\n}\n\n// ─── v2 gate qa|arch handlers ─────────────────────────────────────────────────\n\n/**\n * Options for v2 gate subcommands (qa + arch).\n * Extends GateCliOptions with execution-mode seams.\n */\nexport interface GateV2CliOptions extends GateCliOptions, ExecutionModeOptions {\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Sprint ID for execution_mode discovery. */\n sprintId?: string;\n}\n\nfunction resolveRunScriptForGate(opts: GateV2CliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * `cleargate gate qa <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh qa <worktree> <branch>`\n */\nexport function gateQaHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'qa', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate qa] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n/**\n * `cleargate gate arch <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh arch <worktree> <branch>`\n */\nexport function gateArchHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'arch', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate arch] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * execution-mode.ts — reads `execution_mode` from a Sprint Plan frontmatter.\n *\n * STORY-013-08: provides a test seam via `sprintFilePath` override so tests\n * can inject synthetic SPRINT-99.md fixture without touching live state.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests — keep exitFn\n * only at handler top-level (not inside helper functions).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nconst V1_INERT_MESSAGE =\n 'v1 mode active — command inert. Set execution_mode: v2 in sprint frontmatter to enable.';\n\nexport type ExecutionMode = 'v1' | 'v2';\n\nexport interface ExecutionModeOptions {\n /** Absolute path to the sprint file. Overrides auto-discovery. */\n sprintFilePath?: string;\n /** Working directory for relative-path resolution. Defaults to process.cwd(). */\n cwd?: string;\n /**\n * When true and sprintId is absent or 'SPRINT-UNKNOWN', read\n * `.cleargate/sprint-runs/.active` for the sprint ID before falling through\n * to v1-inert. Callers can also use `resolveSprintIdFromSentinel` directly.\n */\n sentinelFallback?: boolean;\n}\n\n/**\n * Parse just the YAML frontmatter from a markdown file.\n * Returns the raw frontmatter block as a plain object.\n * On any parse failure, returns an empty object.\n */\nfunction parseFrontmatterSimple(raw: string): Record<string, unknown> {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(raw);\n if (!match) return {};\n const block = match[1]!;\n const result: Record<string, unknown> = {};\n for (const line of block.split('\\n')) {\n const kv = /^([^:]+):\\s*(.*)$/.exec(line.trim());\n if (!kv) continue;\n const key = kv[1]!.trim();\n const val = kv[2]!.trim().replace(/^[\"']|[\"']$/g, '');\n result[key] = val;\n }\n return result;\n}\n\n/**\n * Discover the sprint file for a given sprint ID.\n * Looks in `.cleargate/delivery/pending-sync/SPRINT-{id}_*.md`\n * and `.cleargate/delivery/archive/SPRINT-{id}_*.md`.\n *\n * Returns null if no matching file is found.\n */\nfunction discoverSprintFile(sprintId: string, cwd: string): string | null {\n const searchDirs = [\n path.join(cwd, '.cleargate', 'delivery', 'pending-sync'),\n path.join(cwd, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of searchDirs) {\n if (!fs.existsSync(dir)) continue;\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n continue;\n }\n const prefix = `${sprintId}_`;\n for (const entry of entries) {\n if (entry.startsWith(prefix) && entry.endsWith('.md')) {\n return path.join(dir, entry);\n }\n // Also allow exact match like SPRINT-99.md (test fixtures)\n if (entry === `${sprintId}.md`) {\n return path.join(dir, entry);\n }\n }\n }\n\n return null;\n}\n\n/**\n * Read the active sprint ID from `.cleargate/sprint-runs/.active`.\n * Returns null if the file does not exist or is empty after trim.\n *\n * This is the primary API for sentinel-based sprint discovery. Callers that\n * need a fallback chain use:\n * const sprintId = argSprintId ?? resolveSprintIdFromSentinel(cwd);\n * const mode = readSprintExecutionMode(sprintId ?? 'SPRINT-UNKNOWN', { cwd });\n */\nexport function resolveSprintIdFromSentinel(cwd?: string): string | null {\n const resolvedCwd = cwd ?? process.cwd();\n const sentinelPath = path.join(resolvedCwd, '.cleargate', 'sprint-runs', '.active');\n try {\n const content = fs.readFileSync(sentinelPath, 'utf8').trim();\n return content.length > 0 ? content : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read the `execution_mode` field from a sprint file.\n *\n * Resolution order:\n * 1. If `opts.sprintFilePath` is set, use that directly.\n * 2. Otherwise, discover the file by sprintId in `.cleargate/delivery/`.\n * 3. If `opts.sentinelFallback` is true and sprintId is absent or\n * 'SPRINT-UNKNOWN', read `.cleargate/sprint-runs/.active` and substitute.\n * 4. If no file found, return \"v1\" (safe default per §19.5).\n */\nexport function readSprintExecutionMode(\n sprintId: string,\n opts: ExecutionModeOptions = {},\n): ExecutionMode {\n const cwd = opts.cwd ?? process.cwd();\n\n // Sentinel fallback: when sprintId is absent or unknown, try .active\n let resolvedSprintId = sprintId;\n if (opts.sentinelFallback && (!resolvedSprintId || resolvedSprintId === 'SPRINT-UNKNOWN')) {\n const sentinelId = resolveSprintIdFromSentinel(cwd);\n if (sentinelId) {\n resolvedSprintId = sentinelId;\n }\n }\n\n let filePath: string | null = opts.sprintFilePath ?? null;\n if (!filePath) {\n filePath = discoverSprintFile(resolvedSprintId, cwd);\n }\n\n if (!filePath || !fs.existsSync(filePath)) {\n // Default to v1 — safe, no behavioral change (§19.5)\n return 'v1';\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf8');\n } catch {\n return 'v1';\n }\n\n const fm = parseFrontmatterSimple(raw);\n const mode = fm['execution_mode'];\n\n if (mode === 'v2') return 'v2';\n return 'v1';\n}\n\n/**\n * Print the v1-inert message and exit 0.\n * The caller is responsible for calling this when execution_mode is v1.\n */\nexport function printInertAndExit(\n stdoutFn: (s: string) => void,\n exitFn: (code: number) => never,\n): never {\n stdoutFn(V1_INERT_MESSAGE);\n return exitFn(0);\n}\n\nexport { V1_INERT_MESSAGE };\n","/**\n * STORY-008-02: Predicate evaluator for ClearGate readiness gates.\n * Supports exactly 6 closed-set predicate shapes. Any other shape throws.\n * Sandboxed: no shell-out, no network, read-only FS limited to projectRoot.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ParsedPredicate =\n | { kind: 'frontmatter'; ref: string; field: string; op: '==' | '!=' | '>=' | '<='; value: string | number | boolean }\n | { kind: 'body-contains'; needle: string; negated: boolean }\n | { kind: 'marker-absence'; marker: 'TBD' | 'TODO' | 'FIXME' }\n | { kind: 'section'; index: number; count: { op: '>=' | '==' | '>'; n: number }; itemType: 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item' }\n | { kind: 'file-exists'; path: string }\n | { kind: 'link-target-exists'; id: string }\n | { kind: 'status-of'; id: string; value: string };\n\nexport interface ParsedDoc {\n fm: Record<string, unknown>;\n body: string;\n absPath: string;\n}\n\nexport interface EvalOptions {\n projectRoot?: string;\n wikiIndexPath?: string;\n}\n\n// ─── Parser ───────────────────────────────────────────────────────────────────\n\n/**\n * Parse a predicate string into a typed ParsedPredicate.\n * Throws with \"unsupported predicate shape: <src>\" on any unrecognized input.\n * Target: ≤150 LoC, hand-rolled tokenizer + switch on first token.\n */\nexport function parsePredicate(src: string): ParsedPredicate {\n const s = src.trim();\n\n // 1. frontmatter(<ref>).<field> <op> <value>\n const fmMatch = s.match(\n /^frontmatter\\(([^)]*)\\)\\.(\\w+)\\s*(==|!=|>=|<=)\\s*(.+)$/\n );\n if (fmMatch) {\n const ref = fmMatch[1]!.trim();\n if (ref === '') throw new Error(`unsupported predicate shape: ${src}`);\n const field = fmMatch[2]!;\n const op = fmMatch[3] as '==' | '!=' | '>=' | '<=';\n const rawVal = fmMatch[4]!.trim();\n const value = parseValue(rawVal);\n return { kind: 'frontmatter', ref, field, op, value };\n }\n\n // 2a. body does not contain marker '<id>' — new marker-absence shape\n const markerNotMatch = s.match(/^body does not contain marker ['\"]([A-Z]+)['\"]$/);\n if (markerNotMatch) {\n const marker = markerNotMatch[1]!;\n if (marker !== 'TBD' && marker !== 'TODO' && marker !== 'FIXME') {\n throw new Error(`unsupported predicate shape: ${src}`);\n }\n return { kind: 'marker-absence', marker };\n }\n\n // 2b. body does not contain '<needle>'\n const bodyNotMatch = s.match(/^body does not contain ['\"](.+)['\"]$/);\n if (bodyNotMatch) {\n return { kind: 'body-contains', needle: bodyNotMatch[1]!, negated: true };\n }\n\n // 2c. body contains '<needle>'\n const bodyMatch = s.match(/^body contains ['\"](.+)['\"]$/);\n if (bodyMatch) {\n return { kind: 'body-contains', needle: bodyMatch[1]!, negated: false };\n }\n\n // 3. section(<N>) has <count> <item-type>\n const sectionMatch = s.match(\n /^section\\((\\d+)\\) has (≥|>=|==|>)(\\d+) (checked-checkbox|unchecked-checkbox|listed-item)$/\n );\n if (sectionMatch) {\n const index = parseInt(sectionMatch[1]!, 10);\n const opChar = sectionMatch[2]!;\n const n = parseInt(sectionMatch[3]!, 10);\n const itemType = sectionMatch[4] as 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item';\n let countOp: '>=' | '==' | '>';\n if (opChar === '≥' || opChar === '>=') countOp = '>=';\n else if (opChar === '>') countOp = '>';\n else countOp = '==';\n return { kind: 'section', index, count: { op: countOp, n }, itemType };\n }\n\n // 4. file-exists(<path>)\n const fileExistsMatch = s.match(/^file-exists\\((.+)\\)$/);\n if (fileExistsMatch) {\n const filePath = fileExistsMatch[1]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'file-exists', path: filePath };\n }\n\n // 5. link-target-exists([[ID]])\n const linkMatch = s.match(/^link-target-exists\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)$/);\n if (linkMatch) {\n return { kind: 'link-target-exists', id: linkMatch[1]! };\n }\n\n // 6. status-of([[ID]]) == <value>\n const statusMatch = s.match(/^status-of\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)\\s*==\\s*(.+)$/);\n if (statusMatch) {\n const id = statusMatch[1]!;\n const value = statusMatch[2]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'status-of', id, value };\n }\n\n throw new Error(`unsupported predicate shape: ${src}`);\n}\n\n/** Parse a YAML scalar value string to string | number | boolean. */\nfunction parseValue(raw: string): string | number | boolean {\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n if (raw === 'null') return 'null'; // treat null as string \"null\" for comparison\n const num = Number(raw);\n if (!isNaN(num) && raw !== '') return num;\n // Strip quotes\n return raw.replace(/^['\"]|['\"]$/g, '');\n}\n\n// ─── Evaluator ────────────────────────────────────────────────────────────────\n\n/**\n * Evaluate a predicate string against a document. Returns {pass, detail}.\n * Throws on malformed predicate (delegate to parsePredicate).\n */\nexport function evaluate(\n predicate: string,\n doc: ParsedDoc,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const parsed = parsePredicate(predicate);\n const projectRoot = opts?.projectRoot ?? process.cwd();\n\n switch (parsed.kind) {\n case 'frontmatter':\n return evalFrontmatter(parsed, doc, projectRoot);\n case 'body-contains':\n return evalBodyContains(parsed, doc);\n case 'marker-absence':\n return evalMarkerAbsence(parsed, doc);\n case 'section':\n return evalSection(parsed, doc);\n case 'file-exists':\n return evalFileExists(parsed, projectRoot);\n case 'link-target-exists':\n return evalLinkTargetExists(parsed, opts);\n case 'status-of':\n return evalStatusOf(parsed, opts, projectRoot);\n }\n}\n\n// ─── Frontmatter evaluator ────────────────────────────────────────────────────\n\nfunction evalFrontmatter(\n parsed: Extract<ParsedPredicate, { kind: 'frontmatter' }>,\n doc: ParsedDoc,\n projectRoot: string\n): { pass: boolean; detail: string } {\n let fm: Record<string, unknown>;\n\n if (parsed.ref === '.') {\n fm = doc.fm;\n } else {\n // ref is a frontmatter key whose value is a path to another document\n const refVal = doc.fm[parsed.ref];\n if (refVal === undefined || refVal === null) {\n return {\n pass: false,\n detail: `frontmatter key '${parsed.ref}' is missing or null in ${doc.absPath}`,\n };\n }\n\n // Sub-fix #1 (BUG-008): prose-vs-path heuristic.\n // If the value looks like prose (contains a space, em-dash, en-dash, colon, parens,\n // newline, or exceeds 200 chars), it is not a file path. In that case, pass the gate\n // only when the parent document declares an explicit proposal-gate waiver via any of:\n // - proposal_gate_waiver: <truthy> — explicit opt-in waiver field\n // - approved_by: <non-empty> AND approved_at: <non-empty> — existing approval fields\n // A plain path like \"PROPOSAL-999.md\" (no spaces, ≤200 chars, no special prose chars)\n // still falls through to the existing resolveLinkedPath logic — preserving the R-08\n // regression guarantee that broken file references still fail.\n const refStr = String(refVal);\n const looksLikeProse =\n refStr.length > 200 ||\n /[ —–:()\\n]/.test(refStr); // space, em-dash, en-dash, colon, parens, newline\n if (looksLikeProse) {\n // Signal 1: explicit proposal_gate_waiver field\n const waiver = doc.fm['proposal_gate_waiver'];\n const hasExplicitWaiver =\n waiver !== null && waiver !== undefined && waiver !== false &&\n String(waiver).trim() !== '' && String(waiver).trim() !== 'false';\n // Signal 2: approved_by + approved_at both set (existing approval fields)\n const approvedBy = doc.fm['approved_by'];\n const approvedAt = doc.fm['approved_at'];\n const hasApprovalFields =\n approvedBy !== null && approvedBy !== undefined && String(approvedBy).trim() !== '' &&\n approvedAt !== null && approvedAt !== undefined && String(approvedAt).trim() !== '';\n const hasWaiver = hasExplicitWaiver || hasApprovalFields;\n if (hasWaiver) {\n return {\n pass: true,\n detail: `context_source is prose; proposal-gate waiver per frontmatter approved_by/approved_at`,\n };\n }\n return {\n pass: false,\n detail: `context_source is prose but no proposal_gate_waiver (approved_by + approved_at) found in frontmatter`,\n };\n }\n\n // Resolve the path\n const linkedPath = resolveLinkedPath(String(refVal), doc.absPath, projectRoot);\n if (!linkedPath) {\n return {\n pass: false,\n detail: `linked file not found: ${refVal}`,\n };\n }\n fm = readFrontmatterFromFile(linkedPath);\n }\n\n const actual = fm[parsed.field];\n\n // Compare\n const pass = compareValues(actual, parsed.op, parsed.value);\n const detail = pass\n ? `frontmatter(${parsed.ref}).${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)} → actual: ${JSON.stringify(actual)}`\n : `expected ${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)}, got ${JSON.stringify(actual)}`;\n\n return { pass, detail };\n}\n\nfunction compareValues(\n actual: unknown,\n op: '==' | '!=' | '>=' | '<=',\n expected: string | number | boolean\n): boolean {\n // null check for != null\n if (expected === 'null') {\n const isNull = actual === null || actual === undefined || actual === '' || actual === 'null';\n return op === '==' ? isNull : !isNull;\n }\n // Normalize actual: strip quotes\n let a: unknown = actual;\n if (typeof a === 'string') {\n a = a.replace(/^[\"']|[\"']$/g, '');\n // Try to coerce to bool/number for comparison\n if (a === 'true') a = true;\n else if (a === 'false') a = false;\n else {\n const n = Number(a);\n if (!isNaN(n) && (a as string) !== '') a = n;\n }\n }\n\n switch (op) {\n case '==': return a === expected || String(a) === String(expected);\n case '!=': return a !== expected && String(a) !== String(expected);\n case '>=': return Number(a) >= Number(expected);\n case '<=': return Number(a) <= Number(expected);\n }\n}\n\n/** Resolve a path reference relative to the document or project root. */\nfunction resolveLinkedPath(\n ref: string,\n docAbsPath: string,\n projectRoot: string\n): string | null {\n // Try relative to doc first, then relative to projectRoot\n const candidates = [\n path.resolve(path.dirname(docAbsPath), ref),\n path.resolve(projectRoot, ref),\n ];\n for (const candidate of candidates) {\n // Sandbox check\n if (!candidate.startsWith(projectRoot)) continue;\n if (fs.existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/** Read frontmatter from a file as a plain Record. Does not throw on body. */\nfunction readFrontmatterFromFile(absPath: string): Record<string, unknown> {\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return {};\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return {};\n const fmLines = lines.slice(1, closeIdx);\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n if (line.trim() === '' || line.trim().startsWith('#')) continue;\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '' || val === '[]') { fm[key] = []; continue; }\n if (val.startsWith('{')) { fm[key] = val; continue; }\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1).trim();\n fm[key] = inner === '' ? [] : inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return fm;\n } catch {\n return {};\n }\n}\n\n// ─── Body-contains evaluator ──────────────────────────────────────────────────\n\nfunction evalBodyContains(\n parsed: Extract<ParsedPredicate, { kind: 'body-contains' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const body = doc.body;\n const needle = parsed.needle;\n\n // Count occurrences and find section context\n let count = 0;\n let pos = 0;\n const sections: number[] = []; // 1-indexed section numbers for each occurrence\n const bodySections = body.split(/^## /m);\n\n // Simple occurrence count\n while ((pos = body.indexOf(needle, pos)) !== -1) {\n count++;\n // Find which section this occurrence is in\n const before = body.slice(0, pos);\n const sectionCount = (before.match(/^## /gm) || []).length;\n sections.push(sectionCount + 1); // 1-indexed\n pos += needle.length;\n }\n\n const present = count > 0;\n void bodySections; // suppress unused warning\n\n if (parsed.negated) {\n // \"body does not contain\" → pass when absent\n if (present) {\n const sectionList = [...new Set(sections)].map((s) => `§${s}`).join(', ');\n return {\n pass: false,\n detail: `${count} occurrence${count === 1 ? '' : 's'} at ${sectionList}`,\n };\n }\n return { pass: true, detail: `'${needle}' not found in body` };\n } else {\n // \"body contains\" → pass when present\n if (present) {\n return { pass: true, detail: `'${needle}' found ${count} time${count === 1 ? '' : 's'}` };\n }\n return { pass: false, detail: `'${needle}' not found in body` };\n }\n}\n\n// ─── Marker-absence evaluator ─────────────────────────────────────────────────\n\n/**\n * Sub-fix #2 (BUG-008): Evaluates \"body does not contain marker '<id>'\".\n * A marker is counted only when it appears in a syntactic role:\n * 1. Followed immediately by a colon: TBD: ...\n * 2. Wrapped in parens: (TBD) or square brackets: [TBD]\n * 3. The entire trimmed line equals the marker: bare TBD on its own line\n * 4. Preceded by a code-comment prefix: // TBD or # TBD\n *\n * NOT counted:\n * - TBD as part of another word (TBDs, TBDish, TBD's)\n * - TBD inside quotes in prose (\"TBD resolution\")\n * - Template self-reference lines: \"- [x] 0 \"TBDs\" exist\" / \"- [ ] 0 \"TBDs\" exist\"\n */\nfunction evalMarkerAbsence(\n parsed: Extract<ParsedPredicate, { kind: 'marker-absence' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const { marker } = parsed;\n const lines = doc.body.split('\\n');\n\n // Template self-reference lines to exclude (BUG-008 spec: \"- [x] 0 \"TBDs\" exist\")\n const templateSelfRefRe = /^\\s*-\\s*\\[[x ]\\]\\s*0\\s*\"TBDs?\"\\s*exist/i;\n\n // Regex: marker in a syntactic role.\n // Matches: (MARKER) | [MARKER] | MARKER: | // MARKER | # MARKER | bare MARKER line\n // The (?<!\\w) and (?!\\w) prevent matching inside longer words.\n const markerRe = new RegExp(\n `(?:^|(?<=\\\\())${marker}(?=:)|` + // MARKER: (colon follows)\n `\\\\(${marker}\\\\)|` + // (MARKER) parens\n `\\\\[${marker}\\\\]|` + // [MARKER] square brackets\n `(?<=//\\\\s*)${marker}(?!\\\\w)|` + // // MARKER (comment)\n `(?<=#\\\\s*)${marker}(?!\\\\w)`, // # MARKER (comment)\n 'g'\n );\n\n // Also check bare-line: entire trimmed line is just the marker\n const bareLineRe = new RegExp(`^${marker}$`);\n\n const violations: number[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n\n // Skip template self-reference boilerplate\n if (templateSelfRefRe.test(line)) continue;\n\n const trimmed = line.trim();\n\n // Check bare line\n if (bareLineRe.test(trimmed)) {\n violations.push(i + 1);\n continue;\n }\n\n // Check syntactic marker roles via regex\n markerRe.lastIndex = 0;\n if (markerRe.test(line)) {\n violations.push(i + 1);\n }\n }\n\n if (violations.length > 0) {\n return {\n pass: false,\n detail: `${violations.length} marker occurrence${violations.length === 1 ? '' : 's'} of '${marker}' at line${violations.length === 1 ? '' : 's'} ${violations.join(', ')}`,\n };\n }\n return { pass: true, detail: `no '${marker}' markers found in body` };\n}\n\n// ─── Section evaluator ────────────────────────────────────────────────────────\n\nfunction evalSection(\n parsed: Extract<ParsedPredicate, { kind: 'section' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n // Split body on ## headings (1-indexed).\n // Use lookahead so each part starts with \"## \" (or is preamble if body doesn't start with ##).\n const body = doc.body;\n\n const rawParts = body.split(/^(?=## )/m);\n // If body starts with \"## \", rawParts[0] = \"## Section 1\\n...\", rawParts[1] = \"## Section 2\\n...\", etc.\n // Section N is rawParts[N-1] (0-based array, 1-indexed sections).\n // If body has preamble before first ##, rawParts[0] = preamble (section 0), rawParts[1] = section 1, etc.\n\n // Detect if there is a preamble (content before first ##)\n const hasPreamble = rawParts.length > 0 && !rawParts[0]!.startsWith('## ');\n // Section N → rawParts index: with preamble, index = N; without, index = N - 1\n const arrayIndex = hasPreamble ? parsed.index : parsed.index - 1;\n const sectionContent = rawParts[arrayIndex];\n const totalSections = hasPreamble ? rawParts.length - 1 : rawParts.length;\n\n if (!sectionContent) {\n return {\n pass: false,\n detail: `section ${parsed.index} not found (body has ${totalSections} sections)`,\n };\n }\n\n let actualCount: number;\n switch (parsed.itemType) {\n case 'checked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[x\\]/gim) || []).length;\n break;\n case 'unchecked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[ \\]/gim) || []).length;\n break;\n case 'listed-item':\n actualCount = (sectionContent.match(/^\\s*- /gm) || []).length;\n break;\n }\n\n const pass = applyCountOp(actualCount, parsed.count.op, parsed.count.n);\n const opStr = parsed.count.op === '>=' ? '≥' : parsed.count.op;\n const detail = pass\n ? `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`\n : `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`;\n\n return { pass, detail };\n}\n\nfunction applyCountOp(actual: number, op: '>=' | '==' | '>', n: number): boolean {\n switch (op) {\n case '>=': return actual >= n;\n case '==': return actual === n;\n case '>': return actual > n;\n }\n}\n\n// ─── File-exists evaluator ────────────────────────────────────────────────────\n\nfunction evalFileExists(\n parsed: Extract<ParsedPredicate, { kind: 'file-exists' }>,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const resolved = path.resolve(projectRoot, parsed.path);\n\n // Sandbox check: must be inside projectRoot\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n return {\n pass: false,\n detail: `path '${parsed.path}' resolves outside project root (sandbox violation)`,\n };\n }\n\n const exists = fs.existsSync(resolved);\n return {\n pass: exists,\n detail: exists ? `${parsed.path} exists` : `${parsed.path} not found`,\n };\n}\n\n// ─── Link-target-exists evaluator ────────────────────────────────────────────\n\nfunction evalLinkTargetExists(\n parsed: Extract<ParsedPredicate, { kind: 'link-target-exists' }>,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const projectRoot = opts?.projectRoot ?? process.cwd();\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n const found = indexContent.includes(`[[${parsed.id}]]`);\n return {\n pass: found,\n detail: found\n ? `[[${parsed.id}]] found in wiki index`\n : `[[${parsed.id}]] not found in wiki index`,\n };\n}\n\n// ─── Status-of evaluator ─────────────────────────────────────────────────────\n\nfunction evalStatusOf(\n parsed: Extract<ParsedPredicate, { kind: 'status-of' }>,\n opts: EvalOptions | undefined,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n // Find the raw path for this ID in the wiki index\n // Wiki index format: | [[STORY-003-13]] | story | Draft | .cleargate/delivery/... |\n const rowMatch = indexContent.match(\n new RegExp(`\\\\[\\\\[${parsed.id}\\\\]\\\\]\\\\s*\\\\|[^|]+\\\\|[^|]+\\\\|\\\\s*([^|\\\\n]+)`)\n );\n if (!rowMatch) {\n return { pass: false, detail: `[[${parsed.id}]] not found in wiki index` };\n }\n\n const rawPath = rowMatch[1]!.trim();\n const fullPath = path.resolve(projectRoot, rawPath);\n\n // Sandbox check\n if (!fullPath.startsWith(projectRoot)) {\n return { pass: false, detail: `wiki path for ${parsed.id} resolves outside project root` };\n }\n\n const linkedFm = readFrontmatterFromFile(fullPath);\n const status = linkedFm['status'];\n if (status === undefined) {\n return { pass: false, detail: `[[${parsed.id}]] has no status field` };\n }\n\n const pass = String(status).replace(/^[\"']|[\"']$/g, '') === parsed.value;\n return {\n pass,\n detail: pass\n ? `status-of([[${parsed.id}]]) == ${parsed.value}`\n : `status-of([[${parsed.id}]]) is '${status}', expected '${parsed.value}'`,\n };\n}\n","/**\n * STORY-008-02: Idempotent cached_gate_result frontmatter writer.\n * Reuses the shared frontmatter serializer from frontmatter-yaml.ts.\n *\n * writeCachedGate is byte-identical on re-run with identical inputs (same now + same result).\n *\n * Post-BUG-001: cached_gate_result is stored as a native YAML mapping\n * (parseFrontmatter returns it as an object). Legacy flow-style strings are\n * still accepted on read for backwards-compat with old files.\n */\n\nimport * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface CachedGate {\n pass: boolean;\n failing_criteria: { id: string; detail: string }[];\n last_gate_check: string;\n}\n\n/**\n * Read the cached_gate_result from a file's frontmatter.\n * Returns null if the key is absent or the file has no valid frontmatter.\n */\nexport async function readCachedGate(absPath: string): Promise<CachedGate | null> {\n let raw: string;\n try {\n raw = await fs.readFile(absPath, 'utf8');\n } catch {\n return null;\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n return null;\n }\n\n return coerceCachedGate(fm['cached_gate_result']);\n}\n\n/**\n * Write (or update) cached_gate_result in a file's frontmatter.\n * Idempotent: if the result deep-equals the existing cached value AND the same\n * now() is supplied, the file bytes are left untouched.\n *\n * @param absPath Absolute path to the markdown file.\n * @param result The gate result to cache.\n * @param opts Optional: inject `now` for test determinism.\n */\nexport async function writeCachedGate(\n absPath: string,\n result: CachedGate,\n opts?: { now?: () => Date }\n): Promise<void> {\n const nowFn = opts?.now ?? (() => new Date());\n const lastGateCheck = result.last_gate_check || toIsoSecond(nowFn());\n\n const newResult: CachedGate = {\n pass: result.pass,\n failing_criteria: result.failing_criteria,\n last_gate_check: lastGateCheck,\n };\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n throw new Error(`writeCachedGate: failed to parse frontmatter in ${absPath}`);\n }\n\n // Idempotency check: compare existing cached_gate_result\n const existing = coerceCachedGate(fm['cached_gate_result']);\n if (existing && JSON.stringify(existing) === JSON.stringify(newResult)) {\n return;\n }\n\n // Build new frontmatter: preserve all existing keys, inject/update cached_gate_result\n const newFm: Record<string, unknown> = {};\n let inserted = false;\n for (const [k, v] of Object.entries(fm)) {\n if (k === 'cached_gate_result') {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n inserted = true;\n } else {\n newFm[k] = v;\n }\n }\n if (!inserted) {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n }\n\n const fmBlock = serializeFrontmatter(newFm);\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Coerce a frontmatter value (native object or legacy flow-style string) into\n * a CachedGate. Returns null if absent or unrecognizable.\n */\nfunction coerceCachedGate(val: unknown): CachedGate | null {\n if (val === undefined || val === null) return null;\n\n // Native object (current format)\n if (typeof val === 'object' && !Array.isArray(val)) {\n const c = val as Record<string, unknown>;\n return {\n pass: Boolean(c['pass']),\n failing_criteria: Array.isArray(c['failing_criteria'])\n ? (c['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(c['last_gate_check'] ?? ''),\n };\n }\n\n // Legacy flow-style string \"{pass: true, ...}\" — parse via js-yaml\n if (typeof val === 'string' && val.startsWith('{')) {\n try {\n const parsed = yaml.load(val, { schema: yaml.CORE_SCHEMA });\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return {\n pass: Boolean(p['pass']),\n failing_criteria: Array.isArray(p['failing_criteria'])\n ? (p['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(p['last_gate_check'] ?? ''),\n };\n } catch {\n return null;\n }\n }\n\n return null;\n}\n","/**\n * gate-run.ts — `cleargate gate <name>` command handler.\n *\n * STORY-018-03: Config-driven gates. Runs a shell command configured in\n * `.cleargate/config.yml` under `gates.<name>`. Known gate names:\n * precommit, test, typecheck, lint.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n * FLASHCARD #cli #commander #optional-key: opts.strict undefined must be\n * checked with === true, not truthy check.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\nimport type { WikiConfig } from '../lib/wiki-config.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateRunCliOptions {\n cwd?: string;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n spawnFn?: typeof spawnSync;\n configLoader?: (repoRoot: string) => WikiConfig;\n}\n\nconst KNOWN_GATES = ['precommit', 'test', 'typecheck', 'lint'] as const;\ntype KnownGate = typeof KNOWN_GATES[number];\n\n// ─── gateRunHandler ───────────────────────────────────────────────────────────\n\n/**\n * Handle `cleargate gate <name>`.\n *\n * - Validates name is in KNOWN_GATES (exit 2 if not).\n * - Loads config via configLoader (defaults to loadWikiConfig from cwd).\n * - If gate not configured: friendly message + exit 0 (default) or exit 1 (--strict).\n * - If configured: spawnSync with shell:true, propagate exit code.\n */\nexport function gateRunHandler(\n name: string,\n opts: { strict?: boolean },\n cli?: GateRunCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n const configLoaderFn = cli?.configLoader ?? loadWikiConfig;\n\n // Validate gate name\n if (!(KNOWN_GATES as readonly string[]).includes(name)) {\n stderrFn(\n `unknown gate name '${name}' — must be one of: precommit, test, typecheck, lint`,\n );\n return exitFn(2);\n }\n\n // Load config\n const config = configLoaderFn(cwd);\n const cmd = config.gates[name as KnownGate];\n\n if (cmd == null) {\n const msg = `gate \"${name}\" not configured — add gates.${name} to .cleargate/config.yml (see cleargate-planning/.cleargate/config.example.yml)`;\n if (opts.strict === true) {\n stderrFn(msg);\n return exitFn(1);\n } else {\n stdoutFn(msg);\n return exitFn(0);\n }\n }\n\n // Run the configured command\n const result = spawnFn(cmd, { shell: true, stdio: 'inherit', cwd });\n\n if (result.error) {\n stderrFn(`[cleargate gate ${name}] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n return exitFn(result.status ?? 0);\n}\n","/**\n * sprint.ts — `cleargate sprint init|close|archive` command handlers.\n *\n * STORY-013-08: CLI wrappers for sprint lifecycle scripts.\n * STORY-014-08: sprintArchiveHandler — final sprint close-out.\n * STORY-015-04: stampSprintClose + rollback on wiki build/lint failure.\n *\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh\n * or orchestrate filesystem + git operations directly.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly —\n * always route through `run_script.sh`.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport { wikiBuildHandler } from './wiki-build.js';\nimport { wikiLintHandler } from './wiki-lint.js';\n\n// Terminal statuses — re-declared locally to avoid cross-module runtime import.\n// Keep in sync with TERMINAL_STATUSES in wiki-build.ts.\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface SprintCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Test seam: override wiki build invocation. Defaults to wikiBuildHandler. */\n wikiBuildFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n /** Test seam: override wiki lint invocation. Defaults to wikiLintHandler. */\n wikiLintFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n}\n\n// ─── Shared run_script.sh resolution ─────────────────────────────────────────\n\nfunction resolveRunScript(opts: SprintCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\n// ─── sprintInitHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint init <sprint-id> --stories <csv>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh init_sprint.mjs <sprint-id> --stories <csv>`\n */\nexport function sprintInitHandler(\n opts: { sprintId: string; stories: string },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['init_sprint.mjs', opts.sprintId, '--stories', opts.stories];\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint init] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── sprintCloseHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint close <sprint-id> [--assume-ack]`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh close_sprint.mjs <sprint-id> [--assume-ack]`\n */\nexport function sprintCloseHandler(\n opts: { sprintId: string; assumeAck?: boolean },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['close_sprint.mjs', opts.sprintId];\n // FLASHCARD #cli #commander #optional-key: omit the key when undefined; only\n // append --assume-ack when the flag was explicitly set (opts.assumeAck === true).\n if (opts.assumeAck === true) {\n args.push('--assume-ack');\n }\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint close] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── sprintArchiveHandler ─────────────────────────────────────────────────────\n\n/**\n * Parse just the frontmatter from a markdown file using js-yaml CORE_SCHEMA.\n * Returns { fm, body } where body is the content after the closing `---`.\n * FLASHCARD #cli #frontmatter #parse: body may start with blank line; we strip\n * one leading blank to match parseFrontmatter.ts convention.\n */\nfunction parseFileFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return { fm: {}, body: raw };\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return { fm: {}, body: raw };\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n if (yamlText.trim() === '') return { fm: {}, body };\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch {\n return { fm: {}, body };\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return { fm: {}, body };\n return { fm: parsed as Record<string, unknown>, body };\n}\n\n/**\n * Serialize frontmatter + body back to a markdown string.\n * Always adds blank separator between `---` and body.\n */\nfunction serializeFileContent(fm: Record<string, unknown>, body: string): string {\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---\\n\\n${body}`;\n}\n\n/**\n * Atomic write via tmp + rename (same pattern as story.ts / close_sprint.mjs).\n */\nfunction atomicWriteStr(filePath: string, content: string): void {\n const tmp = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmp, content, 'utf8');\n fs.renameSync(tmp, filePath);\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranchForArchive(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Stamp a file's frontmatter with status + completed_at.\n * Returns the stamped content string (does NOT write to disk).\n */\nfunction stampFile(raw: string, status: string, completedAt: string): string {\n const { fm, body } = parseFileFrontmatter(raw);\n fm['status'] = status;\n fm['completed_at'] = completedAt;\n return serializeFileContent(fm, body);\n}\n\n/**\n * STORY-015-04: Stamp the sprint file's frontmatter with status=\"Completed\"\n * (if not already terminal) and completed_at (if absent). Writes atomically.\n *\n * Returns:\n * previousContent — original file bytes for rollback\n * stampedContent — the content written to disk\n * didChange — false if file was already terminal + completed_at set\n */\nexport function stampSprintClose(\n sprintPath: string,\n now: () => string,\n): { previousContent: string; stampedContent: string; didChange: boolean } {\n const previousContent = fs.readFileSync(sprintPath, 'utf8');\n const { fm, body } = parseFileFrontmatter(previousContent);\n\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const alreadyTerminal = TERMINAL_STATUSES.has(currentStatus);\n const hasCompletedAt = typeof fm['completed_at'] === 'string' && fm['completed_at'].length > 0;\n\n if (alreadyTerminal && hasCompletedAt) {\n return { previousContent, stampedContent: previousContent, didChange: false };\n }\n\n if (!alreadyTerminal) {\n fm['status'] = 'Completed';\n }\n if (!hasCompletedAt) {\n fm['completed_at'] = now();\n }\n\n const stampedContent = serializeFileContent(fm, body);\n atomicWriteStr(sprintPath, stampedContent);\n return { previousContent, stampedContent, didChange: true };\n}\n\n/**\n * STORY-015-04: Atomically restore a sprint file from a previously-snapshotted\n * string (rollback after wiki build/lint failure).\n */\nexport function restoreSprintFile(sprintPath: string, original: string): void {\n atomicWriteStr(sprintPath, original);\n}\n\n/** Return state.json story keys that belong to a given epic. */\nfunction storyKeysForEpic(\n stateStories: Record<string, unknown>,\n epicId: string,\n): string[] {\n const epicNum = epicId.replace('EPIC-', '');\n return Object.keys(stateStories).filter((k) => k.startsWith(`STORY-${epicNum}-`));\n}\n\n/**\n * `cleargate sprint archive <sprint-id> [--dry-run]`\n *\n * v1: print inert message, exit 0.\n * v2:\n * 1. Read state.json — refuse if sprint_status !== 'Completed'.\n * 2. Resolve file set from sprint frontmatter + state.json + orphan scan.\n * 3. --dry-run: print plan; no writes.\n * 4. Live: stamp sprint file, wiki build+lint (if wiki initialised), then\n * move files, clear .active, git checkout main, merge, branch -d.\n */\nexport async function sprintArchiveHandler(\n opts: { sprintId: string; dryRun?: boolean },\n cli?: SprintCliOptions,\n): Promise<void> {\n try {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Wiki build/lint seam wrappers.\n // wikiBuildHandler on success returns (no exit call); on failure calls exit(1) which throws.\n // wikiLintHandler on success calls exit(0) which throws; on findings calls exit(1) which throws.\n const wikiBuildFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiBuildFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-build-exit:${code}`); };\n try {\n await wikiBuildHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-build-exit:0')) return;\n throw err;\n }\n });\n const wikiLintFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiLintFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-lint-exit:${code}`); };\n try {\n await wikiLintHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-lint-exit:0')) return;\n throw err;\n }\n });\n\n // Step 1: v1-inert check\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // Step 2: Read state.json — refuse if not Completed\n const stateFile = path.join(cwd, '.cleargate', 'sprint-runs', opts.sprintId, 'state.json');\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate sprint archive] state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n sprint_status?: string;\n stories?: Record<string, unknown>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate sprint archive] failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n if (state.sprint_status !== 'Completed') {\n stderrFn(\n `sprint not closed — run \\`cleargate sprint close ${opts.sprintId} --assume-ack\\` first`,\n );\n return exitFn(1);\n }\n\n const stateStories: Record<string, unknown> = state.stories ?? {};\n\n // Step 3: Resolve file set\n const pendingDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(cwd, '.cleargate', 'delivery', 'archive');\n\n // Sprint file\n let sprintFile: string | null = null;\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${opts.sprintId}_`) || entry === `${opts.sprintId}.md`) && entry.endsWith('.md')) {\n sprintFile = path.join(pendingDir, entry);\n break;\n }\n }\n\n // Sprint frontmatter → epics list\n let epicIds: string[] = [];\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { fm } = parseFileFrontmatter(fs.readFileSync(sprintFile, 'utf8'));\n const epics = fm['epics'];\n if (Array.isArray(epics)) {\n epicIds = epics.map(String);\n }\n }\n\n // Plan entries: { src, destName, status }\n interface FilePlan {\n src: string;\n destName: string;\n status: string;\n }\n const plan: FilePlan[] = [];\n\n if (sprintFile) {\n plan.push({\n src: sprintFile,\n destName: path.basename(sprintFile),\n status: 'Completed',\n });\n }\n\n for (const epicId of epicIds) {\n // Epic file\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${epicId}_`) || entry === `${epicId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Approved',\n });\n break;\n }\n }\n\n // Story files (authoritative: state.json keys for this epic)\n const storyKeys = storyKeysForEpic(stateStories, epicId);\n for (const storyId of storyKeys) {\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${storyId}_`) || entry === `${storyId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Done',\n });\n break;\n }\n }\n }\n }\n\n // Orphan scan: any STORY-*.md in pending-sync with parent_epic_ref matching\n // one of our epics but NOT in state.json keys.\n const storyIdsInState = new Set(Object.keys(stateStories));\n const planSrcs = new Set(plan.map((p) => p.src));\n const orphans: string[] = [];\n for (const entry of fs.readdirSync(pendingDir)) {\n if (!entry.startsWith('STORY-') || !entry.endsWith('.md')) continue;\n const candidate = path.join(pendingDir, entry);\n if (planSrcs.has(candidate)) continue;\n let raw: string;\n try {\n raw = fs.readFileSync(candidate, 'utf8');\n } catch { continue; }\n const { fm } = parseFileFrontmatter(raw);\n const parentRef = String(fm['parent_epic_ref'] ?? '');\n if (epicIds.includes(parentRef)) {\n // Derive story ID from filename (STORY-014-01_Something.md → STORY-014-01)\n const storyId = entry.replace(/\\.md$/, '').replace(/_.*$/, '');\n if (!storyIdsInState.has(storyId)) {\n orphans.push(candidate);\n stderrFn(`WARN: orphan story ${entry} matches epic ${parentRef} but is not in state.json — archiving anyway`);\n plan.push({ src: candidate, destName: entry, status: 'Done' });\n }\n }\n }\n\n // Step 4: --dry-run: print plan + exit 0\n const completedAt = new Date().toISOString();\n const sprintBranch = deriveSprintBranchForArchive(opts.sprintId);\n const activePath = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n\n if (opts.dryRun) {\n stdoutFn(`[dry-run] Sprint archive plan for ${opts.sprintId}:`);\n stdoutFn(` Sprint branch: ${sprintBranch}`);\n stdoutFn(` Files to archive (${plan.length}):`);\n for (const entry of plan) {\n stdoutFn(\n ` ${path.basename(entry.src)} → archive/${entry.destName} [stamp: status=${entry.status}, completed_at=<now>]`,\n );\n }\n if (orphans.length > 0) {\n stdoutFn(` Orphan files (${orphans.length}): ${orphans.map((o) => path.basename(o)).join(', ')}`);\n }\n stdoutFn(` .active → \"\" (truncate)`);\n stdoutFn(` git checkout main`);\n stdoutFn(` git merge --no-ff -m \"merge: ${sprintBranch} → main\" ${sprintBranch}`);\n stdoutFn(` git branch -d ${sprintBranch}`);\n return exitFn(0);\n }\n\n // Step 5a: Stamp the sprint file's frontmatter (status + completed_at)\n let sprintFileSnapshot: string | null = null;\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const wikiInitialised = fs.existsSync(wikiRoot);\n\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { previousContent } = stampSprintClose(sprintFile, () => completedAt);\n sprintFileSnapshot = previousContent;\n\n // Step 5b: wiki build + lint — only if wiki has been initialised.\n // Skip gracefully when .cleargate/wiki/ doesn't exist yet (wiki not built).\n if (wikiInitialised) {\n for (const [stepName, stepFn] of [\n ['wiki build', () => wikiBuildFn(cwd, stdoutFn)] as const,\n ['wiki lint', () => wikiLintFn(cwd, stdoutFn)] as const,\n ]) {\n try {\n await stepFn();\n } catch (err) {\n // Rollback sprint file frontmatter\n atomicWriteStr(sprintFile!, sprintFileSnapshot!);\n stderrFn(\n `[cleargate sprint archive] post-stamp ${stepName} failed — sprint frontmatter reverted`,\n );\n if (err instanceof Error) stderrFn(err.message);\n return exitFn(1);\n }\n }\n }\n }\n\n // Step 5c: Live run — stamp + move each file\n for (const entry of plan) {\n if (!fs.existsSync(entry.src)) {\n stderrFn(`[cleargate sprint archive] source not found: ${entry.src} — skipping`);\n continue;\n }\n const raw = fs.readFileSync(entry.src, 'utf8');\n const stamped = stampFile(raw, entry.status, completedAt);\n const dest = path.join(archiveDir, entry.destName);\n atomicWriteStr(entry.src, stamped);\n fs.renameSync(entry.src, dest);\n stdoutFn(`archived: ${entry.destName}`);\n }\n\n // Step 6: Truncate .active\n try {\n atomicWriteStr(activePath, '');\n } catch {\n // .active may not exist — not fatal\n }\n\n // Step 7: git checkout main\n const step7 = spawnFn('git', ['checkout', 'main'], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step7.error || (step7.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git checkout main failed`);\n if (step7.stderr) stderrFn(String(step7.stderr));\n return exitFn(step7.status ?? 1);\n }\n\n // Step 8: git merge --no-ff -m \"merge: sprint/<branch> → main\" sprint/<branch>\n const mergeMsg = `merge: ${sprintBranch} → main`;\n const step8 = spawnFn(\n 'git',\n ['merge', '--no-ff', '-m', mergeMsg, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step8.error || (step8.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git merge failed — run \\`git merge --abort\\` if needed`);\n if (step8.stderr) stderrFn(String(step8.stderr));\n return exitFn(step8.status ?? 1);\n }\n const mergeSha = String(step8.stdout ?? '').trim().split('\\n')[0] ?? '';\n\n // Step 9: git branch -d sprint/<branch>\n const step9 = spawnFn('git', ['branch', '-d', sprintBranch], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step9.error || (step9.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git branch -d ${sprintBranch} failed`);\n if (step9.stderr) stderrFn(String(step9.stderr));\n return exitFn(step9.status ?? 1);\n }\n\n stdoutFn(\n `archive complete: ${plan.length} files moved, branch ${sprintBranch} deleted` +\n (mergeSha ? `, merge SHA: ${mergeSha}` : ''),\n );\n return exitFn(0);\n } catch (e) {\n // The exitFn seam throws `Error(\"exit:<n>\")` as a synchronous control-flow\n // shortcut so the handler bails without running further steps. Under an\n // async handler that escapes as an unhandled rejection even when tests\n // `getCode()` confirms the expected exit. Swallow the sentinel here; any\n // other error re-throws.\n if (e instanceof Error && /^exit:\\d+$/.test(e.message)) return;\n throw e;\n }\n}\n","/**\n * story.ts — `cleargate story start|complete` command handlers.\n *\n * STORY-014-07: atomic orchestration of worktree + branch + merge + state.\n *\n * Both handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they orchestrate the full\n * worktree/merge sequence via `spawnSync` + a second-pass `state.json` write.\n *\n * `story start <story-id>`:\n * 1. `git worktree add <cwd>/.worktrees/<ID> -b story/<ID> <sprintBranch>`\n * 2. `bash run_script.sh update_state.mjs <ID> Bouncing`\n * 3. Re-read `state.json`, set `stories[<ID>].worktree` field, atomic write.\n *\n * `story complete <story-id>`:\n * 1. `git rev-list --count <sprintBranch>..story/<ID>` — if 0 → abort.\n * 2. `git -C <cwd> checkout <sprintBranch>`\n * 3. `git merge story/<ID> --no-ff -m \"merge: story/<ID> → <sprintBranch>\"`\n * — on conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n * 4. `git worktree remove .worktrees/<ID>`\n * 5. `git branch -d story/<ID>`\n * 6. `bash run_script.sh update_state.mjs <ID> Done`\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StoryCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID override for execution_mode discovery.\n * If absent, the handler reads `.cleargate/sprint-runs/.active`.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StoryCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranch(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Atomic write: tmp + rename. Inlined per plan (do not import from .mjs).\n * Caller passes the raw JSON-stringified text (or any string content).\n */\nfunction atomicWriteString(filePath: string, text: string): void {\n const tmpFile = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmpFile, text, 'utf8');\n fs.renameSync(tmpFile, filePath);\n}\n\n/**\n * Path to the sprint's state.json. The file may not exist yet under v2\n * when the sprint was just initialized — callers must guard.\n */\nfunction stateJsonPath(cwd: string, sprintId: string): string {\n return path.join(cwd, '.cleargate', 'sprint-runs', sprintId, 'state.json');\n}\n\n// ─── storyStartHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate story start <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 3-step spawn sequence + second-pass state.json mutation.\n */\nexport function storyStartHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve sprint ID: explicit override, else sentinel-fallback via .active.\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 3-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const worktreePath = path.join(cwd, '.worktrees', opts.storyId);\n const storyBranch = `story/${opts.storyId}`;\n\n // Step 1: git worktree add <path> -b story/<ID> <sprintBranch>\n // FLASHCARD (plan): flag order matters on macOS git — PATH before -b.\n const step1 = spawnFn(\n 'git',\n ['worktree', 'add', worktreePath, '-b', storyBranch, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n\n // Step 2: run_script.sh update_state.mjs <ID> Bouncing\n const runScript = resolveRunScript(cli ?? { cwd });\n const step2 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Bouncing'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: re-read state.json (fresh bytes AFTER update_state.mjs), set\n // stories[<ID>].worktree = \".worktrees/<ID>\", atomic write.\n const stateFile = stateJsonPath(cwd, sprintId);\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate story start] step 3: state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n stories?: Record<string, { worktree?: string | null } & Record<string, unknown>>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n state.stories = state.stories ?? {};\n const existing = state.stories[opts.storyId] ?? {\n state: 'Bouncing',\n qa_bounces: 0,\n arch_bounces: 0,\n worktree: null,\n updated_at: new Date().toISOString(),\n notes: '',\n };\n existing.worktree = `.worktrees/${opts.storyId}`;\n state.stories[opts.storyId] = existing;\n\n try {\n atomicWriteString(stateFile, JSON.stringify(state, null, 2) + '\\n');\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: atomic write failed: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n stdoutFn(`worktree created at .worktrees/${opts.storyId} on branch ${storyBranch}`);\n return exitFn(0);\n}\n\n// ─── storyCompleteHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate story complete <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 6-step orchestration (pre-flight, checkout, merge, worktree remove,\n * branch -d, update_state.mjs Done).\n */\nexport function storyCompleteHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 6-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const storyBranch = `story/${opts.storyId}`;\n const worktreeRel = path.join('.worktrees', opts.storyId);\n\n // Step 1: pre-flight rev-list — refuse if 0 commits on story branch.\n const step1 = spawnFn(\n 'git',\n ['rev-list', '--count', `${sprintBranch}..${storyBranch}`],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n const count = parseInt(String(step1.stdout ?? '0').trim(), 10);\n if (!Number.isFinite(count) || count <= 0) {\n stderrFn('no commits on story branch — nothing to merge');\n return exitFn(1);\n }\n\n // Step 2: checkout sprint branch.\n const step2 = spawnFn(\n 'git',\n ['-C', cwd, 'checkout', sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story complete] step 2 (checkout) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 2 (checkout ${sprintBranch}) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: merge story/<ID> --no-ff -m \"...\"\n // On conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n const mergeMsg = `merge: ${storyBranch} → ${sprintBranch}`;\n const step3 = spawnFn(\n 'git',\n ['merge', storyBranch, '--no-ff', '-m', mergeMsg],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step3.error) {\n stderrFn(`[cleargate story complete] step 3 (merge) error: ${step3.error.message}`);\n return exitFn(1);\n }\n if ((step3.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] merge conflict — run \\`git merge --abort\\` then re-run after resolution`);\n if (step3.stderr) stderrFn(String(step3.stderr));\n return exitFn(1);\n }\n\n // Step 4: git worktree remove .worktrees/<ID>\n const step4 = spawnFn(\n 'git',\n ['worktree', 'remove', worktreeRel],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step4.error) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove) error: ${step4.error.message}`);\n return exitFn(1);\n }\n if ((step4.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove ${worktreeRel}) failed with exit ${step4.status}`);\n if (step4.stderr) stderrFn(String(step4.stderr));\n return exitFn(step4.status ?? 1);\n }\n\n // Step 5: git branch -d story/<ID>\n const step5 = spawnFn(\n 'git',\n ['branch', '-d', storyBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step5.error) {\n stderrFn(`[cleargate story complete] step 5 (branch -d) error: ${step5.error.message}`);\n return exitFn(1);\n }\n if ((step5.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 5 (branch -d ${storyBranch}) failed with exit ${step5.status}`);\n if (step5.stderr) stderrFn(String(step5.stderr));\n return exitFn(step5.status ?? 1);\n }\n\n // Step 6: run_script.sh update_state.mjs <ID> Done\n const runScript = resolveRunScript(cli ?? { cwd });\n const step6 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Done'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step6.error) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) error: ${step6.error.message}`);\n return exitFn(1);\n }\n if ((step6.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) failed with exit ${step6.status}`);\n if (step6.stderr) stderrFn(String(step6.stderr));\n return exitFn(step6.status ?? 1);\n }\n\n stdoutFn(`merged ${storyBranch} → ${sprintBranch}; worktree + branch removed; state = Done`);\n return exitFn(0);\n}\n","/**\n * state.ts — `cleargate state update|validate` command handlers.\n *\n * STORY-013-08: CLI wrappers for state management scripts.\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StateCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID for execution_mode discovery. Required for state update/validate.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StateCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n// ─── stateUpdateHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate state update <story-id> <new-state>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh update_state.mjs <story-id> <new-state>`\n */\nexport function stateUpdateHandler(\n opts: { storyId: string; newState: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n // Sprint ID resolution order:\n // 1. Explicit --sprint <id> from CLI (cli?.sprintId)\n // 2. .cleargate/sprint-runs/.active sentinel\n // 3. Fall through to SPRINT-UNKNOWN (v1-inert)\n const cwd = cli?.cwd;\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, opts.newState],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state update] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── stateValidateHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate state validate <sprint-id>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh validate_state.mjs <sprint-id>`\n */\nexport function stateValidateHandler(\n opts: { sprintId: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'validate_state.mjs', opts.sprintId],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state validate] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * stamp-tokens.ts — STORY-008-05\n *\n * `cleargate stamp-tokens <file>` CLI command.\n * Reads token-ledger rows for a work item, aggregates per-session totals, and\n * stamps `draft_tokens:` into the file's YAML frontmatter.\n *\n * Hook-invoked. Idempotent within a session (last_stamp check), accumulative\n * across sessions (sessions[] array).\n *\n * No top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from '../lib/frontmatter-yaml.js';\nimport { readLedgerForWorkItem } from '../lib/ledger-reader.js';\nimport { detectWorkItemType } from '../lib/work-item-type.js';\nimport type { SessionBucket } from '../lib/ledger-reader.js';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport interface StampTokensCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n sprintRunsRoot?: string;\n}\n\nexport interface DraftTokensSession {\n session: string;\n model: string;\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n ts: string;\n}\n\nexport interface DraftTokens {\n input: number | null;\n output: number | null;\n cache_creation: number | null;\n cache_read: number | null;\n model: string | null;\n last_stamp: string;\n sessions: DraftTokensSession[];\n}\n\nexport async function stampTokensHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampTokensCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn =\n cli?.exit ??\n ((code: number) => {\n process.exit(code);\n });\n const nowFn = cli?.now ?? (() => new Date());\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Archive freeze: if path contains /.cleargate/delivery/archive/, noop\n if (/\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath)) {\n stdoutFn(`[frozen] ${absPath}`);\n exitFn(0);\n return;\n }\n\n // Read the file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absPath, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot read file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Parse frontmatter\n let fm: Record<string, unknown> = {};\n let body = '';\n\n const hasFrontmatter = rawContent.trimStart().startsWith('---');\n if (hasFrontmatter) {\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot parse frontmatter in: ${absPath}`);\n exitFn(1);\n return;\n }\n } else {\n body = rawContent;\n }\n\n // Extract work_item_id from frontmatter ID key or filename fallback\n const workItemId = extractWorkItemId(fm, absPath);\n if (!workItemId) {\n stdoutFn(`[stamp-tokens] error: cannot determine work_item_id from frontmatter or filename: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Read existing draft_tokens from frontmatter (parsed as native nested object\n // since parseFrontmatter now returns typed values)\n const existingDraftTokens = coerceDraftTokens(fm['draft_tokens']);\n const existingLastStamp = existingDraftTokens?.last_stamp ?? null;\n\n // Read ledger\n const buckets = readLedgerForWorkItem(workItemId, { sprintRunsRoot: cli?.sprintRunsRoot });\n\n // Idempotency check: if all ledger rows are older than last_stamp, and we\n // already have sessions, no-op.\n if (existingLastStamp && buckets.length > 0) {\n const allRowsOlderThanLastStamp = buckets.every((bucket) =>\n bucket.rows.every((row) => row.ts < existingLastStamp),\n );\n if (allRowsOlderThanLastStamp && existingDraftTokens !== null) {\n // No new rows since last stamp — no-op\n exitFn(0);\n return;\n }\n }\n\n const nowIso = toIsoSecond(nowFn());\n\n let newFm: Record<string, unknown>;\n let stampError: string | undefined;\n\n if (buckets.length === 0) {\n // Missing ledger: write all-null draft_tokens + stamp_error\n stampError = `no ledger rows for work_item_id ${workItemId}`;\n const nullTokens: DraftTokens = {\n input: null,\n output: null,\n cache_creation: null,\n cache_read: null,\n model: null,\n last_stamp: nowIso,\n sessions: [],\n };\n newFm = buildNewFrontmatter(fm, nullTokens, stampError);\n } else {\n // Aggregate across all sessions\n const tokens = aggregateBuckets(buckets, nowIso);\n newFm = buildNewFrontmatter(fm, tokens, undefined);\n // Remove stale stamp_error if ledger now has rows\n delete newFm['stamp_error'];\n }\n\n const serialized = buildSerializedContent(newFm, body);\n\n if (opts.dryRun) {\n // Print planned diff without writing\n stdoutFn(`[dry-run] stamp-tokens would write draft_tokens for ${workItemId}:`);\n const draftTokensVal = newFm['draft_tokens'];\n stdoutFn(` draft_tokens: ${JSON.stringify(draftTokensVal)}`);\n if (stampError) {\n stdoutFn(` stamp_error: \"${stampError}\"`);\n }\n exitFn(0);\n return;\n }\n\n // Write the file\n try {\n fs.writeFileSync(absPath, serialized, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot write file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n stdoutFn(`[stamped] ${absPath} (${workItemId})`);\n exitFn(0);\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Extract the work_item_id from frontmatter ID fields or filename regex fallback.\n */\nfunction extractWorkItemId(fm: Record<string, unknown>, absPath: string): string | null {\n // Try frontmatter ID keys in order\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id'];\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim() !== '') {\n return val.trim();\n }\n }\n\n // Fallback: extract from filename\n const basename = path.basename(absPath);\n const match = basename.match(/^(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/i);\n if (match) {\n return match[0].toUpperCase();\n }\n\n // Also try detectWorkItemType for any prefix match\n const typeFromPath = detectWorkItemType(absPath);\n if (typeFromPath) {\n // Try to find the ID segment in the basename\n const idMatch = basename.match(/((?:STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(?:-\\d+)?)/i);\n if (idMatch) {\n return idMatch[1].toUpperCase();\n }\n }\n\n return null;\n}\n\n/**\n * Coerce the existing draft_tokens value from frontmatter into a DraftTokens shape.\n * Handles two on-disk shapes:\n * 1. Native nested YAML map (current format, parsed by js-yaml as an object)\n * 2. Legacy JSON-in-a-string, from pre-fix files written before BUG-001\n */\nfunction coerceDraftTokens(val: unknown): DraftTokens | null {\n if (val == null) return null;\n\n if (typeof val === 'object' && !Array.isArray(val)) {\n const o = val as Record<string, unknown>;\n return {\n input: typeof o['input'] === 'number' ? o['input'] : null,\n output: typeof o['output'] === 'number' ? o['output'] : null,\n cache_creation: typeof o['cache_creation'] === 'number' ? o['cache_creation'] : null,\n cache_read: typeof o['cache_read'] === 'number' ? o['cache_read'] : null,\n model: typeof o['model'] === 'string' ? o['model'] : null,\n last_stamp: typeof o['last_stamp'] === 'string' ? o['last_stamp'] : '',\n sessions: Array.isArray(o['sessions']) ? (o['sessions'] as DraftTokensSession[]) : [],\n };\n }\n\n if (typeof val === 'string') {\n try {\n const parsed = JSON.parse(val) as DraftTokens;\n return parsed;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Aggregate ledger buckets into a DraftTokens object.\n * model: comma-joined sorted unique models across all buckets' rows.\n * sessions[]: one entry per bucket, using bucket totals.\n */\nexport function aggregateBuckets(buckets: SessionBucket[], nowIso: string): DraftTokens {\n let totalInput = 0;\n let totalOutput = 0;\n let totalCacheCreation = 0;\n let totalCacheRead = 0;\n\n const uniqueModels = new Set<string>();\n const sessions: DraftTokensSession[] = [];\n\n for (const bucket of buckets) {\n totalInput += bucket.totals.input;\n totalOutput += bucket.totals.output;\n totalCacheCreation += bucket.totals.cache_creation;\n totalCacheRead += bucket.totals.cache_read;\n\n // Collect unique models from rows and derive session model\n const sessionModels = new Set<string>();\n for (const row of bucket.rows) {\n if (row.model) {\n uniqueModels.add(row.model);\n sessionModels.add(row.model);\n }\n }\n\n // Session model: comma-joined unique models for this session\n const sessionModel = Array.from(sessionModels).sort().join(', ');\n\n sessions.push({\n session: bucket.session_id,\n model: sessionModel,\n input: bucket.totals.input,\n output: bucket.totals.output,\n cache_read: bucket.totals.cache_read,\n cache_creation: bucket.totals.cache_creation,\n ts: bucket.rows[0]?.ts ?? '',\n });\n }\n\n const model = Array.from(uniqueModels).sort().join(', ') || null;\n\n return {\n input: totalInput,\n output: totalOutput,\n cache_creation: totalCacheCreation,\n cache_read: totalCacheRead,\n model,\n last_stamp: nowIso,\n sessions,\n };\n}\n\n/**\n * Build the new frontmatter record with draft_tokens as a native nested\n * object. serializeFrontmatter (js-yaml) emits it as a block-style YAML map.\n */\nfunction buildNewFrontmatter(\n existingFm: Record<string, unknown>,\n tokens: DraftTokens,\n stampError: string | undefined,\n): Record<string, unknown> {\n const newFm: Record<string, unknown> = {};\n\n // Preserve all existing keys except draft_tokens and stamp_error (we'll re-add)\n for (const [k, v] of Object.entries(existingFm)) {\n if (k !== 'draft_tokens' && k !== 'stamp_error') {\n newFm[k] = v;\n }\n }\n\n if (stampError) {\n newFm['stamp_error'] = stampError;\n }\n\n // Store as a plain object; serializer emits block-style YAML\n newFm['draft_tokens'] = tokens as unknown as Record<string, unknown>;\n\n return newFm;\n}\n\n/**\n * Build the final file content: frontmatter block + body.\n */\nfunction buildSerializedContent(fm: Record<string, unknown>, body: string): string {\n const fmBlock = serializeFrontmatter(fm);\n if (body.length > 0) {\n return `${fmBlock}\\n\\n${body}`;\n }\n return `${fmBlock}\\n`;\n}\n\n","/**\n * ledger-reader.ts — STORY-008-04\n *\n * Read-only library for scanning token-ledger.jsonl files across all sprint-run\n * directories and grouping rows by session for per-work-item cost attribution.\n *\n * Node built-ins only. No runtime deps.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface LedgerRow {\n ts: string;\n sprint_id: string;\n agent_type: string;\n /** Populated only for STORY-* items (backward compat; empty string for non-story items) */\n story_id: string;\n /** Always populated when detection succeeded. Equals story_id for STORY items. */\n work_item_id: string;\n session_id: string;\n transcript: string;\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n model: string;\n turns: number;\n}\n\nexport interface SessionBucket {\n session_id: string;\n rows: LedgerRow[];\n totals: {\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n turns: number;\n };\n}\n\nexport interface ReadLedgerOptions {\n /** ISO timestamp string; rows with ts < since are excluded */\n since?: string;\n /**\n * Root directory for sprint-runs/. Defaults to\n * <repo_root>/.cleargate/sprint-runs where repo_root is resolved by\n * walking up from cwd to find .cleargate/.\n * Override in tests to avoid touching the real repo.\n */\n sprintRunsRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Walk up from cwd until we find a directory containing `.cleargate/sprint-runs/`.\n * Returns the sprint-runs path or null if not found.\n */\nfunction findSprintRunsRoot(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const candidate = path.join(dir, '.cleargate', 'sprint-runs');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n}\n\nfunction normalizeRow(raw: Record<string, unknown>): LedgerRow {\n // work_item_id may be absent in pre-STORY-008-04 rows; default from story_id.\n const story_id = typeof raw['story_id'] === 'string' ? raw['story_id'] : '';\n const work_item_id =\n typeof raw['work_item_id'] === 'string' && raw['work_item_id'] !== ''\n ? raw['work_item_id']\n : story_id;\n\n return {\n ts: typeof raw['ts'] === 'string' ? raw['ts'] : '',\n sprint_id: typeof raw['sprint_id'] === 'string' ? raw['sprint_id'] : '',\n agent_type: typeof raw['agent_type'] === 'string' ? raw['agent_type'] : 'unknown',\n story_id,\n work_item_id,\n session_id: typeof raw['session_id'] === 'string' ? raw['session_id'] : '',\n transcript: typeof raw['transcript'] === 'string' ? raw['transcript'] : '',\n input: typeof raw['input'] === 'number' ? raw['input'] : 0,\n output: typeof raw['output'] === 'number' ? raw['output'] : 0,\n cache_creation: typeof raw['cache_creation'] === 'number' ? raw['cache_creation'] : 0,\n cache_read: typeof raw['cache_read'] === 'number' ? raw['cache_read'] : 0,\n model: typeof raw['model'] === 'string' ? raw['model'] : '',\n turns: typeof raw['turns'] === 'number' ? raw['turns'] : 0,\n };\n}\n\nfunction rowMatchesWorkItem(row: LedgerRow, workItemId: string): boolean {\n return row.work_item_id === workItemId || row.story_id === workItemId;\n}\n\nfunction buildBucket(session_id: string, rows: LedgerRow[]): SessionBucket {\n const totals = rows.reduce(\n (acc, r) => ({\n input: acc.input + r.input,\n output: acc.output + r.output,\n cache_creation: acc.cache_creation + r.cache_creation,\n cache_read: acc.cache_read + r.cache_read,\n turns: acc.turns + r.turns,\n }),\n { input: 0, output: 0, cache_creation: 0, cache_read: 0, turns: 0 }\n );\n return { session_id, rows, totals };\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Scan all sprint-runs/<sprint>/token-ledger.jsonl files and return rows\n * matching the given workItemId, grouped by session_id.\n *\n * Rows are compared by work_item_id first, then story_id (for backward compat\n * with pre-STORY-008-04 rows that lack work_item_id).\n *\n * Pre-fix rows (missing work_item_id) default work_item_id from story_id.\n *\n * @param workItemId e.g. \"STORY-008-04\", \"EPIC-008\", \"PROPOSAL-042\"\n * @param opts optional filters and path overrides\n * @returns array of SessionBucket, one per unique session_id, sorted\n * by the ts of the earliest row in each bucket\n */\nexport function readLedgerForWorkItem(\n workItemId: string,\n opts: ReadLedgerOptions = {}\n): SessionBucket[] {\n // Resolve sprint-runs root\n let sprintRunsRoot: string;\n if (opts.sprintRunsRoot) {\n sprintRunsRoot = opts.sprintRunsRoot;\n } else {\n const found = findSprintRunsRoot(process.cwd());\n if (!found) {\n return [];\n }\n sprintRunsRoot = found;\n }\n\n if (!fs.existsSync(sprintRunsRoot)) {\n return [];\n }\n\n // Collect all token-ledger.jsonl files across all sprint dirs\n let ledgerFiles: string[];\n try {\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n ledgerFiles = entries\n .filter((e) => e.isDirectory())\n .map((e) => path.join(sprintRunsRoot, e.name, 'token-ledger.jsonl'))\n .filter((f) => fs.existsSync(f));\n } catch {\n return [];\n }\n\n // Parse matching rows from each file\n const matchingRows: LedgerRow[] = [];\n\n for (const ledgerFile of ledgerFiles) {\n let content: string;\n try {\n content = fs.readFileSync(ledgerFile, 'utf-8');\n } catch {\n continue;\n }\n\n const lines = content.split('\\n').filter((l) => l.trim() !== '');\n for (const line of lines) {\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(line) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n const row = normalizeRow(raw);\n\n // Apply since filter\n if (opts.since && row.ts < opts.since) {\n continue;\n }\n\n if (rowMatchesWorkItem(row, workItemId)) {\n matchingRows.push(row);\n }\n }\n }\n\n // Group by session_id\n const sessionMap = new Map<string, LedgerRow[]>();\n for (const row of matchingRows) {\n const key = row.session_id || '(unknown-session)';\n const existing = sessionMap.get(key);\n if (existing) {\n existing.push(row);\n } else {\n sessionMap.set(key, [row]);\n }\n }\n\n // Build buckets and sort by earliest row ts within each bucket\n const buckets = Array.from(sessionMap.entries()).map(([session_id, rows]) => {\n rows.sort((a, b) => a.ts.localeCompare(b.ts));\n return buildBucket(session_id, rows);\n });\n\n // Sort buckets by earliest row ts\n buckets.sort((a, b) => {\n const aTs = a.rows[0]?.ts ?? '';\n const bTs = b.rows[0]?.ts ?? '';\n return aTs.localeCompare(bTs);\n });\n\n return buckets;\n}\n\n","/**\n * upgrade.ts — STORY-009-05\n *\n * `cleargate upgrade [--dry-run] [--yes] [--only <tier>]`\n *\n * Three-way merge driver: walks each tracked scaffold file, classifies its\n * drift state, routes by overwrite_policy, and applies the user-chosen merge\n * action. Snapshot is updated atomically after every successfully handled file\n * so the command is resumable (re-run continues from the last clean state).\n *\n * Special-case handling:\n * - CLAUDE.md tier (cli-config): uses claude-md-surgery to merge only the\n * CLEARGATE:START…CLEARGATE:END bounded block, leaving user prose intact.\n * - settings.json tier (cli-config): uses settings-json-surgery to merge\n * only ClearGate-owned hooks, leaving user hooks intact.\n *\n * CRITICAL: all file mutations are atomic (write .tmp → fs.rename).\n * INCREMENTAL: each file is independent. Failure on file N does not roll back\n * file N-1. Re-running re-classifies against the updated snapshot.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n * Inject `promptMergeChoice` + `openInEditor` via `UpgradeCliOptions` for tests\n * (FLASHCARD #cli #determinism #test-seam).\n */\n\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n type ManifestFile,\n type ManifestEntry,\n type DriftMap,\n type Tier,\n} from '../lib/manifest.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { readBlock, writeBlock } from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\nimport {\n promptMergeChoice as defaultPromptMergeChoice,\n type MergeChoice,\n} from '../lib/merge-ui.js';\nimport {\n openInEditor as defaultOpenInEditor,\n containsConflictMarkers,\n} from '../lib/editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface UpgradeCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: override the package root for loadPackageManifest. */\n packageRoot?: string;\n /** Test seam: inject a custom promptMergeChoice (avoids interactive stdin). */\n promptMergeChoice?: typeof defaultPromptMergeChoice;\n /** Test seam: inject a custom openInEditor (avoids forking real editors). */\n openInEditor?: typeof defaultOpenInEditor;\n /** Test seam: inject a custom stdin for promptMergeChoice. */\n stdin?: NodeJS.ReadableStream;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/** Write file atomically: write to .tmp, then rename. Never leaves partial writes. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = filePath + '.tmp.' + Date.now();\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/**\n * Update a single file's sha256 in the install snapshot atomically.\n * Reads the snapshot file, patches the matching entry, and re-writes atomically.\n */\nasync function updateSnapshotEntry(\n projectRoot: string,\n filePath: string,\n newSha: string | null\n): Promise<void> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n let snapshot: ManifestFile;\n\n try {\n const raw = await fsp.readFile(snapshotPath, 'utf-8');\n snapshot = JSON.parse(raw) as ManifestFile;\n } catch {\n // If no snapshot exists yet, cannot update — silently skip.\n return;\n }\n\n const updated: ManifestFile = {\n ...snapshot,\n files: snapshot.files.map((entry) =>\n entry.path === filePath ? { ...entry, sha256: newSha } : entry\n ),\n };\n\n await writeAtomic(snapshotPath, JSON.stringify(updated, null, 2) + '\\n');\n}\n\n/**\n * Determine if this is a CLAUDE.md file (needs block surgery).\n */\nfunction isClaudeMd(filePath: string): boolean {\n return path.basename(filePath) === 'CLAUDE.md';\n}\n\n/**\n * Determine if this is a settings.json file (needs hook surgery).\n */\nfunction isSettingsJson(filePath: string): boolean {\n return path.basename(filePath) === 'settings.json' && filePath.includes('.claude');\n}\n\n/**\n * Merge-3way handler: always-policy — overwrite file with package content.\n * Used for `always` overwrite_policy files.\n */\nasync function applyAlwaysOverwrite(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n stdout: (s: string) => void\n): Promise<void> {\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n try {\n const pkgContent = await fsp.readFile(sourcePath, 'utf-8');\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, pkgContent);\n await updateSnapshotEntry(projectRoot, entry.path, entry.sha256);\n stdout(`[always] overwritten: ${entry.path}`);\n } catch (err) {\n stdout(`[always] error overwriting ${entry.path}: ${(err as Error).message}`);\n }\n}\n\n/**\n * Merge-3way handler for `merge-3way` overwrite_policy files.\n * Supports k/t/e choices with conflict-marker semantics for 'e'.\n * Returns the post-merge sha or null if skipped/failed.\n */\nasync function applyMerge3Way(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n installSha: string | null,\n currentSha: string | null,\n flags: { yes?: boolean; dryRun?: boolean },\n opts: {\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n promptMergeChoiceFn: typeof defaultPromptMergeChoice;\n openInEditorFn: typeof defaultOpenInEditor;\n stdin?: NodeJS.ReadableStream;\n }\n): Promise<{ updated: boolean; newSha: string | null }> {\n const { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin } = opts;\n\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n // Read current (ours) and package (theirs) content\n let ours = '';\n let theirs = '';\n\n try {\n ours = await fsp.readFile(targetPath, 'utf-8');\n } catch {\n // File missing on disk — treat as empty\n ours = '';\n }\n\n try {\n theirs = await fsp.readFile(sourcePath, 'utf-8');\n } catch {\n // Package file missing — skip\n stdout(`[merge] skip: package file not found for ${entry.path}`);\n return { updated: false, newSha: null };\n }\n\n // Compute drift state for display\n const state = classify(entry.sha256, installSha, currentSha, entry.tier);\n\n let choice: MergeChoice;\n\n if (flags.yes) {\n // --yes: auto-take-theirs\n choice = 't';\n stdout(`[yes] taking theirs: ${entry.path} state=${state}`);\n } else {\n // Interactive prompt\n choice = await promptMergeChoiceFn({\n path: entry.path,\n state,\n ours,\n theirs,\n stdin,\n stdout,\n });\n }\n\n if (choice === 'k') {\n // Keep mine: file unchanged, snapshot records current_sha as installed_sha\n stdout(`[keep] ${entry.path}`);\n await updateSnapshotEntry(projectRoot, entry.path, currentSha);\n return { updated: true, newSha: currentSha };\n }\n\n if (choice === 't') {\n // Take theirs: apply CLAUDE.md or settings.json surgery if needed, else raw overwrite\n let mergedContent = theirs;\n\n if (isClaudeMd(entry.path)) {\n // Merge only the bounded block; preserve user prose\n try {\n const ourBlock = readBlock(ours);\n const theirBlock = readBlock(theirs);\n if (ourBlock !== null && theirBlock !== null) {\n mergedContent = writeBlock(ours, theirBlock);\n } else if (theirBlock !== null) {\n // No block in ours — full overwrite\n mergedContent = theirs;\n }\n // If no block in theirs, skip (no ClearGate content to take)\n } catch {\n // Surgery failed — fall back to full overwrite\n mergedContent = theirs;\n }\n } else if (isSettingsJson(entry.path)) {\n // Merge only ClearGate-owned hooks; preserve user hooks\n try {\n const ourSettings = JSON.parse(ours) as ClaudeSettings;\n const theirSettings = JSON.parse(theirs) as ClaudeSettings;\n // Remove ClearGate hooks from ours, then add ClearGate hooks from theirs\n const withoutOurCg = removeClearGateHooks(ourSettings);\n // Build merged: user hooks from ours + ClearGate hooks from theirs\n const cgHooks = theirSettings.hooks ?? {};\n const merged: ClaudeSettings = { ...withoutOurCg };\n if (Object.keys(cgHooks).length > 0) {\n merged.hooks = { ...(withoutOurCg.hooks ?? {}), ...cgHooks };\n }\n mergedContent = JSON.stringify(merged, null, 2) + '\\n';\n } catch {\n // Surgery failed — full overwrite\n mergedContent = theirs;\n }\n }\n\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, mergedContent);\n const newSha = hashNormalized(mergedContent);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[take] ${entry.path}`);\n return { updated: true, newSha };\n }\n\n // choice === 'e': write conflict-marker file + open editor\n const mergeFilePath = targetPath + '.cleargate-merge';\n const conflictContent =\n `<<<<<<< ours (installed)\\n${ours}=======\\n${theirs}>>>>>>> theirs (upstream)\\n`;\n\n await fsp.mkdir(path.dirname(mergeFilePath), { recursive: true });\n await writeAtomic(mergeFilePath, conflictContent);\n\n try {\n const result = await openInEditorFn(mergeFilePath);\n if (result.exitCode !== 0) {\n stderr(`[edit] editor exited with code ${result.exitCode}; markers may remain in ${mergeFilePath}`);\n }\n } catch (err) {\n stderr(`[edit] could not open editor: ${(err as Error).message}`);\n stderr(`[edit] resolve markers manually in: ${mergeFilePath}`);\n return { updated: false, newSha: null };\n }\n\n // Read post-edit content\n let edited = '';\n try {\n edited = await fsp.readFile(mergeFilePath, 'utf-8');\n } catch {\n stderr(`[edit] could not read ${mergeFilePath} after editor exit`);\n return { updated: false, newSha: null };\n }\n\n if (containsConflictMarkers(edited)) {\n stderr(`[edit] unresolved conflict markers remain in ${mergeFilePath}`);\n stderr(`[edit] file NOT updated. Resolve manually and re-run upgrade.`);\n // Leave .cleargate-merge in place for manual resolution\n return { updated: false, newSha: null };\n }\n\n // Markers resolved — overwrite target file, remove merge file\n await writeAtomic(targetPath, edited);\n try {\n await fsp.unlink(mergeFilePath);\n } catch {\n // Non-fatal if cleanup fails\n }\n\n const newSha = hashNormalized(edited);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[edit] resolved: ${entry.path}`);\n return { updated: true, newSha };\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function upgradeHandler(\n flags: { dryRun?: boolean; yes?: boolean; only?: string },\n cli?: UpgradeCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n const promptMergeChoiceFn = cli?.promptMergeChoice ?? defaultPromptMergeChoice;\n const openInEditorFn = cli?.openInEditor ?? defaultOpenInEditor;\n const stdin = cli?.stdin;\n\n // ─── 1. Load manifests + compute drift ──────────────────────────────────────\n\n let pkgManifest: ManifestFile;\n try {\n pkgManifest = loadPackageManifest({ packageRoot: cli?.packageRoot });\n } catch (err) {\n stderr(`[upgrade] ${(err as Error).message}`);\n exit(1);\n return;\n }\n\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // Build a lookup from the snapshot\n const snapshotByPath = new Map<string, string | null>();\n for (const entry of installSnapshot?.files ?? []) {\n snapshotByPath.set(entry.path, entry.sha256);\n }\n\n // ─── 2. Apply --only <tier> filter ──────────────────────────────────────────\n\n const onlyTier: Tier | undefined = flags.only as Tier | undefined;\n const filteredFiles = onlyTier\n ? pkgManifest.files.filter((e) => e.tier === onlyTier)\n : pkgManifest.files;\n\n // ─── 3. Classify all files ──────────────────────────────────────────────────\n\n interface FileWork {\n entry: ManifestEntry;\n currentSha: string | null;\n installSha: string | null;\n action: 'overwrite' | 'skip' | 'merge-3way';\n }\n\n const workItems: FileWork[] = [];\n\n await Promise.all(\n filteredFiles.map(async (entry) => {\n if (entry.tier === 'user-artifact') {\n // Always skip user-artifact tier (never tracked)\n return;\n }\n\n const currentSha = await computeCurrentSha(entry, cwd);\n const installSha = snapshotByPath.get(entry.path) ?? null;\n\n let action: FileWork['action'];\n switch (entry.overwrite_policy) {\n case 'always':\n action = 'overwrite';\n break;\n case 'skip':\n case 'preserve':\n action = 'skip';\n break;\n case 'merge-3way':\n default:\n action = 'merge-3way';\n break;\n }\n\n workItems.push({ entry, currentSha, installSha, action });\n })\n );\n\n // Sort for deterministic output\n workItems.sort((a, b) => a.entry.path.localeCompare(b.entry.path));\n\n // ─── 4. --dry-run: print plan and exit ──────────────────────────────────────\n\n if (flags.dryRun) {\n let count = 0;\n for (const item of workItems) {\n const state = classify(item.entry.sha256, item.installSha, item.currentSha, item.entry.tier);\n stdout(`[dry-run] ${item.entry.path} action=${item.action} state=${state}`);\n count++;\n }\n stdout(`[dry-run] ${count} files planned. No changes made.`);\n return;\n }\n\n // ─── 5. Execute per file ─────────────────────────────────────────────────────\n\n // Determine package root for reading source files.\n // cli.packageRoot is the test seam (always injected in tests).\n // In production, use the same default resolution as loadPackageManifest.\n // Since we already loaded the manifest above (which resolves the path internally),\n // we simply use cli.packageRoot when provided, otherwise fall back to cwd\n // (in production the actual resolution is inside loadPackageManifest).\n const packageRoot = cli?.packageRoot ?? cwd;\n\n const driftMap: DriftMap = {};\n\n for (const item of workItems) {\n const { entry, currentSha, installSha, action } = item;\n\n switch (action) {\n case 'skip': {\n // never / preserve — no prompt, no write\n stdout(`[skip] ${entry.path} policy=${entry.overwrite_policy}`);\n break;\n }\n\n case 'overwrite': {\n // always — overwrite silently\n await applyAlwaysOverwrite(entry, cwd, packageRoot, stdout);\n break;\n }\n\n case 'merge-3way': {\n // Interactive 3-way merge (unless --yes)\n await applyMerge3Way(\n entry,\n cwd,\n packageRoot,\n installSha,\n currentSha,\n { yes: flags.yes, dryRun: false },\n { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin }\n );\n break;\n }\n }\n\n // Re-compute current sha after potential mutation (for drift map)\n const postSha = await computeCurrentSha(entry, cwd);\n driftMap[entry.path] = {\n state: classify(entry.sha256, installSha, postSha, entry.tier),\n entry,\n install_sha: installSha,\n current_sha: postSha,\n package_sha: entry.sha256,\n };\n }\n\n // ─── 6. Refresh .drift-state.json ────────────────────────────────────────────\n\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n stdout('[upgrade] complete.');\n}\n","export const CLEARGATE_START = '<!-- CLEARGATE:START -->';\nexport const CLEARGATE_END = '<!-- CLEARGATE:END -->';\n\n// IMPORTANT: regex MUST be GREEDY ([\\s\\S]* not [\\s\\S]*?)\n// The block body itself may reference both markers in prose (FLASHCARD 2026-04-19 #init #inject-claude-md #regex).\n// Non-greedy would stop at the first inline END marker in prose, cutting off the real block.\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->([\\s\\S]*)<!-- CLEARGATE:END -->/;\n\n/**\n * Returns the content between CLEARGATE:START and CLEARGATE:END markers.\n * Returns null if either marker is missing.\n */\nexport function readBlock(content: string): string | null {\n const match = BLOCK_REGEX.exec(content);\n if (!match) return null;\n return match[1];\n}\n\n/**\n * Replaces the content between CLEARGATE:START and CLEARGATE:END markers,\n * preserving the markers themselves and all surrounding content.\n * Throws if markers are missing.\n */\nexport function writeBlock(content: string, newBlockBody: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, `${CLEARGATE_START}${newBlockBody}${CLEARGATE_END}`);\n}\n\n/**\n * Removes both markers AND the content between them, leaving surrounding content intact.\n * Throws if markers are missing.\n */\nexport function removeBlock(content: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, '');\n}\n","export interface HookCommand {\n type: 'command';\n command: string;\n if?: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n}\n\nexport interface ClaudeSettings {\n hooks?: {\n PostToolUse?: HookEntry[];\n SessionStart?: HookEntry[];\n SubagentStop?: HookEntry[];\n [k: string]: HookEntry[] | undefined;\n };\n [k: string]: unknown;\n}\n\n/**\n * Returns true if the given command string belongs to ClearGate.\n * Matches:\n * - .claude/hooks/token-ledger.sh\n * - .claude/hooks/stamp-and-gate.sh\n * - .claude/hooks/session-start.sh\n * - .claude/hooks/wiki-ingest.sh (legacy)\n * - .claude/hooks/cleargate-*.sh (catch-all for future hooks)\n * - inline commands containing 'wiki ingest' (legacy SPRINT-04 PostToolUse inline)\n */\nfunction isClearGateCommand(command: string): boolean {\n if (command.includes('wiki ingest')) return true;\n return /\\/\\.claude\\/hooks\\/(token-ledger|stamp-and-gate|session-start|wiki-ingest|cleargate-[^/]*)\\.sh/.test(command);\n}\n\n/**\n * Removes ClearGate-owned hook entries from a ClaudeSettings object.\n * - Removes individual inner `hooks[]` sub-entries whose `command` matches ClearGate patterns.\n * - If the parent HookEntry's `hooks[]` array becomes empty, removes that HookEntry.\n * - If an event-category array (e.g. hooks.PostToolUse) becomes empty, removes the key.\n * - Preserves all other hook entries and all other top-level keys.\n */\nexport function removeClearGateHooks(settings: ClaudeSettings): ClaudeSettings {\n if (!settings.hooks) return { ...settings };\n\n const newHooks: NonNullable<ClaudeSettings['hooks']> = {};\n\n for (const [eventName, entries] of Object.entries(settings.hooks)) {\n if (!entries) continue;\n\n const filteredEntries: HookEntry[] = [];\n\n for (const entry of entries) {\n if (!entry.hooks || entry.hooks.length === 0) {\n // No inner hooks — keep as-is (not a ClearGate entry)\n filteredEntries.push(entry);\n continue;\n }\n\n const remainingInnerHooks = entry.hooks.filter(\n (h) => !isClearGateCommand(h.command)\n );\n\n if (remainingInnerHooks.length === 0) {\n // All inner hooks were ClearGate — drop this HookEntry entirely\n continue;\n }\n\n if (remainingInnerHooks.length === entry.hooks.length) {\n // No change — keep original entry reference\n filteredEntries.push(entry);\n } else {\n // Some removed — keep entry with remaining inner hooks\n filteredEntries.push({ ...entry, hooks: remainingInnerHooks });\n }\n }\n\n if (filteredEntries.length > 0) {\n newHooks[eventName] = filteredEntries;\n }\n // If filteredEntries is empty, skip the key entirely\n }\n\n const result: ClaudeSettings = { ...settings };\n\n if (Object.keys(newHooks).length > 0) {\n result.hooks = newHooks;\n } else {\n delete result.hooks;\n }\n\n return result;\n}\n\n/**\n * Returns true if settings contains any ClearGate-owned hook entries.\n */\nexport function hasClearGateHooks(settings: ClaudeSettings): boolean {\n if (!settings.hooks) return false;\n\n for (const entries of Object.values(settings.hooks)) {\n if (!entries) continue;\n for (const entry of entries) {\n if (!entry.hooks) continue;\n for (const h of entry.hooks) {\n if (isClearGateCommand(h.command)) return true;\n }\n }\n }\n\n return false;\n}\n","/**\n * merge-ui.ts — STORY-009-05\n *\n * Diff renderer + 3-choice interactive prompt for `cleargate upgrade`.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { createPatch } from 'diff';\nimport type { DriftState } from './manifest.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeChoice = 'k' | 't' | 'e';\n\n// ─── Diff renderer ────────────────────────────────────────────────────────────\n\n/**\n * Render an inline unified diff of `ours` vs `theirs` for the given `filePath`.\n * Uses the `diff` npm package's `createPatch`.\n */\nexport function renderInlineDiff(ours: string, theirs: string, filePath: string): string {\n return createPatch(filePath, ours, theirs, 'installed', 'upstream');\n}\n\n// ─── Prompt ───────────────────────────────────────────────────────────────────\n\n/**\n * Print file info + inline diff, then prompt the user for a merge choice.\n * Returns one of: 'k' (keep mine), 't' (take theirs), 'e' (edit in $EDITOR).\n *\n * Test seam: pass `stdin` to drive from a buffer instead of process.stdin.\n * Test seam: pass `stdout` to capture output instead of process.stdout.write.\n */\nexport async function promptMergeChoice(opts: {\n path: string;\n state: DriftState;\n ours: string;\n theirs: string;\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n}): Promise<MergeChoice> {\n const { path: filePath, state, ours, theirs } = opts;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stdin = opts.stdin ?? process.stdin;\n\n // Print file header\n stdout(`\\n[merge] ${filePath} state=${state}\\n`);\n\n // Print diff\n const patch = renderInlineDiff(ours, theirs, filePath);\n stdout(patch + '\\n');\n\n // Prompt\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR: ');\n\n return new Promise<MergeChoice>((resolve, reject) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n const answer = buf.slice(0, newline).trim().toLowerCase();\n stdin.removeListener('data', onData);\n stdin.removeListener('error', onError);\n if (answer === 'k' || answer === 't' || answer === 'e') {\n resolve(answer as MergeChoice);\n } else {\n // Default to 'k' (keep mine) for unrecognised input\n stdout(`Unknown choice '${answer}'; defaulting to [k]eep mine.\\n`);\n resolve('k');\n }\n }\n };\n\n const onError = (err: Error) => {\n stdin.removeListener('data', onData);\n reject(err);\n };\n\n stdin.on('data', onData);\n stdin.once('error', onError);\n });\n}\n","/**\n * editor.ts — STORY-009-05\n *\n * Safely spawn $EDITOR for the `cleargate upgrade` edit-in-editor flow.\n * Uses child_process.spawn with stdio: 'inherit' — does NOT use execSync\n * (would block the event loop and prevent test injection).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { spawn } from 'node:child_process';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Spawn $EDITOR for `filePath`, waiting for the editor process to exit.\n *\n * Returns the editor's exit code.\n * If `opts.editor` is provided, uses it instead of $EDITOR.\n * If neither is set, returns `{ exitCode: -1 }` with an error (handled by caller).\n *\n * Uses `stdio: 'inherit'` so the terminal is fully connected to the editor.\n * Do NOT use execSync — it blocks the event loop and breaks tests.\n */\nexport async function openInEditor(\n filePath: string,\n opts?: { editor?: string; env?: NodeJS.ProcessEnv }\n): Promise<{ exitCode: number }> {\n const env = opts?.env ?? process.env;\n const editor = opts?.editor ?? env['EDITOR'] ?? env['VISUAL'];\n\n if (!editor) {\n throw new Error('$EDITOR not set; cannot [e]dit option. Set the EDITOR environment variable.');\n }\n\n return new Promise<{ exitCode: number }>((resolve, reject) => {\n const child = spawn(editor, [filePath], {\n stdio: 'inherit',\n env: { ...env },\n });\n\n child.on('error', (err) => {\n reject(new Error(`Failed to start editor '${editor}': ${err.message}`));\n });\n\n child.on('close', (code) => {\n resolve({ exitCode: code ?? 0 });\n });\n });\n}\n\n/**\n * Return true if `content` contains unresolved conflict markers.\n *\n * Checks for any of:\n * <<<<<<< ours\n * =======\n * >>>>>>> theirs\n */\nexport function containsConflictMarkers(content: string): boolean {\n return (\n content.includes('<<<<<<< ours') ||\n content.includes('>>>>>>> theirs') ||\n // Also catch generic git-style markers in case user merged manually\n /^<<<<<<< /m.test(content) ||\n /^>>>>>>> /m.test(content)\n );\n}\n","/**\n * uninstall.ts — STORY-009-07\n *\n * `cleargate uninstall [--dry-run] [--preserve <tiers>] [--remove <tiers>] [--yes] [--path <dir>] [--force]`\n *\n * Most-destructive command in the CLI. Preservation-first: user artifacts\n * (FLASHCARD, archive, pending-sync, sprint retrospectives) are kept by default.\n * Framework files (knowledge, templates, wiki, hook-log, agents, hooks, skills)\n * are removed. CLAUDE.md and settings.json are surgically stripped — the rest\n * of those files is left intact.\n *\n * Safety rails:\n * - Typed confirmation: user must type the project name (or pass --yes).\n * - Uncommitted-changes check: git status --porcelain on manifest-tracked files.\n * - --dry-run: preview only, zero disk writes.\n * - Single-target: does NOT recurse into nested .cleargate/ directories.\n * - Idempotency: if .uninstalled marker exists and .install-manifest.json is absent → \"already uninstalled\".\n *\n * All test-facing behaviours are injectable via UninstallOptions seams.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport {\n loadInstallSnapshot,\n type ManifestFile,\n type ManifestEntry,\n type Tier,\n} from '../lib/manifest.js';\nimport {\n removeBlock,\n CLEARGATE_START,\n CLEARGATE_END,\n} from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/** Tiers that belong to \"user-artifact\" bucket (preserved by default). */\nconst USER_ARTIFACT_TIERS: Tier[] = ['user-artifact'];\n\n/** Framework tiers that are removed by default. */\nconst FRAMEWORK_TIERS: Tier[] = ['protocol', 'template', 'agent', 'hook', 'skill', 'cli-config', 'derived'];\n\nexport interface UninstallOptions {\n cwd?: string;\n path?: string; // resolved target dir (defaults to cwd)\n dryRun?: boolean;\n yes?: boolean; // skip typed confirmation\n force?: boolean; // override uncommitted-changes refusal\n preserve?: string[]; // tier ids to force-preserve (comma-split at CLI level)\n remove?: string[]; // tier ids to force-remove (comma-split at CLI level, 'all' = all tiers)\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: prompt for typed project-name confirmation. */\n promptName?: () => Promise<string>;\n /** Test seam: prompt yes/no for interactive category decisions. */\n promptYesNo?: (q: string) => Promise<boolean>;\n /** Test seam: injectable clock. */\n now?: () => Date;\n /** Test seam: git status --porcelain runner. Returns { stdout, code }. */\n git?: (args: string[]) => { stdout: string; code: number };\n}\n\nexport interface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n removed: string[];\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse a tier list string (comma-separated) into a Set.\n * 'all' expands to all known tiers.\n */\nfunction parseTierList(raw: string[]): Set<Tier> {\n const result = new Set<Tier>();\n for (const item of raw) {\n for (const t of item.split(',')) {\n const tier = t.trim();\n if (tier === 'all') {\n for (const f of FRAMEWORK_TIERS) result.add(f);\n for (const u of USER_ARTIFACT_TIERS) result.add(u);\n } else {\n result.add(tier as Tier);\n }\n }\n }\n return result;\n}\n\n/**\n * Determine if an entry should be preserved (true) or removed (false).\n *\n * Rules (highest priority first):\n * 1. --remove tier override → remove\n * 2. --preserve tier override → preserve\n * 3. user-artifact tier → preserve (default)\n * 4. Framework tiers (protocol/template/agent/hook/skill/cli-config/derived) → remove (default)\n */\nexport function shouldPreserve(\n entry: ManifestEntry,\n preserveSet: Set<Tier>,\n removeSet: Set<Tier>\n): boolean {\n if (removeSet.has(entry.tier)) return false;\n if (preserveSet.has(entry.tier)) return true;\n if (USER_ARTIFACT_TIERS.includes(entry.tier)) return true;\n return false;\n}\n\n/**\n * Read the project name from package.json (name field), or fall back to\n * the basename of the target directory.\n */\nfunction resolveProjectName(target: string): string {\n const pkgPath = path.join(target, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf-8');\n const parsed = JSON.parse(raw) as { name?: string };\n if (parsed.name && typeof parsed.name === 'string') {\n return parsed.name;\n }\n } catch {\n // Fall through to basename\n }\n }\n return path.basename(target);\n}\n\n/**\n * Run git status --porcelain in the target directory, filtered to manifest-tracked paths.\n * Returns the list of uncommitted files that overlap with the manifest.\n *\n * Non-git targets return an empty array (skip silently per orchestrator decision).\n */\nfunction detectUncommittedChanges(\n target: string,\n manifestPaths: string[],\n gitRunner?: UninstallOptions['git']\n): string[] {\n const run = gitRunner ?? ((args: string[]) => {\n try {\n const out = execSync(['git', ...args].join(' '), {\n cwd: target,\n stdio: ['pipe', 'pipe', 'pipe'],\n encoding: 'utf-8',\n });\n return { stdout: out, code: 0 };\n } catch (e: unknown) {\n const err = e as { stdout?: string; status?: number };\n return { stdout: err.stdout ?? '', code: err.status ?? 1 };\n }\n });\n\n // First check if this is a git repo at all\n const isGit = run(['-C', target, 'rev-parse', '--is-inside-work-tree']);\n if (isGit.code !== 0) {\n // Not a git repo — skip silently (orchestrator decision)\n return [];\n }\n\n const result = run(['-C', target, 'status', '--porcelain']);\n if (result.code !== 0) {\n return [];\n }\n\n // Parse porcelain output — each line: XY path\n const changedFiles = result.stdout\n .split('\\n')\n .filter(Boolean)\n .map((line) => line.slice(3).trim());\n\n // Only flag files that are listed in the install manifest\n const manifestSet = new Set(manifestPaths);\n return changedFiles.filter((f) => manifestSet.has(f));\n}\n\n/**\n * Remove @cleargate/cli from package.json dependencies/devDependencies.\n * Writes back if modified. Returns true if modified.\n */\nasync function removeFromPackageJson(target: string, dryRun: boolean): Promise<boolean> {\n const pkgPath = path.join(target, 'package.json');\n if (!fs.existsSync(pkgPath)) return false;\n\n let raw: string;\n try {\n raw = await fsp.readFile(pkgPath, 'utf-8');\n } catch {\n return false;\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n let modified = false;\n\n for (const key of ['dependencies', 'devDependencies'] as const) {\n const deps = parsed[key];\n if (deps && typeof deps === 'object' && '@cleargate/cli' in (deps as Record<string, unknown>)) {\n const updated = { ...(deps as Record<string, unknown>) };\n delete updated['@cleargate/cli'];\n parsed[key] = updated;\n modified = true;\n }\n }\n\n if (modified && !dryRun) {\n await fsp.writeFile(pkgPath, JSON.stringify(parsed, null, 2) + '\\n', 'utf-8');\n }\n\n return modified;\n}\n\n/** Atomically write a file (tmp → rename). */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/** Remove a file, silently ignoring missing files. */\nasync function removeFile(filePath: string): Promise<void> {\n try {\n await fsp.unlink(filePath);\n } catch {\n // Ignore ENOENT and other errors\n }\n}\n\n/** Remove a directory tree, silently ignoring missing directories. */\nasync function removeDir(dirPath: string): Promise<void> {\n try {\n fs.rmSync(dirPath, { recursive: true, force: true });\n } catch {\n // Ignore\n }\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function uninstallHandler(opts: UninstallOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = opts.exit ?? ((code: number) => process.exit(code) as never);\n const now = opts.now ?? (() => new Date());\n const dryRun = opts.dryRun ?? false;\n const yes = opts.yes ?? false;\n const force = opts.force ?? false;\n\n // Parse tier overrides\n const preserveSet = parseTierList(opts.preserve ?? []);\n const removeSet = parseTierList(opts.remove ?? []);\n const removeAll = (opts.remove ?? []).some((r) => r === 'all');\n if (removeAll) {\n for (const t of FRAMEWORK_TIERS) removeSet.add(t);\n for (const u of USER_ARTIFACT_TIERS) removeSet.add(u);\n }\n\n // ─── 1. Resolve target ────────────────────────────────────────────────────────\n\n const target = opts.path ? path.resolve(opts.path) : cwd;\n const cleargateDir = path.join(target, '.cleargate');\n const manifestPath = path.join(cleargateDir, '.install-manifest.json');\n const uninstalledPath = path.join(cleargateDir, '.uninstalled');\n\n // ─── 2. Missing manifest → no install detected ────────────────────────────────\n\n if (!fs.existsSync(manifestPath)) {\n // Check idempotency: if .uninstalled exists without manifest → already done\n if (fs.existsSync(uninstalledPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 3. Idempotency short-circuit ────────────────────────────────────────────\n\n if (fs.existsSync(uninstalledPath) && !fs.existsSync(manifestPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n\n // ─── 4. Load install manifest ────────────────────────────────────────────────\n\n const snapshot: ManifestFile | null = await loadInstallSnapshot(target);\n if (!snapshot) {\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 5. Uncommitted-changes check ────────────────────────────────────────────\n\n if (!force) {\n const manifestPaths = snapshot.files.map((e) => e.path);\n const uncommitted = detectUncommittedChanges(target, manifestPaths, opts.git);\n if (uncommitted.length > 0) {\n stderr(\n `Uncommitted changes to tracked files: ${uncommitted.slice(0, 5).join(', ')}${uncommitted.length > 5 ? ` and ${uncommitted.length - 5} more` : ''}. Commit, stash, or pass --force.`\n );\n exit(1);\n return;\n }\n }\n\n // ─── 6. CLAUDE.md marker check ───────────────────────────────────────────────\n\n const claudeMdPath = path.join(target, 'CLAUDE.md');\n let claudeMdContent: string | null = null;\n\n if (fs.existsSync(claudeMdPath)) {\n claudeMdContent = fs.readFileSync(claudeMdPath, 'utf-8');\n if (!claudeMdContent.includes(CLEARGATE_START)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n exit(1);\n return;\n }\n if (!claudeMdContent.includes(CLEARGATE_END)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n exit(1);\n return;\n }\n }\n\n // ─── 7. Classify entries ──────────────────────────────────────────────────────\n\n const toRemove: ManifestEntry[] = [];\n const toPreserve: ManifestEntry[] = [];\n const toSkip: ManifestEntry[] = []; // missing from disk\n\n for (const entry of snapshot.files) {\n const filePath = path.join(target, entry.path);\n if (!fs.existsSync(filePath)) {\n toSkip.push(entry);\n continue;\n }\n if (shouldPreserve(entry, preserveSet, removeSet)) {\n toPreserve.push(entry);\n } else {\n toRemove.push(entry);\n }\n }\n\n // ─── 8. Preview summary ───────────────────────────────────────────────────────\n\n stdout(`Will remove ${toRemove.length} files, keep ${toPreserve.length} files, update CLAUDE.md to strip CLEARGATE block, remove @cleargate/cli from package.json.`);\n\n if (dryRun) {\n stdout('');\n stdout('[dry-run] Planned removals:');\n for (const e of toRemove) {\n stdout(` [remove] ${e.path}`);\n }\n stdout('');\n stdout('[dry-run] Planned preservations:');\n for (const e of toPreserve) {\n stdout(` [keep] ${e.path}`);\n }\n if (toSkip.length > 0) {\n stdout('');\n stdout('[dry-run] Untracked (already missing on disk):');\n for (const e of toSkip) {\n stdout(` [skip] ${e.path}`);\n }\n }\n stdout('');\n stdout('[dry-run] No files changed.');\n exit(0);\n return;\n }\n\n // ─── 9. Typed confirmation (skipped with --yes) ───────────────────────────────\n\n if (!yes) {\n const projectName = resolveProjectName(target);\n\n const promptNameFn = opts.promptName ?? (() => {\n // Real interactive prompt — readline\n return new Promise<string>((resolve) => {\n process.stdout.write(`Type the project name \"${projectName}\" to confirm uninstall: `);\n let buf = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.once('data', (chunk: Buffer | string) => {\n buf = chunk.toString().trim();\n resolve(buf);\n });\n });\n });\n\n const typed = await promptNameFn();\n if (typed !== projectName) {\n stdout('name mismatch — aborting');\n exit(1);\n return;\n }\n }\n\n // ─── 10. Execute removal ──────────────────────────────────────────────────────\n\n const removedPaths: string[] = [];\n const preservedPaths: string[] = [];\n\n // Remove classified entries\n for (const entry of toRemove) {\n const filePath = path.join(target, entry.path);\n await removeFile(filePath);\n removedPaths.push(entry.path);\n }\n\n // Collect preserved paths\n for (const entry of toPreserve) {\n preservedPaths.push(entry.path);\n }\n\n // Surgery: CLAUDE.md — strip only the CLEARGATE block\n if (claudeMdContent !== null) {\n try {\n const stripped = removeBlock(claudeMdContent);\n await writeAtomic(claudeMdPath, stripped);\n removedPaths.push('CLAUDE.md (CLEARGATE block)');\n } catch (err) {\n stderr(`Warning: could not strip CLAUDE.md block: ${(err as Error).message}`);\n }\n }\n\n // Surgery: settings.json — remove ClearGate hooks only\n const settingsPath = path.join(target, '.claude', 'settings.json');\n if (fs.existsSync(settingsPath)) {\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as ClaudeSettings;\n const cleaned = removeClearGateHooks(settings);\n await writeAtomic(settingsPath, JSON.stringify(cleaned, null, 2) + '\\n');\n removedPaths.push('.claude/settings.json (ClearGate hooks)');\n } catch (err) {\n stderr(`Warning: could not update settings.json: ${(err as Error).message}`);\n }\n }\n\n // Remove @cleargate/cli from package.json\n const pkgModified = await removeFromPackageJson(target, false);\n if (pkgModified) {\n removedPaths.push('package.json (@cleargate/cli dep)');\n stdout('Removed @cleargate/cli from package.json. Run `npm install` to update package-lock.json.');\n }\n\n // Remove the install manifest itself (and drift-state)\n await removeFile(manifestPath);\n await removeFile(path.join(cleargateDir, '.drift-state.json'));\n\n // ─── 11. Write .uninstalled marker ───────────────────────────────────────────\n\n const marker: UninstalledMarker = {\n uninstalled_at: now().toISOString(),\n prior_version: snapshot.cleargate_version,\n preserved: preservedPaths,\n removed: removedPaths,\n };\n\n // Ensure .cleargate/ dir still exists (may have had files removed from it)\n await fsp.mkdir(cleargateDir, { recursive: true });\n await writeAtomic(uninstalledPath, JSON.stringify(marker, null, 2) + '\\n');\n\n // ─── 12. Empty .cleargate/ cleanup ───────────────────────────────────────────\n\n // Per story Gherkin scenario 11: when --remove all removes all user-artifact\n // items too (nothing preserved inside .cleargate/), the directory itself is\n // removed (including the .uninstalled marker we just wrote).\n // In the default case (preservations exist), we leave .cleargate/ with the\n // preserved files + .uninstalled marker.\n if (removeAll) {\n const hasPreservedInsideCleargate = preservedPaths.some((p) =>\n p.startsWith('.cleargate/')\n );\n if (!hasPreservedInsideCleargate) {\n // Remove the entire .cleargate/ directory (including the marker we wrote)\n await removeDir(cleargateDir);\n }\n }\n\n // ─── 13. Restore hint ────────────────────────────────────────────────────────\n\n if (preservedPaths.length > 0) {\n stdout(`Preserved ${preservedPaths.length} items. Run cleargate init in this directory to restore.`);\n }\n}\n","/**\n * sync.ts — STORY-010-04\n *\n * `cleargate sync` — canonical 6-step driver:\n * 1. Identity + sprint resolve\n * 2. List remote updates + pull each item\n * (3. STORY-010-05 insertion point: runIntakeBranch)\n * 4. Classify conflicts per local work items\n * 5. Resolve (merge prompt / silent merge / remote-wins / refuse)\n * 6. Apply + log\n *\n * R2 invariant: ALL pulls complete before ANY push begins.\n *\n * --dry-run: steps 1–4 only; zero fs writes; zero sync-log entries.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { classify } from '../lib/conflict-detector.js';\nimport type { SinceLastSync, LocalSnapshot, RemoteSnapshot } from '../lib/conflict-detector.js';\nimport { promptThreeWayMerge } from '../lib/merge-helper.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteUpdateRef } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { runIntakeBranch } from '../lib/intake.js';\nimport type { IntakeResult } from '../lib/intake.js';\nimport { resolveActiveItems } from '../lib/active-criteria.js';\nimport type { LocalWorkItemRef } from '../lib/active-criteria.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\nimport type { RemoteComment } from '../lib/mcp-client.js';\n\n// ── Public Options ─────────────────────────────────────────────────────────────\n\nexport interface SyncOptions {\n dryRun?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (bypasses acquireAccessToken) */\n mcp?: McpClient;\n /** Test seam: override process.stdin for merge prompt */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── SyncCheckOptions ──────────────────────────────────────────────────────────\n\nexport interface SyncCheckOptions {\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n/**\n * syncCheckHandler — `cleargate sync --check`\n *\n * Hook-safe read-only drift probe. Exits 0 on ALL failure paths.\n * Emits a single JSON line to stdout: {\"updates\":<N>,\"since\":\"<iso>\"} on success\n * or {\"updates\":0,\"error\":\"<msg>\"} on failure.\n *\n * Never writes sync-log entries, never touches .conflicts.json, never pushes.\n * Updates .cleargate/.sync-marker.json with last_check timestamp (even on error,\n * to throttle repeat attempts).\n *\n * FLASHCARD: #hook-safe #sync-check #exit-code\n * syncHandler exits 2 on missing token/URL/adapter. This handler MUST NOT.\n */\nexport async function syncCheckHandler(opts: SyncCheckOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n const markerPath = path.join(projectRoot, '.cleargate', '.sync-marker.json');\n\n // Helper: write marker atomically, even on error (throttle repeat calls)\n const updateMarker = async (nowIso: string): Promise<void> => {\n try {\n const content = JSON.stringify({ last_check: nowIso });\n await fsPromises.mkdir(path.dirname(markerPath), { recursive: true });\n const tmpPath = `${markerPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, markerPath);\n } catch {\n // Ignore marker write failures — do not surface to caller\n }\n };\n\n // Helper: emit error JSON and exit 0\n const emitError = async (msg: string, nowIso: string): Promise<void> => {\n stdout(JSON.stringify({ updates: 0, error: msg.slice(0, 200) }) + '\\n');\n await updateMarker(nowIso);\n };\n\n const nowIso = nowFn();\n\n // Read last_check from marker; fall back to epoch\n let since = '1970-01-01T00:00:00.000Z';\n try {\n const raw = await fsPromises.readFile(markerPath, 'utf8');\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n if (typeof parsed['last_check'] === 'string') {\n since = parsed['last_check'];\n }\n } catch {\n // No marker file or parse error — use epoch\n }\n\n // Resolve MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n // Acquire token — hook-safe: any AcquireError → emitError, exit 0\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Pre-flight: adapter info (but don't exit 2 — emit error JSON instead)\n try {\n const adapterInfo = await mcp.adapterInfo();\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n } catch {\n // If adapterInfo fails, proceed — older MCP may not have this tool\n }\n\n // Call list_remote_updates\n let refs: RemoteUpdateRef[];\n try {\n refs = await mcp.call<RemoteUpdateRef[]>('cleargate_list_remote_updates', { since });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n await emitError(msg, nowIso);\n return;\n }\n\n // Success path\n stdout(JSON.stringify({ updates: refs.length, since }) + '\\n');\n await updateMarker(nowIso);\n}\n\n// ── ConflictJson shape ────────────────────────────────────────────────────────\n\nexport interface ConflictEntry {\n item_id: string;\n remote_id: string;\n state: string;\n resolution: string;\n reason: string;\n local_path: string;\n}\n\nexport interface ConflictsJson {\n generated_at: string;\n sprint_id: string;\n unresolved: ConflictEntry[];\n}\n\n// ── Main handler ──────────────────────────────────────────────────────────────\n\nexport async function syncHandler(opts: SyncOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const dryRun = opts.dryRun ?? false;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // ── Step 1: Identity + sprint resolve ───────────────────────────────────────\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintRoot);\n\n // ── MCP client setup ────────────────────────────────────────────────────────\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n if (err.code === 'token_revoked') {\n stderr(`Error: ${err.message}\\n`);\n } else if (err.code === 'no_stored_token' || err.code === 'invalid_token') {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${err.message}\\n`);\n }\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Pre-flight: adapter-info probe ──────────────────────────────────────────\n let adapterInfo: { configured: boolean; name: string };\n try {\n adapterInfo = await mcp.adapterInfo();\n } catch {\n // Tool may not exist on older MCP versions — warn and proceed\n adapterInfo = { configured: true, name: 'unknown' };\n }\n\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n stderr(\n 'Error: ClearGate MCP has no PM adapter configured (LINEAR_API_KEY missing server-side). ' +\n 'Sync cannot proceed.\\n',\n );\n exit(2);\n return;\n }\n\n // ── Step 2: List remote updates + pull ──────────────────────────────────────\n // Read last_remote_sync from wiki meta or use epoch\n const wikiMetaPath = path.join(projectRoot, '.cleargate', 'wiki', 'meta.json');\n let lastRemoteSync = '1970-01-01T00:00:00.000Z';\n try {\n const metaRaw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n const meta = JSON.parse(metaRaw) as Record<string, unknown>;\n if (typeof meta['last_remote_sync'] === 'string') {\n lastRemoteSync = meta['last_remote_sync'];\n }\n } catch {\n // No meta file — start from epoch\n }\n\n const remoteRefs = await mcp.call<RemoteUpdateRef[]>(\n 'cleargate_list_remote_updates',\n { since: lastRemoteSync },\n );\n\n // Pull each item — all awaited BEFORE any push (R2 invariant)\n const pulled: RemoteItem[] = [];\n for (const ref of remoteRefs) {\n const item = await mcp.call<RemoteItem | null>(\n 'cleargate_pull_item',\n { remote_id: ref.remote_id },\n );\n if (item !== null) {\n pulled.push(item);\n }\n }\n\n // ── Step 3: Stakeholder proposal intake (STORY-010-05) ──────────────────────\n const labelFilter = (opts.env ?? process.env)['CLEARGATE_PROPOSAL_LABEL'] ?? 'cleargate:proposal';\n let intakeResult: IntakeResult = { created: 0, items: [] };\n try {\n intakeResult = await runIntakeBranch({\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter,\n now: nowFn,\n });\n } catch (err) {\n // Non-fatal: intake errors do not abort the main sync loop\n stderr(`warn: intake branch failed: ${String(err)}\\n`);\n }\n\n // Emit R10 warning if present\n if (intakeResult.warning) {\n stderr(`${intakeResult.warning}\\n`);\n }\n\n // ── Step 3b: Pull comments for active items (STORY-010-06) ─────────────────\n // Load all local items first (needed for active-criteria + wiki render).\n const localItems = await scanLocalItems(projectRoot);\n\n if (!dryRun) {\n const localRefs: LocalWorkItemRef[] = localItems.map(({ fm }) => ({\n primaryId: getItemId(fm),\n remoteId: typeof fm['remote_id'] === 'string' && fm['remote_id'] ? fm['remote_id'] : undefined,\n lastRemoteUpdate: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : undefined,\n }));\n\n const activeSet = await resolveActiveItems(projectRoot, localRefs, nowFn);\n\n for (const remoteId of activeSet) {\n try {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n await renderCommentsSection({ projectRoot, remoteId, comments, localItems });\n } catch (err: unknown) {\n // R4 mitigation: per-item try/catch — do NOT break the outer loop\n const errMsg = err instanceof Error ? err.message : String(err);\n if (/MCP HTTP 429/.test(errMsg)) {\n // Find primary ID for logging\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'skipped-rate-limit',\n detail: '429',\n });\n } else {\n // Other errors: log as error-transport, continue\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'error-transport',\n detail: errMsg.slice(0, 200),\n });\n }\n }\n }\n }\n\n // ── Step 4: Classify local work items ───────────────────────────────────────\n // localItems already loaded above for comment-pull; reuse here.\n const conflictsJson: ConflictEntry[] = [];\n\n // Maps for tracking what to apply\n type QueuedPull = { item: RemoteItem; localPath: string; fm: Record<string, unknown>; body: string };\n type QueuedPush = { localPath: string; fm: Record<string, unknown>; body: string; itemId: string };\n\n const pullQueue: QueuedPull[] = [];\n const pushQueue: QueuedPush[] = [];\n\n const pulledByRemoteId = new Map<string, RemoteItem>();\n for (const item of pulled) {\n pulledByRemoteId.set(item.remote_id, item);\n }\n\n let dryRunPulls = 0;\n let dryRunPushes = 0;\n let dryRunConflicts = 0;\n\n for (const { localPath, fm, body } of localItems) {\n const remoteIdVal = fm['remote_id'];\n if (typeof remoteIdVal !== 'string' || !remoteIdVal) continue;\n\n const remoteItem = pulledByRemoteId.get(remoteIdVal);\n if (!remoteItem) continue;\n\n const lastBodySha = typeof fm['last_synced_body_sha'] === 'string' ? fm['last_synced_body_sha'] : null;\n const localBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n\n const localSnap: LocalSnapshot = {\n updated_at: typeof fm['updated_at'] === 'string' ? fm['updated_at'] : '1970-01-01T00:00:00Z',\n body_sha: localBodySha,\n status: typeof fm['status'] === 'string' ? fm['status'] : '',\n deleted: false,\n };\n\n const remoteSnap: RemoteSnapshot = {\n updated_at: remoteItem.updated_at,\n body_sha: remoteBodySha,\n status: remoteItem.status,\n deleted: false,\n };\n\n const since: SinceLastSync = {\n last_pushed_at: typeof fm['pushed_at'] === 'string' ? fm['pushed_at'] : null,\n last_pulled_at: typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null,\n last_remote_update: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : null,\n last_body_sha: lastBodySha,\n last_synced_status: typeof fm['last_synced_status'] === 'string' ? fm['last_synced_status'] : null,\n };\n\n const classification = classify(localSnap, remoteSnap, since);\n\n if (dryRun) {\n if (classification.resolution === 'pull') dryRunPulls++;\n else if (classification.resolution === 'push') dryRunPushes++;\n else if (classification.resolution === 'refuse' || classification.resolution === 'halt') dryRunConflicts++;\n continue;\n }\n\n // ── Step 5: Resolve ──────────────────────────────────────────────────────\n switch (classification.resolution) {\n case 'pull':\n case 'merge-silent':\n case 'remote-wins':\n pullQueue.push({ item: remoteItem, localPath, fm, body });\n break;\n\n case 'push':\n if (fm['approved'] === true) {\n pushQueue.push({ localPath, fm, body, itemId: getItemId(fm) });\n }\n break;\n\n case 'merge': {\n // Three-way merge prompt\n const mergeResult = await promptThreeWayMerge({\n local: body,\n remote: remoteItem.body ?? '',\n base: '',\n itemId: getItemId(fm),\n stdin: opts.stdin ?? process.stdin,\n stdout,\n });\n if (mergeResult.resolution === 'aborted') {\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n } else {\n // Apply merge result\n pullQueue.push({\n item: { ...remoteItem, body: mergeResult.body },\n localPath,\n fm,\n body,\n });\n }\n break;\n }\n\n case 'refuse':\n case 'halt':\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n // Do NOT abort — continue processing remaining items (AC §2.1)\n break;\n\n default:\n break;\n }\n }\n\n // ── dry-run summary and early exit ──────────────────────────────────────────\n if (dryRun) {\n stdout(\n `Would pull: ${dryRunPulls}, push: ${dryRunPushes}, ` +\n `intake: ${intakeResult.created}, conflicts: ${dryRunConflicts}\\n`,\n );\n return;\n }\n\n // ── Step 6: Apply + log ──────────────────────────────────────────────────────\n // R2: ALL pulls applied before pushes\n for (const { item, localPath, fm } of pullQueue) {\n await applyPull(item, localPath, fm, identity.email, nowFn);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n for (const { localPath, fm, body, itemId } of pushQueue) {\n // Push item to MCP\n await mcp.call('push_item', {\n cleargate_id: itemId,\n type: typeof fm['story_id'] === 'string' ? 'story'\n : typeof fm['epic_id'] === 'string' ? 'epic'\n : typeof fm['proposal_id'] === 'string' ? 'proposal'\n : 'story',\n payload: fm,\n });\n\n // Stamp last_synced_status + last_synced_body_sha so classify() sees a clean\n // baseline on the next sync run (mirrors the pull-apply path at applyPull:393-394).\n const pushedFm: Record<string, unknown> = {\n ...fm,\n last_synced_status: fm['status'],\n last_synced_body_sha: hashNormalized(body),\n };\n const newContent = serializeFrontmatter(pushedFm) + '\\n\\n' + body;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Log conflicts\n for (const c of conflictsJson) {\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'conflict-refused',\n target: c.item_id,\n remote_id: c.remote_id,\n result: 'halted',\n detail: c.reason,\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Atomic-write .conflicts.json\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n const conflictsContent: ConflictsJson = {\n generated_at: nowFn(),\n sprint_id: sprintId,\n unresolved: conflictsJson,\n };\n await writeAtomic(conflictsFile, JSON.stringify(conflictsContent, null, 2) + '\\n');\n\n // Update wiki meta last_remote_sync\n try {\n await fsPromises.mkdir(path.dirname(wikiMetaPath), { recursive: true });\n let meta: Record<string, unknown> = {};\n try {\n const raw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n meta = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // New meta\n }\n meta['last_remote_sync'] = nowFn();\n await writeAtomic(wikiMetaPath, JSON.stringify(meta, null, 2) + '\\n');\n } catch {\n // Non-fatal\n }\n\n const totalPulls = pullQueue.length;\n const totalPushes = pushQueue.length;\n const totalConflicts = conflictsJson.length;\n stdout(`sync: pulled ${totalPulls}, pushed ${totalPushes}, conflicts ${totalConflicts}\\n`);\n\n // Print intake summary (orchestrator stdout format)\n if (intakeResult.created > 0) {\n const plural = intakeResult.created === 1 ? 'proposal' : 'proposals';\n const inlineList = intakeResult.items\n .map((item) => `${item.proposalId} (${item.remoteId} '${item.title}')`)\n .join(', ');\n stdout(`📥 ${intakeResult.created} new stakeholder ${plural} pulled: ${inlineList}\\n`);\n stdout(` — review at .cleargate/delivery/pending-sync/\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Apply a remote item to the local file: update frontmatter + body. */\nasync function applyPull(\n item: RemoteItem,\n localPath: string,\n fm: Record<string, unknown>,\n actorEmail: string,\n nowFn: () => string,\n): Promise<void> {\n const now = nowFn();\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: item.status,\n last_pulled_by: actorEmail,\n last_pulled_at: now,\n last_remote_update: item.updated_at,\n last_synced_status: item.status,\n last_synced_body_sha: hashNormalized(item.body ?? ''),\n };\n\n const newBody = item.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n}\n\n/** Atomic write: write to .tmp file then rename. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\ninterface LocalWorkItem {\n localPath: string;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Scan .cleargate/delivery/pending-sync/ for tracked work items with remote_id. */\nasync function scanLocalItems(projectRoot: string): Promise<LocalWorkItem[]> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const results: LocalWorkItem[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm, body } = parseFrontmatter(raw);\n if (typeof fm['remote_id'] === 'string' && fm['remote_id']) {\n results.push({ localPath: fullPath, fm, body });\n }\n } catch {\n // Skip malformed files\n }\n }\n\n return results;\n}\n\n/** Extract primary item ID from frontmatter. */\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * sync-log.ts — append-only JSONL sync audit log.\n *\n * STORY-010-01: defines types + appendSyncLog + readSyncLog + resolveActiveSprintDir.\n *\n * Atomicity guarantee for appendSyncLog:\n * Uses fs.promises.appendFile with default flag 'a' (O_APPEND).\n * POSIX guarantees that concurrent O_APPEND writes ≤ PIPE_BUF (4 KB) are atomic\n * — a sync-log line is < 500 bytes so lines from concurrent writers never interleave.\n * We deliberately use appendFile (not read-then-write) to preserve this guarantee.\n *\n * Token redaction:\n * detail fields containing JWT tokens (eyJ…) are redacted before writing.\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\n/** R-014 rule ID constant (defined here for STORY-010-08 to wire into lint pipeline). */\nexport const R014 = 'sync-attribution-missing' as const;\n\nexport type SyncLogOp =\n | 'push'\n | 'pull'\n | 'pull-intake' // STORY-010-05: stakeholder proposal intake\n | 'pull-comments' // STORY-010-06: comment snapshot pull\n | 'push-revert'\n | 'sync-status'\n | 'conflict-remote-wins'\n | 'conflict-refused';\n\nexport type SyncLogResult =\n | 'ok'\n | 'no-op'\n | 'error-not-found'\n | 'error-transport'\n | 'skipped-rate-limit'\n | 'halted';\n\nexport interface SyncLogEntry {\n ts: string;\n actor: string;\n op: SyncLogOp;\n target: string;\n remote_id?: string;\n result: SyncLogResult;\n detail?: string;\n}\n\n// ── Active-sprint resolution ─────────────────────────────────────────────────\n\n/**\n * Resolve the active sprint directory under .cleargate/sprint-runs/.\n *\n * Strategy: scan sprint-runs/* (excluding _off-sprint), pick the entry with the\n * newest mtime. If none exist, create and return _off-sprint/.\n *\n * Open Decision (flagged to orchestrator): story §1.2 says \"read INDEX.md for\n * active sprint\", but INDEX.md has no machine-readable status:active field —\n * only execution_order integers inside sprint frontmatter. Newest-mtime dir is\n * the reliable signal available today.\n */\nexport function resolveActiveSprintDir(\n projectRoot: string,\n _opts?: { now?: () => string },\n): string {\n const sprintRunsRoot = path.join(projectRoot, '.cleargate', 'sprint-runs');\n const offSprint = path.join(sprintRunsRoot, '_off-sprint');\n\n if (!fs.existsSync(sprintRunsRoot)) {\n fs.mkdirSync(sprintRunsRoot, { recursive: true });\n fs.mkdirSync(offSprint, { recursive: true });\n return offSprint;\n }\n\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n const sprintDirs = entries\n .filter((e) => e.isDirectory() && e.name !== '_off-sprint')\n .map((e) => {\n const fullPath = path.join(sprintRunsRoot, e.name);\n const stat = fs.statSync(fullPath);\n return { name: e.name, fullPath, mtimeMs: stat.mtimeMs };\n })\n .sort((a, b) => b.mtimeMs - a.mtimeMs);\n\n if (sprintDirs.length === 0) {\n if (!fs.existsSync(offSprint)) {\n fs.mkdirSync(offSprint, { recursive: true });\n }\n return offSprint;\n }\n\n return sprintDirs[0].fullPath;\n}\n\n// ── JWT redaction ─────────────────────────────────────────────────────────────\n\nfunction redactDetail(detail: string | undefined): string | undefined {\n if (detail === undefined) return undefined;\n return detail.replace(/eyJ[A-Za-z0-9._-]+/g, '[REDACTED]');\n}\n\n// ── Append ────────────────────────────────────────────────────────────────────\n\n/**\n * Append one JSONL entry to <sprintRoot>/sync-log.jsonl.\n * Creates the file and parent directory if absent.\n * Uses O_APPEND for POSIX atomicity — never read-modify-write.\n */\nexport async function appendSyncLog(\n sprintRoot: string,\n entry: SyncLogEntry,\n): Promise<void> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n // Ensure directory exists\n await fsPromises.mkdir(sprintRoot, { recursive: true });\n\n const safeEntry: SyncLogEntry = {\n ...entry,\n detail: redactDetail(entry.detail),\n };\n\n const line = JSON.stringify(safeEntry) + '\\n';\n\n // appendFile with flag 'a' → O_APPEND; POSIX guarantees line-atomicity for <PIPE_BUF writes\n await fsPromises.appendFile(logPath, line, { encoding: 'utf8' });\n}\n\n// ── Read ──────────────────────────────────────────────────────────────────────\n\n/**\n * Read and optionally filter sync-log entries, returning newest-first.\n * Skips malformed lines silently — partial writes never crash the reader.\n */\nexport async function readSyncLog(\n sprintRoot: string,\n filters?: { actor?: string; op?: SyncLogOp; target?: string },\n): Promise<SyncLogEntry[]> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(logPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw err;\n }\n\n const entries: SyncLogEntry[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n try {\n const parsed = JSON.parse(trimmed) as SyncLogEntry;\n entries.push(parsed);\n } catch {\n // malformed line — skip silently\n }\n }\n\n // Apply filters\n let result = entries;\n if (filters?.actor !== undefined) {\n result = result.filter((e) => e.actor === filters.actor);\n }\n if (filters?.op !== undefined) {\n result = result.filter((e) => e.op === filters.op);\n }\n if (filters?.target !== undefined) {\n result = result.filter((e) => e.target === filters.target);\n }\n\n // Newest first (entries are appended oldest-first)\n return result.reverse();\n}\n","/**\n * conflict-detector.ts — STORY-010-03\n *\n * Pure classifier for local-vs-remote sync conflicts.\n * Implements the 8-state PROP-007 §2.3 matrix + explicit 9th \"unknown\" fallthrough.\n *\n * No I/O. No imports from node:fs, node:child_process, node:os.\n * No imports from commands/ or bin/.\n */\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type ConflictState =\n | 'no-change'\n | 'local-only' // local content edit, no remote change\n | 'remote-only' // remote status/metadata change, local untouched\n | 'content-content' // both bodies diverged since last sync\n | 'content-status' // local body + remote status\n | 'status-status' // both statuses diverged\n | 'local-delete-remote-edit'\n | 'remote-delete-local-edit'\n | 'unknown'; // R3: explicit fallthrough — never silently wrong\n\nexport type Resolution =\n | 'push'\n | 'pull'\n | 'merge' // three-way merge prompt\n | 'merge-silent' // content+status: no prompt, apply both sides\n | 'remote-wins' // status+status: silent remote takes, log conflict-remote-wins\n | 'refuse' // halt, surface to human\n | 'halt'; // unknown state — halt sync with explicit message\n\nexport interface LocalSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface RemoteSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface SinceLastSync {\n last_pushed_at: string | null;\n last_pulled_at: string | null;\n last_remote_update: string | null; // ISO-8601 string or null; opaque per M1 lock\n last_body_sha: string | null; // sha at last successful sync (merge-base)\n last_synced_status: string | null; // status recorded at last successful sync (rule 6)\n}\n\nexport interface Classification {\n state: ConflictState;\n resolution: Resolution;\n reason: string; // human-readable; used for sync-log detail + halt messages\n}\n\n// ─── Classifier ───────────────────────────────────────────────────────────────\n\n/**\n * classify — pure function; maps a (local, remote, since) triple to a Classification.\n *\n * Decision table follows PROP-007 §2.3 + M2 plan \"8-state + 9th fallthrough\" exactly.\n * States are evaluated in priority order; first match wins.\n */\nexport function classify(\n local: LocalSnapshot,\n remote: RemoteSnapshot,\n since: SinceLastSync,\n): Classification {\n const baseSha = since.last_body_sha ?? '';\n const lastPulled = since.last_pulled_at ?? '0';\n const lastPushed = since.last_pushed_at ?? '0';\n\n // Rule 7 — local-delete-remote-edit (check deletes first — highest priority)\n if (local.deleted && remote.updated_at > lastPulled) {\n return {\n state: 'local-delete-remote-edit',\n resolution: 'refuse',\n reason: 'local deletion conflicts with remote edit',\n };\n }\n\n // Rule 8 — remote-delete-local-edit\n if (remote.deleted && local.updated_at > lastPushed) {\n return {\n state: 'remote-delete-local-edit',\n resolution: 'refuse',\n reason: 'remote deletion conflicts with local edit',\n };\n }\n\n // Rule 1 — no-change: bodies and status are identical at sync base; nothing deleted\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status === remote.status &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'no-change',\n resolution: 'pull',\n reason: 'no change since last sync',\n };\n }\n\n // Rule 4 — content-content: both bodies diverged\n if (\n local.body_sha !== baseSha &&\n remote.body_sha !== baseSha &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'content-content',\n resolution: 'merge',\n reason: 'both bodies diverged — three-way merge required',\n };\n }\n\n // Rule 5 — content-status: local body changed + remote status changed, bodies otherwise aligned\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status !== local.status\n ) {\n return {\n state: 'content-status',\n resolution: 'merge-silent',\n reason: 'local body edit + remote status change — merged without prompt',\n };\n }\n\n // Rule 2 — local-only: local content edited, remote unchanged, same status\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status === local.status &&\n !remote.deleted\n ) {\n return {\n state: 'local-only',\n resolution: 'push',\n reason: 'local content edit only',\n };\n }\n\n // Rule 6 — status-status: both sides changed status since last sync\n // Requires since.last_synced_status to distinguish from remote-only.\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status !== remote.status &&\n since.last_synced_status !== null &&\n local.status !== since.last_synced_status &&\n remote.status !== since.last_synced_status\n ) {\n return {\n state: 'status-status',\n resolution: 'remote-wins',\n reason: 'status diverged on both sides; remote authoritative',\n };\n }\n\n // Rule 3 — remote-only: local body unchanged, remote status or metadata changed\n if (\n local.body_sha === baseSha &&\n (remote.status !== local.status || remote.updated_at > lastPulled)\n ) {\n return {\n state: 'remote-only',\n resolution: 'pull',\n reason: 'remote status/metadata change only',\n };\n }\n\n // Rule 9 — unknown fallthrough (R3): explicitly refuse to guess\n return {\n state: 'unknown',\n resolution: 'halt',\n reason:\n 'conflict shape not recognized — please resolve manually and file a ClearGate bug with this sync-log entry',\n };\n}\n","/**\n * merge-helper.ts — STORY-010-03\n *\n * Three-way merge prompt for `cleargate sync` content-content conflicts.\n * Reuses EPIC-009 primitives: renderInlineDiff (merge-ui.ts), openInEditor +\n * containsConflictMarkers (editor.ts).\n *\n * Adds the fourth [a]bort branch on top of merge-ui's k/t/e set.\n * Never writes to the caller's file — returns MergeResult; caller applies.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { renderInlineDiff } from './merge-ui.js';\nimport { openInEditor, containsConflictMarkers } from './editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeResolution = 'keep' | 'take' | 'edited' | 'aborted';\n\nexport interface MergeResult {\n resolution: MergeResolution;\n body: string; // the chosen/edited body\n}\n\nexport interface PromptThreeWayMergeOpts {\n local: string;\n remote: string;\n base: string; // merge-base body; used to construct git-merge-markers on [e]dit\n itemId: string; // for diff header + temp-file name\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n editor?: string; // override $EDITOR for tests\n now?: () => string; // temp-file name determinism seam\n}\n\n// ─── Four-choice prompt (extends merge-ui's k/t/e with [a]bort) ──────────────\n\ntype FourChoice = 'k' | 't' | 'e' | 'a';\n\n/**\n * promptFourChoice — renders the four-option prompt and reads one line from stdin.\n * Ctrl-C (stream 'close' without data) and 'a' both resolve to 'a' (abort).\n */\nfunction promptFourChoice(opts: {\n stdin: NodeJS.ReadableStream;\n stdout: (s: string) => void;\n}): Promise<FourChoice> {\n const { stdin, stdout } = opts;\n\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR / [a]bort: ');\n\n return new Promise<FourChoice>((resolve) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n cleanup();\n const answer = buf.slice(0, newline).trim().toLowerCase();\n if (answer === 'k' || answer === 't' || answer === 'e' || answer === 'a') {\n resolve(answer as FourChoice);\n } else {\n stdout(`Unknown choice '${answer}'; treating as [a]bort.\\n`);\n resolve('a');\n }\n }\n };\n\n // Ctrl-C or stdin close without data → abort\n const onClose = () => {\n cleanup();\n resolve('a');\n };\n\n const onEnd = () => {\n cleanup();\n resolve('a');\n };\n\n const onError = () => {\n cleanup();\n resolve('a');\n };\n\n function cleanup() {\n stdin.removeListener('data', onData);\n stdin.removeListener('close', onClose);\n stdin.removeListener('end', onEnd);\n stdin.removeListener('error', onError);\n }\n\n stdin.on('data', onData);\n stdin.once('close', onClose);\n stdin.once('end', onEnd);\n stdin.once('error', onError);\n });\n}\n\n// ─── Main export ──────────────────────────────────────────────────────────────\n\n/**\n * promptThreeWayMerge — interactive three-way merge UX.\n *\n * Flow:\n * 1. Renders unified diff of local vs remote.\n * 2. Prompts [k]eep / [t]ake / [e]dit / [a]bort.\n * 3. On [e]dit: writes a temp file with git-merge-marker content, spawns $EDITOR,\n * re-reads on close, re-prompts if conflict markers remain.\n * Temp file is always unlinked (finally block).\n * 4. On [a]bort or Ctrl-C: returns { resolution: 'aborted', body: local }.\n * 5. Never writes to caller's file.\n */\nexport async function promptThreeWayMerge(opts: PromptThreeWayMergeOpts): Promise<MergeResult> {\n const {\n local,\n remote,\n itemId,\n stdin = process.stdin,\n editor,\n } = opts;\n\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const now = opts.now ?? (() => Date.now().toString());\n\n // 1. Render diff\n const patch = renderInlineDiff(local, remote, itemId);\n stdout(`\\n[merge] ${itemId}\\n`);\n stdout(patch + '\\n');\n\n // 2–3. Prompt loop (re-prompt if editor leaves unresolved markers)\n for (;;) {\n const choice = await promptFourChoice({ stdin, stdout });\n\n switch (choice) {\n case 'k':\n return { resolution: 'keep', body: local };\n\n case 't':\n return { resolution: 'take', body: remote };\n\n case 'a':\n return { resolution: 'aborted', body: local };\n\n case 'e': {\n const tmpFile = path.join(os.tmpdir(), `cleargate-merge-${itemId}-${now()}.md`);\n const markerContent = `<<<<<<< local\\n${local}\\n=======\\n${remote}\\n>>>>>>> remote\\n`;\n\n try {\n await fs.writeFile(tmpFile, markerContent, 'utf-8');\n\n await openInEditor(tmpFile, { editor: editor ?? process.env['EDITOR'] ?? 'vi' });\n\n const edited = await fs.readFile(tmpFile, 'utf-8');\n\n if (containsConflictMarkers(edited)) {\n stdout('File still contains conflict markers — please resolve all conflicts.\\n');\n // re-prompt\n continue;\n }\n\n return { resolution: 'edited', body: edited };\n } finally {\n // Always clean up temp file even on error\n await fs.unlink(tmpFile).catch(() => {/* already gone — ignore */});\n }\n }\n }\n }\n}\n","/**\n * mcp-client.ts — STORY-010-04 / updated STORY-011-01\n *\n * Minimal JSON-RPC-over-HTTP client for ClearGate's MCP server.\n *\n * Token acquisition: callers obtain a token via acquireAccessToken() from\n * cleargate-cli/src/auth/acquire.ts and pass it via McpClientOptions.token.\n * This file does NOT read CLEARGATE_MCP_TOKEN or call acquireAccessToken.\n *\n * MCP endpoint: passed directly as McpClientOptions.baseUrl by the caller.\n *\n * Flashcard: no pre-existing MCP client in cleargate-cli — built from scratch here.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\n// ── Wire types (re-exported for consumers) ───────────────────────────────────\n\nexport interface RemoteUpdateRef {\n remote_id: string;\n updated_at: string;\n}\n\nexport interface RemoteItem {\n remote_id: string;\n title: string;\n body: string | null;\n status: string;\n assignees: string[];\n labels: string[];\n updated_at: string;\n source_tool: string;\n raw: unknown;\n}\n\nexport interface RemoteComment {\n id: string;\n author_email: string | null;\n author_name: string;\n body: string;\n created_at: string;\n remote_id: string;\n}\n\nexport interface AdapterInfo {\n configured: boolean;\n name: 'linear' | 'jira' | 'github-projects' | 'no-adapter-configured';\n}\n\n// ── McpClient interface ───────────────────────────────────────────────────────\n\nexport interface McpClient {\n call<T>(tool: string, args: Record<string, unknown>): Promise<T>;\n adapterInfo(): Promise<AdapterInfo>;\n}\n\nexport interface McpClientOptions {\n baseUrl: string;\n token: string;\n /** Test seam: override globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n}\n\n// ── JSON-RPC envelope ─────────────────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n method: 'tools/call';\n params: {\n name: string;\n arguments: Record<string, unknown>;\n };\n id: number;\n}\n\ninterface JsonRpcResponse<T> {\n jsonrpc: '2.0';\n id: number;\n result?: {\n content?: Array<{ type: 'text'; text: string }>;\n structuredContent?: T;\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\nlet _reqId = 1;\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * createMcpClient — build a JSON-RPC client for the ClearGate MCP server.\n *\n * Sends POST to ${baseUrl}/mcp with Authorization: Bearer <token>.\n * Parses the StreamableHTTP response (may be plain JSON or SSE stream).\n */\nexport function createMcpClient(opts: McpClientOptions): McpClient {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n async function call<T>(tool: string, args: Record<string, unknown>): Promise<T> {\n const body: JsonRpcRequest = {\n jsonrpc: '2.0',\n method: 'tools/call',\n params: { name: tool, arguments: args },\n id: _reqId++,\n };\n\n let response: Response;\n try {\n response = await fetchFn(`${opts.baseUrl}/mcp`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json, text/event-stream',\n 'Authorization': `Bearer ${opts.token}`,\n },\n body: JSON.stringify(body),\n });\n } catch (err) {\n throw new Error(`MCP transport error calling ${tool}: ${String(err)}`);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`MCP HTTP ${response.status} calling ${tool}: ${text.slice(0, 256)}`);\n }\n\n const text = await response.text();\n\n // Handle SSE stream: extract last JSON from event-stream data lines\n let jsonText = text;\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/event-stream')) {\n const dataLines = text\n .split('\\n')\n .filter((l) => l.startsWith('data: '))\n .map((l) => l.slice('data: '.length).trim())\n .filter((l) => l !== '' && l !== '[DONE]');\n if (dataLines.length === 0) {\n throw new Error(`MCP SSE response for ${tool} contained no data lines`);\n }\n jsonText = dataLines[dataLines.length - 1];\n }\n\n let parsed: JsonRpcResponse<T>;\n try {\n parsed = JSON.parse(jsonText) as JsonRpcResponse<T>;\n } catch {\n throw new Error(`MCP response for ${tool} is not valid JSON: ${jsonText.slice(0, 256)}`);\n }\n\n if (parsed.error) {\n throw new Error(`MCP tool ${tool} returned error ${parsed.error.code}: ${parsed.error.message}`);\n }\n\n // structuredContent preferred; fall back to parsing text content\n if (parsed.result?.structuredContent !== undefined) {\n return parsed.result.structuredContent as T;\n }\n\n const textContent = parsed.result?.content?.find((c) => c.type === 'text')?.text;\n if (textContent !== undefined) {\n try {\n return JSON.parse(textContent) as T;\n } catch {\n throw new Error(`MCP tool ${tool} text content is not valid JSON: ${textContent.slice(0, 256)}`);\n }\n }\n\n throw new Error(`MCP tool ${tool} returned no content`);\n }\n\n async function adapterInfo(): Promise<AdapterInfo> {\n return call<AdapterInfo>('cleargate_adapter_info', {});\n }\n\n return { call, adapterInfo };\n}\n\n","/**\n * intake.ts — STORY-010-05\n *\n * Stakeholder proposal intake branch for `cleargate sync`.\n *\n * `runIntakeBranch` is called from `syncHandler` at step 3 (after pull, before\n * conflict classification). It:\n * 1. Calls `cleargate_detect_new_items({ label })` to get remote proposals.\n * 2. Deduplicates against pending-sync + archive by `remote_id` frontmatter.\n * 3. For each new item: allocates a `PROP-NNN` ID, slugifies the title,\n * writes a draft proposal file from the template, and appends a sync-log\n * entry with op:'pull-intake'.\n * 4. Emits an R10 warning to stderr if zero items matched AND this appears to\n * be the first intake run for this workspace (no prior `source: remote-authored`\n * files detected).\n * 5. Returns a summary for the end-of-sync stdout print.\n *\n * Respects dryRun: zero fs writes, zero sync-log entries, returns the plan.\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { slugify, nextProposalId, findByRemoteId } from './slug.js';\nimport { appendSyncLog, type SyncLogEntry } from './sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from './frontmatter-yaml.js';\nimport type { McpClient, RemoteItem } from './mcp-client.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface IntakeItem {\n proposalId: string;\n remoteId: string;\n title: string;\n path: string;\n}\n\nexport interface IntakeResult {\n created: number;\n items: IntakeItem[];\n warning?: string;\n}\n\nexport interface IntakeBranchOptions {\n mcp: McpClient;\n identity: { email: string };\n sprintRoot: string;\n projectRoot: string;\n dryRun: boolean;\n labelFilter?: string;\n /** Test seam: override now() */\n now?: () => string;\n}\n\n// ── runIntakeBranch ───────────────────────────────────────────────────────────\n\nexport async function runIntakeBranch(opts: IntakeBranchOptions): Promise<IntakeResult> {\n const {\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter = 'cleargate:proposal',\n now = () => new Date().toISOString(),\n } = opts;\n\n const pendingSyncDir = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n\n // Detect new remote items with the cleargate:proposal label\n let remoteItems: RemoteItem[] = [];\n try {\n remoteItems = await mcp.call<RemoteItem[]>(\n 'cleargate_detect_new_items',\n { label: labelFilter },\n );\n if (!Array.isArray(remoteItems)) {\n remoteItems = [];\n }\n } catch {\n // Non-fatal: if the tool doesn't exist yet, treat as zero items\n remoteItems = [];\n }\n\n // R10: zero-label warning — fires when zero items returned AND this is the\n // first intake run (no existing `source: remote-authored` files found)\n let warning: string | undefined;\n if (remoteItems.length === 0) {\n const hasExistingIntake = await hasAnyRemoteAuthored(projectRoot);\n if (!hasExistingIntake) {\n warning =\n `warn: no Linear issues match label '${labelFilter}' — ` +\n `confirm the label exists in your workspace. See EPIC-010 R10.`;\n }\n }\n\n const createdItems: IntakeItem[] = [];\n\n for (const item of remoteItems) {\n // Idempotency: skip if a local counterpart already exists (in either dir)\n const existingPath = await findByRemoteId(projectRoot, item.remote_id);\n if (existingPath !== null) {\n continue;\n }\n\n if (dryRun) {\n // In dry-run mode: plan the intake without writing anything\n const proposalId = await nextProposalId(projectRoot);\n const slug = slugify(item.title ?? 'untitled');\n const num = proposalId.replace('PROP-', '');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n continue;\n }\n\n // Compute the next proposal ID and filename\n // Note: we recompute after each write so IDs are consistent even when multiple\n // items are being created in the same run\n const proposalId = await nextProposalId(projectRoot);\n const num = proposalId.replace('PROP-', '');\n const slug = slugify(item.title ?? 'untitled');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n const nowTs = now();\n\n // Build frontmatter from template + our sync fields\n const fm: Record<string, unknown> = {\n proposal_id: proposalId,\n remote_id: item.remote_id,\n status: 'Draft',\n approved: false,\n source: 'remote-authored',\n last_pulled_by: identity.email,\n last_pulled_at: nowTs,\n last_remote_update: item.updated_at,\n created_at: nowTs,\n updated_at: nowTs,\n pushed_by: null,\n pushed_at: null,\n last_synced_status: null,\n last_synced_body_sha: null,\n };\n\n // Build body from the proposal template + pre-fill §1 body from remote item\n const body = buildProposalBody(item, projectRoot);\n\n // Atomic write: .tmp + rename\n await fsPromises.mkdir(pendingSyncDir, { recursive: true });\n const content = serializeFrontmatter(fm) + '\\n\\n' + body;\n const tmpPath = `${targetPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, targetPath);\n\n // Sync-log entry\n const logEntry: SyncLogEntry = {\n ts: nowTs,\n actor: identity.email,\n op: 'pull-intake',\n target: proposalId,\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, logEntry);\n\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n }\n\n return {\n created: createdItems.length,\n items: createdItems,\n warning,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Build the body for an intake proposal file.\n * Seeds §1 \"Initiative & Context\" with the remote item body;\n * leaves other sections as template placeholders.\n */\nfunction buildProposalBody(item: RemoteItem, _projectRoot: string): string {\n const title = item.title ?? '(untitled)';\n const remoteBody = item.body ?? '';\n\n return `# ${title}\n\n## 1. Initiative & Context\n\n### 1.1 Objective\n\n${remoteBody || '(pre-filled from Linear issue body)'}\n\n### 1.2 The \"Why\"\n\n{Reason 1}\n{Reason 2}\n\n## 2. Technical Architecture & Constraints\n\n### 2.1 Dependencies\n\n{List required external APIs, packages, or systems}\n\n### 2.2 System Constraints\n\n| Constraint | Details |\n|---|---|\n| Architectural Rules | {e.g., Must use purely functional components} |\n| Security | {e.g., Data must be encrypted at rest.} |\n\n## 3. Scope Impact (Touched Files & Data)\n\n### 3.1 Known Files\n\npath/to/existing/file.ext - {Explanation of expected change}\n\n### 3.2 Expected New Entities\n\npath/to/new/file.ext - {Explanation of purpose}\n\n## Approval Gate\n\n(Vibe Coder: Review this proposal. If the architecture and context are correct, change approved: false to approved: true in the YAML frontmatter. Only then is the AI authorized to proceed with Epic/Story decomposition.)\n`;\n}\n\n/**\n * Check if any `source: remote-authored` file already exists in pending-sync or archive.\n * Used to gate the R10 zero-label warning.\n */\nasync function hasAnyRemoteAuthored(projectRoot: string): Promise<boolean> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n // Quick scan without full parse — look for source: remote-authored in frontmatter\n const fmEnd = raw.indexOf('\\n---', 4);\n if (fmEnd === -1) continue;\n const fmBlock = raw.slice(0, fmEnd);\n if (/source:\\s*['\"]?remote-authored['\"]?/.test(fmBlock)) {\n return true;\n }\n } catch {\n // Skip\n }\n }\n }\n\n return false;\n}\n\n// Re-export parseFrontmatter for consumers that read template files\nexport { parseFrontmatter };\n","/**\n * slug.ts — STORY-010-05\n *\n * Filename slug helper + proposal ID scanner for stakeholder intake.\n *\n * `slugify(title, max)` — deterministic, locale-free slug from a title string.\n * `nextProposalId(projectRoot)` — scans pending-sync + archive for max PROP-NNN\n * and returns the next sequential ID as a zero-padded string.\n * `findByRemoteId(projectRoot, remoteId)` — dedup helper; returns local path on\n * hit, null on miss. Scans frontmatter block only (first --- ... --- delimiters).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── slugify ──────────────────────────────────────────────────────────────────\n\n/**\n * Convert a title string into a URL-safe slug.\n *\n * Algorithm:\n * 1. NFKD-normalize → strip combining marks (é→e, ü→u, etc.)\n * 2. Lowercase.\n * 3. Replace runs of [^a-z0-9]+ with a single dash.\n * 4. Trim leading/trailing dashes.\n * 5. Truncate to `max` characters; re-trim trailing dash.\n * 6. If empty/all-dashes after step 5 → return \"untitled\".\n */\nexport function slugify(title: string, max: number = 40): string {\n // Step 1: NFKD normalize + strip combining marks (Unicode category M)\n const normalized = title.normalize('NFKD').replace(/\\p{M}/gu, '');\n // Step 2: lowercase\n const lowered = normalized.toLowerCase();\n // Step 3: replace non-alphanumeric runs with dash\n const dashed = lowered.replace(/[^a-z0-9]+/g, '-');\n // Step 4: trim leading/trailing dashes\n const trimmed = dashed.replace(/^-+|-+$/g, '');\n // Step 5: truncate to max; re-trim trailing dash\n const truncated = trimmed.slice(0, max).replace(/-+$/, '');\n // Step 6: fallback for empty result\n if (!truncated) {\n return 'untitled';\n }\n return truncated;\n}\n\n// ── nextProposalId ────────────────────────────────────────────────────────────\n\n/** Pattern matching `proposal_id: \"PROP-NNN\"` or `proposal_id: PROP-NNN` */\nconst PROPOSAL_ID_RE = /^proposal_id:\\s*\"?PROP-(\\d+)\"?/m;\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for `.md` files whose frontmatter contains `proposal_id: \"PROP-NNN\"`.\n * Returns the next ID as `\"PROP-<max+1, zero-padded to 3>\"`.\n *\n * Gap-tolerant: returns max+1, NOT the first gap.\n * Empty dirs or no proposals found → `\"PROP-001\"`.\n */\nexport async function nextProposalId(projectRoot: string): Promise<string> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n let maxN = 0;\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n // Read only the frontmatter block (first --- ... ---)\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fmEnd = extractFrontmatterBlock(raw);\n if (!fmEnd) continue;\n const match = PROPOSAL_ID_RE.exec(fmEnd);\n if (!match) continue;\n const n = parseInt(match[1]!, 10);\n if (n > maxN) maxN = n;\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n const next = maxN + 1;\n return `PROP-${String(next).padStart(3, '0')}`;\n}\n\n// ── findByRemoteId ────────────────────────────────────────────────────────────\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for a `.md` file whose frontmatter contains `remote_id: \"<remoteId>\"`.\n *\n * Returns the absolute path of the first match, or `null` if not found.\n * Reads only the frontmatter block (first --- ... ---) for efficiency.\n */\nexport async function findByRemoteId(\n projectRoot: string,\n remoteId: string,\n): Promise<string | null> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n // Build a regex matching `remote_id: \"LIN-NNN\"` or `remote_id: LIN-NNN`\n const escaped = remoteId.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`^remote_id:\\\\s*\"?${escaped}\"?\\\\s*$`, 'm');\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fm = extractFrontmatterBlock(raw);\n if (!fm) continue;\n if (re.test(fm)) return fullPath;\n } catch {\n // Skip\n }\n }\n }\n\n return null;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Extract the YAML content between the first `---` delimiters.\n * Returns the raw YAML text (without the delimiters), or null if not found.\n */\nfunction extractFrontmatterBlock(raw: string): string | null {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return null;\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return null;\n return lines.slice(1, closeIdx).join('\\n');\n}\n","/**\n * active-criteria.ts — STORY-010-06\n *\n * Resolves which work items are \"active\" for the purpose of comment-pull.\n * Active = (item is referenced in the current sprint) OR (item's last_remote_update is within 30 days).\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but NO `stories:` list.\n * Body-regex scan is the only reliable signal available today.\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveActiveSprintDir } from './sync-log.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface LocalWorkItemRef {\n /** Primary cleargate ID (e.g. STORY-010-06, EPIC-010). */\n primaryId: string;\n /** Remote ID in the PM tool (e.g. LIN-1042). May be undefined if not yet pushed. */\n remoteId: string | undefined;\n /** ISO string from `last_remote_update` frontmatter field. */\n lastRemoteUpdate: string | undefined;\n}\n\n// ── Active set resolver ───────────────────────────────────────────────────────\n\n/**\n * Resolve which items (by remote_id) are \"active\" and should have comments fetched.\n *\n * @param projectRoot - absolute path to the project root\n * @param localItems - all local work items with their IDs\n * @param nowFn - injectable clock (for 30-day window)\n * @returns - Set of remote_id values that are active\n */\nexport async function resolveActiveItems(\n projectRoot: string,\n localItems: LocalWorkItemRef[],\n nowFn: () => string = () => new Date().toISOString(),\n): Promise<Set<string>> {\n const active = new Set<string>();\n const now = Date.parse(nowFn());\n const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;\n\n // ── Branch 1: items referenced in the current sprint ─────────────────────\n const inSprintIds = await resolveInSprintIds(projectRoot);\n\n // ── Branch 2: items updated in last 30 days + union of sprint ─────────────\n for (const item of localItems) {\n if (!item.remoteId) continue;\n\n // In-sprint check\n if (inSprintIds.has(item.primaryId)) {\n active.add(item.remoteId);\n continue;\n }\n\n // 30-day window check\n if (item.lastRemoteUpdate) {\n const itemMs = Date.parse(item.lastRemoteUpdate);\n if (!isNaN(itemMs) && (now - itemMs) <= thirtyDaysMs) {\n active.add(item.remoteId);\n }\n }\n }\n\n return active;\n}\n\n// ── Internal: resolve sprint item references ──────────────────────────────────\n\n/**\n * Read the active sprint file and extract all work-item ID references from its body.\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but no `stories:` array.\n * We scan the full body text for IDs matching:\n * (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n *\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n */\nasync function resolveInSprintIds(projectRoot: string): Promise<Set<string>> {\n const ids = new Set<string>();\n\n try {\n const sprintDir = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintDir);\n\n if (sprintId === '_off-sprint') return ids;\n\n // Try pending-sync first, then archive\n const sprintFile = await findSprintFile(projectRoot, sprintId);\n if (!sprintFile) return ids;\n\n const content = await fsPromises.readFile(sprintFile, 'utf8');\n\n // Scan body for work-item ID patterns\n // (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n const pattern = /(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/g;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(content)) !== null) {\n ids.add(match[0]);\n }\n } catch {\n // Non-fatal: if we can't resolve sprint, return empty set\n }\n\n return ids;\n}\n\nasync function findSprintFile(projectRoot: string, sprintId: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isFile() && entry.name.startsWith(sprintId) && entry.name.endsWith('.md')) {\n return path.join(dir, entry.name);\n }\n }\n } catch {\n // Directory not found — try next\n }\n }\n\n return null;\n}\n","/**\n * comments-cache.ts — STORY-010-06\n *\n * Atomic read/write for comment cache files at\n * .cleargate/.comments-cache/<remote_id>.json\n *\n * Contents = raw RemoteComment[] array from cleargate_pull_comments.\n * Write is atomic via .tmp + rename (mirrors sync.ts:writeAtomic).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Cache directory ───────────────────────────────────────────────────────────\n\nfunction cacheDir(projectRoot: string): string {\n return path.join(projectRoot, '.cleargate', '.comments-cache');\n}\n\nfunction cachePath(projectRoot: string, remoteId: string): string {\n return path.join(cacheDir(projectRoot), `${remoteId}.json`);\n}\n\n// ── Write ──────────────────────────────────────────────────────────────────────\n\n/**\n * Atomically write a RemoteComment[] array to the cache for `remoteId`.\n * Creates the cache directory if absent.\n */\nexport async function writeCommentCache(\n projectRoot: string,\n remoteId: string,\n comments: RemoteComment[],\n): Promise<void> {\n const dir = cacheDir(projectRoot);\n await fsPromises.mkdir(dir, { recursive: true });\n\n const filePath = cachePath(projectRoot, remoteId);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n const content = JSON.stringify(comments, null, 2) + '\\n';\n\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Read ───────────────────────────────────────────────────────────────────────\n\n/**\n * Read cached comments for `remoteId`.\n * Returns null if the cache file is absent or contains malformed JSON.\n */\nexport async function readCommentCache(\n projectRoot: string,\n remoteId: string,\n): Promise<RemoteComment[] | null> {\n const filePath = cachePath(projectRoot, remoteId);\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(filePath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n\n try {\n return JSON.parse(raw) as RemoteComment[];\n } catch {\n return null;\n }\n}\n","/**\n * wiki-comments-render.ts — STORY-010-06\n *\n * Renders (inserts / replaces / removes) the \"## Remote comments\" section\n * on an existing wiki page at .cleargate/wiki/<bucket>/<primaryId>.md.\n *\n * DELIBERATELY separate from commands/wiki-ingest.ts which owns full-page\n * rebuild from raw. Section overlay is a different concern and would fight\n * wiki-ingest's SHA-idempotency guard.\n *\n * Delimiter matching uses literal-string indexOf, NOT regex.\n * FLASHCARD #regex #inject-claude-md: fuzzy whitespace regex breaks when the\n * block body itself references both markers in prose — use indexOf exclusively.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Delimiters (literal strings — never change to regex) ───────────────────────\n\nconst START = '<!-- cleargate:comments:start -->';\nconst END = '<!-- cleargate:comments:end -->';\n\n// ── Bucket resolution ─────────────────────────────────────────────────────────\n\n/**\n * Map a frontmatter record to the wiki bucket directory name.\n * Returns null if the item type cannot be determined.\n */\nexport function resolveBucket(fm: Record<string, unknown>): string | null {\n if (typeof fm['story_id'] === 'string' && fm['story_id']) return 'stories';\n if (typeof fm['epic_id'] === 'string' && fm['epic_id']) return 'epics';\n if (typeof fm['proposal_id'] === 'string' && fm['proposal_id']) return 'proposals';\n if (typeof fm['cr_id'] === 'string' && fm['cr_id']) return 'crs';\n if (typeof fm['bug_id'] === 'string' && fm['bug_id']) return 'bugs';\n return null;\n}\n\n/**\n * Extract the primary item ID (e.g. STORY-010-06) from frontmatter.\n */\nexport function getPrimaryId(fm: Record<string, unknown>): string | null {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return null;\n}\n\n// ── Section builder ───────────────────────────────────────────────────────────\n\n/**\n * Build the full delimited comment section string.\n * Sorts comments by created_at ascending.\n */\nexport function buildCommentSection(comments: RemoteComment[]): string {\n const sorted = [...comments].sort((a, b) => {\n return a.created_at < b.created_at ? -1 : a.created_at > b.created_at ? 1 : 0;\n });\n\n const entries = sorted.map((c) => {\n const author = c.author_email\n ? `${c.author_name} (${c.author_email})`\n : c.author_name;\n\n // Multi-line body: prefix every line with \"> \"\n const bodyLines = c.body.split('\\n').map((line) => `> ${line}`).join('\\n');\n\n return `### ${author} · ${c.created_at}\\n${bodyLines}`;\n });\n\n return (\n `${START}\\n` +\n `## Remote comments\\n` +\n `\\n` +\n `_Read-only snapshot. Comments live in the PM tool — reply there, not here._\\n` +\n `\\n` +\n entries.join('\\n\\n') +\n `\\n${END}`\n );\n}\n\n// ── Main export ───────────────────────────────────────────────────────────────\n\nexport interface RenderCommentsSectionOpts {\n /** Absolute path to project root */\n projectRoot: string;\n /** Remote ID (e.g. LIN-1042) */\n remoteId: string;\n /** Comment array from cleargate_pull_comments */\n comments: RemoteComment[];\n /** Local work items to resolve wiki path from */\n localItems: Array<{ fm: Record<string, unknown> }>;\n}\n\n/**\n * Insert / replace / remove the ## Remote comments section on the wiki page\n * corresponding to the given remote_id.\n *\n * - If the wiki page does not exist: no-op (wiki-ingest may not have run yet).\n * - Byte-idempotent: running twice with identical input produces identical output.\n * - Atomic write via .tmp + rename.\n */\nexport async function renderCommentsSection(\n opts: RenderCommentsSectionOpts,\n): Promise<void> {\n const { projectRoot, remoteId, comments, localItems } = opts;\n\n // Find the local item whose remote_id matches\n const localItem = localItems.find(\n (item) => item.fm['remote_id'] === remoteId,\n );\n if (!localItem) return;\n\n const bucket = resolveBucket(localItem.fm);\n const primaryId = getPrimaryId(localItem.fm);\n if (!bucket || !primaryId) return;\n\n const wikiPath = path.join(\n projectRoot,\n '.cleargate',\n 'wiki',\n bucket,\n `${primaryId}.md`,\n );\n\n // Read existing wiki page\n let existing: string;\n try {\n existing = await fsPromises.readFile(wikiPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return; // wiki page not yet built\n throw err;\n }\n\n const startIdx = existing.indexOf(START);\n const endIdx = existing.indexOf(END);\n\n let updated: string;\n\n if (startIdx === -1 && comments.length === 0) {\n // No section, no comments — no-op\n return;\n } else if (startIdx === -1 && comments.length > 0) {\n // Insert new section at end of file\n const section = buildCommentSection(comments);\n const base = existing.endsWith('\\n\\n')\n ? existing.slice(0, -1) // trim one trailing newline\n : existing.endsWith('\\n')\n ? existing\n : existing + '\\n';\n updated = base + '\\n' + section + '\\n';\n } else if (startIdx !== -1 && comments.length > 0) {\n // Replace existing section\n const section = buildCommentSection(comments);\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n\\n' + section + '\\n' + (after ? '\\n' + after : '');\n } else {\n // startIdx !== -1 && comments.length === 0: remove section\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n' + (after ? after : '');\n }\n\n // Atomic write\n await writeAtomic(wikiPath, updated);\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n","/**\n * pull.ts — STORY-010-04\n *\n * `cleargate pull <ID-or-remote_id>` — targeted single-item pull.\n *\n * --comments flag: reserved for STORY-010-06; emits a warn-level message and\n * proceeds without comment pull. Does NOT error. Does NOT accept silently.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteComment } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\n\nexport interface PullOptions {\n comments?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\nexport async function pullHandler(idOrRemoteId: string, opts: PullOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Resolve the remote_id to pull\n // If idOrRemoteId looks like a remote ID (e.g. LIN-1042) use directly;\n // otherwise try to find local file with matching story_id / epic_id / etc.\n const remoteId = await resolveRemoteId(idOrRemoteId, projectRoot);\n if (!remoteId) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a remote_id. Check that the item has been pushed first.\\n`);\n exit(1);\n return;\n }\n\n // Pull from MCP\n const remoteItem = await mcp.call<RemoteItem | null>('cleargate_pull_item', { remote_id: remoteId });\n if (!remoteItem) {\n stderr(`Error: item ${remoteId} not found on MCP server.\\n`);\n exit(1);\n return;\n }\n\n // Find the local file\n const localPath = await findLocalFile(remoteId, projectRoot);\n\n if (!localPath) {\n stderr(`Error: no local file found with remote_id \"${remoteId}\".\\n`);\n exit(1);\n return;\n }\n\n // Read current state and check idempotency\n const rawContent = await fsPromises.readFile(localPath, 'utf8');\n const { fm, body } = parseFrontmatter(rawContent);\n\n const currentBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const lastPulledAt = typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null;\n\n // Idempotency check: if nothing changed since last pull, skip\n const isNoOp =\n currentBodySha === remoteBodySha &&\n currentStatus === remoteItem.status &&\n typeof fm['last_synced_body_sha'] === 'string' &&\n fm['last_synced_body_sha'] === remoteBodySha &&\n lastPulledAt !== null;\n\n const now = nowFn();\n\n if (isNoOp) {\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'no-op',\n };\n await appendSyncLog(sprintRoot, entry);\n stdout(`pull: ${remoteId} no-op (no changes)\\n`);\n return;\n }\n\n // Apply the pull\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: remoteItem.status,\n last_pulled_by: identity.email,\n last_pulled_at: now,\n last_remote_update: remoteItem.updated_at,\n last_synced_status: remoteItem.status,\n last_synced_body_sha: remoteBodySha,\n };\n\n const newBody = remoteItem.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`pull: ${remoteId} applied to ${path.relative(projectRoot, localPath)}\\n`);\n\n // ── --comments: pull comment snapshot for this item ──────────────────────\n // Always pulls when flag is set (manual override; ignores active criteria).\n if (opts.comments) {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n\n // Rebuild updatedFm as the local item state post-pull for wiki-render\n const localItemForRender = { fm: { ...updatedFm, remote_id: remoteId } };\n await renderCommentsSection({\n projectRoot,\n remoteId,\n comments,\n localItems: [localItemForRender],\n });\n stdout(`pull: ${remoteId} comments fetched (${comments.length})\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveRemoteId(idOrRemoteId: string, projectRoot: string): Promise<string | null> {\n // If it looks like a remote ID pattern (e.g. LIN-NNNN, GH-NNNN, JIRA-123)\n if (/^[A-Z]+-\\d+/.test(idOrRemoteId)) {\n return idOrRemoteId;\n }\n // Try to find a local work item with matching ID, read its remote_id\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n try {\n const raw = await fsPromises.readFile(path.join(pendingSync, entry.name), 'utf8');\n const { fm } = parseFrontmatter(raw);\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId && typeof fm['remote_id'] === 'string') {\n return fm['remote_id'];\n }\n }\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function findLocalFile(remoteId: string, projectRoot: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n if (fm['remote_id'] === remoteId) return fullPath;\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * push.ts — STORY-010-07\n *\n * `cleargate push <file>` — push a local work item to the MCP server.\n * `cleargate push --revert <ID-or-remote_id>` — soft-revert a pushed item.\n *\n * Pre-push gate (client-side):\n * Reads local frontmatter. If approved !== true, exits 1 with a clear message\n * BEFORE any MCP call. Zero network traffic on refusal.\n *\n * Attribution write-back:\n * On success, writes pushed_by + pushed_at from MCP response back into the\n * local frontmatter atomically (.tmp + rename). Appends sync-log entry op='push'.\n *\n * Soft revert (--revert):\n * Calls cleargate_sync_status with new_status='archived-without-shipping'.\n * Does NOT delete the remote item. Does NOT clear local remote_id.\n * Guards against reverting status='done' items unless --force is passed.\n * Appends sync-log entry op='push-revert'.\n *\n * Token safety:\n * JWT tokens (eyJ…) are NEVER written to stdout, stderr, or sync-log.\n * redactDetail in appendSyncLog covers the detail field.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\n// ── Response shapes ─────────────────────────────────────────────────────────────\n\ninterface PushItemResult {\n version: number;\n updated_at: string;\n pushed_by: string;\n pushed_at: string;\n}\n\n// ── Options ─────────────────────────────────────────────────────────────────────\n\nexport interface PushOptions {\n /** --revert <ID-or-remote_id>: soft-revert a pushed item */\n revert?: string;\n /** --force: bypass \"done\" guard on revert */\n force?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (prevents token-from-env requirement) */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────────\n\nexport async function pushHandler(fileOrId: string, opts: PushOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client — resolved lazily and asynchronously via acquireAccessToken.\n // STORY-011-01: approved gate in handlePush runs BEFORE this, so no network\n // traffic happens on refusal (STORY-010-07 invariant preserved).\n async function resolveMcp(): Promise<McpClient> {\n if (opts.mcp) return opts.mcp;\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n throw new Error('unreachable');\n }\n // Acquire token\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n throw new Error('unreachable');\n }\n return createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Revert path ───────────────────────────────────────────────────────────────\n if (opts.revert !== undefined) {\n await handleRevert(opts.revert, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n force: opts.force ?? false,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n return;\n }\n\n // ── Push path ─────────────────────────────────────────────────────────────────\n await handlePush(fileOrId, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n}\n\n// ── Push implementation ───────────────────────────────────────────────────────\n\ninterface PushCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handlePush(filePath: string, ctx: PushCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve path (absolute or relative to projectRoot)\n const resolvedPath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(projectRoot, filePath);\n\n let rawContent: string;\n try {\n rawContent = await fsPromises.readFile(resolvedPath, 'utf8');\n } catch {\n stderr(`Error: cannot read file \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(rawContent));\n } catch (err) {\n stderr(`Error: cannot parse frontmatter in \"${resolvedPath}\": ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Client-side approved gate (BEFORE any MCP call) ─────────────────────────\n // STORY-010-07: if approved !== true, refuse and exit without network call.\n if (fm['approved'] !== true) {\n const itemId = getItemId(fm);\n stderr(\n `Error: push refused — ${itemId} has approved: false. ` +\n `Set approved: true in frontmatter after review.\\n`,\n );\n exit(1);\n return;\n }\n\n const itemId = getItemId(fm);\n const type = getItemType(fm);\n if (!type) {\n stderr(`Error: cannot determine item type from frontmatter in \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n // Derive title from body's first H1 if frontmatter lacks one.\n // ClearGate templates put the human-readable title in `# {ID}: {Name}`,\n // not in a `title:` frontmatter field. Admin UI reads payload.title for\n // item rows; without this, every row renders with an empty heading.\n const payloadForPush: Record<string, unknown> = { ...fm };\n if (typeof payloadForPush['title'] !== 'string' || payloadForPush['title'].length === 0) {\n const h1 = body.match(/^#\\s+(.+?)\\s*$/m)?.[1]?.trim();\n if (h1) payloadForPush['title'] = h1;\n }\n // Include the markdown body verbatim so the admin UI can render the full\n // work-item content (spec, Gherkin, implementation notes, DoD). Stored as\n // payload.body under the item's jsonb column — repo remains the canonical\n // source, MCP is a queryable mirror.\n payloadForPush['body'] = body;\n\n // MCP call\n const mcp = await resolveMcp();\n\n let result: PushItemResult;\n try {\n result = await mcp.call<PushItemResult>('push_item', {\n cleargate_id: itemId,\n type,\n payload: payloadForPush,\n ...(typeof fm['remote_id'] === 'string' ? { remote_id: fm['remote_id'] } : {}),\n });\n } catch (err) {\n stderr(`Error: push_item failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Attribution write-back (atomic) ──────────────────────────────────────────\n const updatedFm: Record<string, unknown> = {\n ...fm,\n pushed_by: result.pushed_by,\n pushed_at: result.pushed_at,\n ...(result.version !== undefined ? { push_version: result.version } : {}),\n };\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + body;\n await writeAtomic(resolvedPath, newContent);\n\n // Sync-log\n const now = nowFn();\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n // Note: pushed_by and pushed_at go in frontmatter, NOT in sync-log detail\n // to prevent any accidental token leakage via the detail field.\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push: ${itemId} → version ${result.version} (pushed_by: ${result.pushed_by})\\n`);\n}\n\n// ── Revert implementation ─────────────────────────────────────────────────────\n\ninterface RevertCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n force: boolean;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handleRevert(idOrRemoteId: string, ctx: RevertCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, force, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve to local file\n const resolved = await resolveLocalItem(idOrRemoteId, projectRoot);\n if (!resolved) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a local work item.\\n`);\n exit(1);\n return;\n }\n\n const { localPath, fm } = resolved;\n const itemId = getItemId(fm);\n const localStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n\n // Guard: refuse to revert \"done\" items without --force\n if (localStatus === 'done' && !force) {\n stderr(`Error: refusing to revert shipped item. Pass --force to override.\\n`);\n exit(1);\n return;\n }\n\n // Call sync_status with new_status='archived-without-shipping'\n const mcp = await resolveMcp();\n try {\n await mcp.call('sync_status', {\n cleargate_id: itemId,\n new_status: 'archived-without-shipping',\n });\n } catch (err) {\n stderr(`Error: sync_status revert failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // DO NOT clear local remote_id — item stays traceable\n // DO NOT overwrite local status — sync will pull the server state back\n\n // Sync-log\n const now = nowFn();\n const remoteId = typeof fm['remote_id'] === 'string' ? fm['remote_id'] : undefined;\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push-revert',\n target: itemId,\n ...(remoteId !== undefined ? { remote_id: remoteId } : {}),\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push --revert: ${itemId} → archived-without-shipping\\n`);\n void localPath; // referenced for clarity; not needed after initial read\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveLocalItem(\n idOrRemoteId: string,\n projectRoot: string,\n): Promise<{ localPath: string; fm: Record<string, unknown> } | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n\n // Match by remote_id\n if (fm['remote_id'] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n\n // Match by primary item ID (story_id, epic_id, etc.)\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n }\n } catch {\n // skip malformed\n }\n }\n }\n\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n\nfunction getItemType(fm: Record<string, unknown>): string | null {\n const typeMap: Record<string, string> = {\n story_id: 'story',\n epic_id: 'epic',\n proposal_id: 'proposal',\n cr_id: 'cr',\n bug_id: 'bug',\n };\n for (const [key, type] of Object.entries(typeMap)) {\n if (typeof fm[key] === 'string' && fm[key]) return type;\n }\n return null;\n}\n","/**\n * conflicts.ts — STORY-010-04 / updated STORY-011-01\n *\n * `cleargate conflicts` — read-only command that reads .cleargate/.conflicts.json\n * and prints unresolved items with one-line resolution hints.\n *\n * Exit 0 when unresolved: [], exit 1 otherwise.\n *\n * --refresh flag: force-invalidate the acquire cache and rotate the stored\n * refresh token even if the cached access token is still valid. This is the\n * only MCP call conflicts makes.\n *\n * No mutations (besides keychain rotation on --refresh). No top-level await\n * (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { ConflictsJson, ConflictEntry } from './sync.js';\nimport { acquireAccessToken } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\nexport interface ConflictsOptions {\n projectRoot?: string;\n /** --refresh: bypass single-flight cache and force a new /auth/refresh. */\n refresh?: boolean;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: override process.env lookup */\n env?: NodeJS.ProcessEnv;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n}\n\nconst RESOLUTION_HINTS: Record<string, string> = {\n 'local-delete-remote-edit': 'remote-delete: resurrect or delete remote?',\n 'remote-delete-local-edit': 'local-edit: push your changes or accept remote deletion?',\n 'refuse': 'manual resolution required — re-run sync after resolving',\n 'halt': 'unknown conflict shape — file a ClearGate bug',\n};\n\nfunction getHint(entry: ConflictEntry): string {\n return RESOLUTION_HINTS[entry.state] ?? RESOLUTION_HINTS[entry.resolution] ?? `resolve and re-run sync`;\n}\n\nexport async function conflictsHandler(opts: ConflictsOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n // ── --refresh: force-invalidate the cache and rotate the keychain token ─────\n if (opts.refresh) {\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — skip refresh; command proceeds without token rotation\n }\n }\n if (baseUrl && baseUrl.trim()) {\n try {\n await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n forceRefresh: true,\n env,\n });\n } catch {\n // Refresh errors are non-fatal for `conflicts` — proceed to print conflicts\n }\n }\n }\n\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n\n let data: ConflictsJson;\n try {\n const raw = await fsPromises.readFile(conflictsFile, 'utf8');\n data = JSON.parse(raw) as ConflictsJson;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n stdout('No conflicts file found. Run `cleargate sync` first.\\n');\n exit(0);\n return;\n }\n throw err;\n }\n\n const unresolved = data.unresolved ?? [];\n\n if (unresolved.length === 0) {\n stdout('No unresolved conflicts.\\n');\n exit(0);\n return;\n }\n\n stdout(`Unresolved conflicts (${unresolved.length}):\\n`);\n stdout(`Generated: ${data.generated_at} Sprint: ${data.sprint_id}\\n\\n`);\n\n for (const item of unresolved) {\n const hint = getHint(item);\n stdout(` ${item.item_id.padEnd(20)} ${item.state.padEnd(30)} ${hint}\\n`);\n }\n\n stdout('\\nRe-run `cleargate sync` after resolving conflicts.\\n');\n exit(1);\n}\n","/**\n * sync-log.ts (command) — STORY-010-04\n *\n * `cleargate sync-log` — filter/print wrapper over readSyncLog() from M1.\n *\n * Flags: --actor, --op, --target, --limit N (default 50). Newest-first guaranteed by lib.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { resolveActiveSprintDir, readSyncLog, type SyncLogOp, type SyncLogEntry } from '../lib/sync-log.js';\n\nexport interface SyncLogCommandOptions {\n actor?: string;\n op?: string;\n target?: string;\n limit?: number;\n projectRoot?: string;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n}\n\nexport async function syncLogHandler(opts: SyncLogCommandOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const limit = opts.limit ?? 50;\n\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // Validate --op value if provided\n const validOps = new Set<SyncLogOp>([\n 'push', 'pull', 'pull-intake', 'push-revert', 'sync-status', 'conflict-remote-wins', 'conflict-refused',\n ]);\n let opFilter: SyncLogOp | undefined;\n if (opts.op !== undefined) {\n if (!validOps.has(opts.op as SyncLogOp)) {\n stderr(`Warning: unknown op \"${opts.op}\". Valid ops: ${[...validOps].join(', ')}\\n`);\n } else {\n opFilter = opts.op as SyncLogOp;\n }\n }\n\n const entries = await readSyncLog(sprintRoot, {\n actor: opts.actor,\n op: opFilter,\n target: opts.target,\n });\n\n const limited = entries.slice(0, limit);\n\n if (limited.length === 0) {\n stdout('No sync-log entries match the given filters.\\n');\n return;\n }\n\n for (const entry of limited) {\n stdout(formatEntry(entry) + '\\n');\n }\n}\n\nfunction formatEntry(entry: SyncLogEntry): string {\n const parts: string[] = [\n entry.ts,\n entry.actor,\n entry.op.padEnd(20),\n entry.target.padEnd(24),\n entry.result,\n ];\n if (entry.remote_id) parts.push(`remote=${entry.remote_id}`);\n if (entry.detail) parts.push(`detail=${entry.detail}`);\n return parts.join(' ');\n}\n","/**\n * `cleargate admin login` — GitHub OAuth device flow for admin CLI login.\n *\n * Flow:\n * 1. POST <mcp-url>/admin-api/v1/auth/device/start → gets device_code + user_code + verification_uri.\n * 2. Prints the verification URL and user code for the operator to open in a browser.\n * 3. Polls POST /admin-api/v1/auth/device/poll every `interval` seconds.\n * 4. On success: writes ~/.cleargate/admin-auth.json { version: 1, token: <admin_jwt> } at chmod 600.\n * 5. On failure: prints a clear error message and exits with appropriate exit code.\n *\n * Exit codes (per STORY-005-06 spec):\n * 0 — success\n * 3 — network error (unreachable)\n * 4 — auth rejected (non-admin GitHub user — not_admin)\n * 5 — device-flow timeout or user denied (expired_token / access_denied)\n * 6 — other device-flow error\n * 99 — unhandled\n *\n * Secrets NEVER appear on stdout/stderr: neither the GitHub access token\n * (server-side only) nor the admin JWT.\n *\n * STORY-005-06.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { startDeviceFlow, DeviceFlowError } from '../auth/identity-flow.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface DevicePollPendingResponse {\n pending: true;\n retry_after?: number;\n}\n\nexport interface DevicePollSuccessResponse {\n pending: false;\n admin_token: string;\n expires_at: string;\n admin_user_id: string;\n}\n\nexport type DevicePollResponse = DevicePollPendingResponse | DevicePollSuccessResponse;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Options / seams\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AdminLoginOptions {\n mcpUrl?: string;\n env?: NodeJS.ProcessEnv;\n homedir?: () => string;\n fetch?: typeof globalThis.fetch;\n stdout?: (msg: string) => void;\n stderr?: (msg: string) => void;\n exit?: (code: number) => never;\n /** Override polling interval in milliseconds (used in tests to avoid real waits) */\n intervalOverrideMs?: number;\n /** Override admin-auth file path */\n authFilePath?: string;\n /** Override sleep implementation — injected in tests to capture interval values */\n sleepFn?: (ms: number) => Promise<void>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_MCP_URL = 'http://localhost:3000';\n\n/** Sleep helper — exported so tests can spy on it. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\nfunction resolveMcpUrl(mcpUrlFlag?: string, env?: NodeJS.ProcessEnv): string {\n return (\n mcpUrlFlag ??\n (env ?? process.env)['CLEARGATE_MCP_URL'] ??\n DEFAULT_MCP_URL\n ).replace(/\\/$/, '');\n}\n\nfunction resolveAuthFilePath(opts: AdminLoginOptions): string {\n if (opts.authFilePath) return opts.authFilePath;\n const homedirFn = opts.homedir ?? os.homedir;\n return path.join(homedirFn(), '.cleargate', 'admin-auth.json');\n}\n\nfunction writeAdminAuth(filePath: string, token: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n const payload = JSON.stringify({ version: 1, token }, null, 2);\n fs.writeFileSync(filePath, payload, { encoding: 'utf8', mode: 0o600 });\n // Explicit chmod in case the file already existed with wider permissions\n fs.chmodSync(filePath, 0o600);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main handler\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport async function adminLoginHandler(opts: AdminLoginOptions = {}): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((msg: string) => process.stdout.write(msg + '\\n'));\n const stderr = opts.stderr ?? ((msg: string) => process.stderr.write(msg + '\\n'));\n const exitFn = opts.exit ?? ((code: number): never => process.exit(code));\n const mcpBase = resolveMcpUrl(opts.mcpUrl, opts.env);\n\n // ── Step 1: Start device flow ──────────────────────────────────────────────\n let startData: DeviceStartResponse;\n try {\n const startRes = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n });\n\n if (!startRes.ok) {\n const body = (await startRes.json().catch(() => ({}))) as { error?: string };\n if (startRes.status === 503) {\n stderr('cleargate: error: device flow not configured on the server (CLEARGATE_GITHUB_CLI_CLIENT_ID not set).');\n return exitFn(6);\n }\n stderr(`cleargate: error: server error ${startRes.status}: ${body.error ?? 'unknown'}`);\n return exitFn(6);\n }\n\n startData = (await startRes.json()) as DeviceStartResponse;\n } catch (err) {\n stderr(`cleargate: error: cannot reach ${mcpBase} (${err instanceof Error ? err.message : String(err)})`);\n return exitFn(3);\n }\n\n // ── Step 2: Display instructions ───────────────────────────────────────────\n stdout(`Open the following URL in your browser and enter the code:`);\n stdout(` URL: ${startData.verification_uri}`);\n stdout(` Code: ${startData.user_code}`);\n stdout(` (Code expires in ${Math.floor(startData.expires_in / 60)} minutes)`);\n stdout('Waiting for authorization...');\n\n // ── Step 3: Poll via identity-flow.startDeviceFlow with captured success body ─\n // We wrap fetchPoll to capture the full DevicePollSuccessResponse (which includes\n // expires_at + admin_user_id needed for the success stdout message) since\n // startDeviceFlow only returns { accessToken }.\n let capturedSuccessBody: DevicePollSuccessResponse | null = null;\n\n const fetchPollCapture = async (deviceCode: string) => {\n const res = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/poll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ device_code: deviceCode }),\n });\n // Capture the success body before startDeviceFlow consumes it.\n // We intercept by wrapping json() to also store the body when pending=false.\n const originalJson = res.json.bind(res);\n return {\n status: res.status,\n json: async () => {\n const body = (await originalJson()) as Record<string, unknown>;\n if (body['pending'] === false) {\n capturedSuccessBody = body as unknown as DevicePollSuccessResponse;\n }\n return body;\n },\n };\n };\n\n try {\n await startDeviceFlow({\n deviceCode: startData.device_code,\n interval: startData.interval,\n expiresIn: startData.expires_in,\n fetchPoll: fetchPollCapture,\n // Only pass sleepFn if the caller explicitly injected one (test seam).\n // When sleepFn is omitted, startDeviceFlow uses its own defaultSleep.\n // This preserves the original bump-suppression logic:\n // shouldApplyBump = (sleepFn provided) || (intervalOverrideMs not set).\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n deadlineGraceMs: 10_000,\n });\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: error: access denied — you declined authorization in the browser.');\n return exitFn(5);\n case 'not_admin':\n stderr('cleargate: error: your GitHub account is not authorized as an admin user.');\n return exitFn(4);\n case 'expired_token':\n stderr('cleargate: error: device code expired — please run `cleargate admin login` again.');\n return exitFn(5);\n case 'timeout':\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n case 'unreachable':\n stderr(`cleargate: error: network error while polling`);\n return exitFn(3);\n default:\n stderr(`cleargate: error: unexpected server response`);\n return exitFn(6);\n }\n }\n stderr(`cleargate: error: unexpected error during device flow`);\n return exitFn(6);\n }\n\n if (!capturedSuccessBody) {\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n }\n\n // Extract fields with explicit typing to avoid TypeScript discriminated union narrowing\n const successBody = capturedSuccessBody as DevicePollSuccessResponse;\n\n // ── Step 4: Write admin-auth.json ──────────────────────────────────────────\n const authFilePath = resolveAuthFilePath(opts);\n try {\n writeAdminAuth(authFilePath, successBody.admin_token);\n } catch (err) {\n stderr(`cleargate: error: failed to write ${authFilePath}: ${err instanceof Error ? err.message : String(err)}`);\n return exitFn(99);\n }\n\n // ── Step 5: Success message (no secrets in output) ─────────────────────────\n stdout(`Logged in successfully. Token expires ${successBody.expires_at}.`);\n stdout(`Credentials saved to ${authFilePath} (chmod 600).`);\n}\n","/**\n * hotfix.ts — `cleargate hotfix new <slug>` command handler.\n *\n * STORY-022-06: Hotfix lane scaffolding.\n *\n * Creates a new HOTFIX-NNN_<slug>.md file in `.cleargate/delivery/pending-sync/`\n * from the bundled hotfix.md template, with ID auto-incremented via a scan of\n * existing HOTFIX-* files.\n *\n * Cap stub: blocks the 4th hotfix in a rolling 7-day window (pending-sync +\n * archive files modified within the last 7 days). Node fs APIs only — no\n * shell-outs (cross-OS per BUG-010 §4b).\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface HotfixCliOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override cwd for the repo root (test seam). */\n cwd?: string;\n /** Override the current ISO timestamp (test seam). */\n now?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nconst SLUG_RE = /^[a-z0-9-]+$/;\nconst HOTFIX_FILE_RE = /^HOTFIX-(\\d+)_.*\\.md$/;\n\n/**\n * Scan pending-sync/ for HOTFIX-NNN_*.md files; return the highest NNN found\n * (or 0 if none). Synchronous — no async needed for the tiny delivery dir.\n */\nfunction maxHotfixId(pendingDir: string): number {\n let max = 0;\n let entries: string[];\n try {\n entries = fs.readdirSync(pendingDir);\n } catch {\n return 0;\n }\n for (const entry of entries) {\n const m = HOTFIX_FILE_RE.exec(entry);\n if (m) {\n const n = parseInt(m[1]!, 10);\n if (n > max) max = n;\n }\n }\n return max;\n}\n\n/**\n * Count active hotfixes for the rolling-window cap.\n *\n * Counts:\n * - All HOTFIX-*.md files in pending-sync/ (regardless of mtime — they are\n * by definition active/in-flight).\n * - HOTFIX-*.md files in archive/ whose mtime is within the last 7 days\n * (recently merged/resolved within the window).\n */\nfunction countActiveHotfixes(repoRoot: string): number {\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(repoRoot, '.cleargate', 'delivery', 'archive');\n const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;\n\n let count = 0;\n\n // All in pending-sync\n let pendingEntries: string[] = [];\n try {\n pendingEntries = fs.readdirSync(pendingDir);\n } catch {\n // dir may not exist in test fixtures\n }\n for (const entry of pendingEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) count++;\n }\n\n // Recently archived (within 7-day window)\n let archiveEntries: string[] = [];\n try {\n archiveEntries = fs.readdirSync(archiveDir);\n } catch {\n // dir may not exist\n }\n for (const entry of archiveEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) {\n try {\n const stat = fs.statSync(path.join(archiveDir, entry));\n if (stat.mtimeMs >= sevenDaysAgo) count++;\n } catch {\n // Skip unreadable entries\n }\n }\n }\n\n return count;\n}\n\n/**\n * Resolve the path to `.cleargate/templates/hotfix.md` relative to the repo root.\n */\nfunction resolveTemplatePath(repoRoot: string): string {\n return path.join(repoRoot, '.cleargate', 'templates', 'hotfix.md');\n}\n\n// ─── hotfixNewHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate hotfix new <slug>`\n *\n * - Validates slug matches ^[a-z0-9-]+$.\n * - Checks rolling-window hotfix cap (≤3 per 7 days).\n * - Reads .cleargate/templates/hotfix.md and substitutes {ID}, {SLUG}, {ISO}.\n * - Writes to .cleargate/delivery/pending-sync/HOTFIX-<NNN>_<slug>.md\n * (slug with - replaced by _ in filename, matching STORY-NNN-NN_Name.md convention).\n *\n * Exits 1 on validation failure or cap exceeded; exits 0 on success.\n */\nexport function hotfixNewHandler(\n opts: { slug: string },\n cli?: HotfixCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const repoRoot = cli?.cwd ?? process.cwd();\n const now = cli?.now ?? new Date().toISOString();\n\n // ── Validate slug ────────────────────────────────────────────────────────\n if (!SLUG_RE.test(opts.slug)) {\n stderrFn(`[cleargate hotfix new] slug must match ^[a-z0-9-]+$ (got: \"${opts.slug}\")`);\n return exitFn(1);\n }\n\n // ── Cap check ────────────────────────────────────────────────────────────\n const activeCount = countActiveHotfixes(repoRoot);\n if (activeCount >= 3) {\n stderrFn(\n `Hotfix cap: ≤3 per rolling 7-day window. Currently ${activeCount} active. Bundle into a sprint or downgrade one to a CR.`,\n );\n return exitFn(1);\n }\n\n // ── Next ID ──────────────────────────────────────────────────────────────\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const maxId = maxHotfixId(pendingDir);\n const nextId = maxId + 1;\n const idStr = `HOTFIX-${String(nextId).padStart(3, '0')}`;\n\n // ── Read template ────────────────────────────────────────────────────────\n const templatePath = resolveTemplatePath(repoRoot);\n let templateContent: string;\n try {\n templateContent = fs.readFileSync(templatePath, 'utf8');\n } catch {\n stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);\n return exitFn(2);\n }\n\n // ── Substitute placeholders ──────────────────────────────────────────────\n const content = templateContent\n .replace(/\\{ID\\}/g, idStr)\n .replace(/\\{SLUG\\}/g, opts.slug)\n .replace(/\\{ISO\\}/g, now);\n\n // ── Write output file ────────────────────────────────────────────────────\n // Filename convention: HOTFIX-NNN_slug_with_underscores.md\n const fileSlug = opts.slug.replace(/-/g, '_');\n const fileName = `${idStr}_${fileSlug}.md`;\n const outPath = path.join(pendingDir, fileName);\n\n try {\n fs.mkdirSync(pendingDir, { recursive: true });\n fs.writeFileSync(outPath, content, 'utf8');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n stderrFn(`[cleargate hotfix new] write failed: ${msg}`);\n return exitFn(1);\n }\n\n stdoutFn(`[cleargate hotfix new] created: ${outPath}`);\n // Explicit exit 0 to satisfy the exit-seam contract used in tests.\n return exitFn(0);\n}\n","/**\n * `cleargate mcp serve` — stdio↔HTTP MCP proxy with auto-refresh auth.\n *\n * BUG-019 fix. Claude Code spawns this as a stdio MCP server (see\n * inject-mcp-json.ts). Each line on stdin is a JSON-RPC message; the proxy\n * POSTs it to the cleargate-mcp HTTPS `/mcp` endpoint with `Authorization:\n * Bearer <access_token>`, then writes the response (single JSON OR SSE-framed\n * data events) back to stdout, one JSON-RPC message per line.\n *\n * Auth model:\n * - boot: load refresh_token from keychain → POST /auth/refresh → cache\n * access_token + persist rotated refresh_token.\n * - per-request: lazy refresh ~60s before access expiry; on 401 invalidate\n * and retry once.\n * - if refresh itself fails (no token / revoked): print actionable hint to\n * stderr and exit non-zero so Claude Code surfaces \"auth failed\".\n *\n * Notification messages (id-less JSON-RPC) get no stdout response per spec.\n *\n * Streaming: stateless server returns single JSON for cleargate's tools. SSE\n * is handled defensively for forward-compat (parses `data:` events line-wise).\n */\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\nimport { loadConfig } from '../config.js';\nimport { createTokenStore } from '../auth/factory.js';\nimport { AuthFetcher, RefreshError } from '../auth/refresh.js';\n\nexport interface McpServeOptions {\n profile: string;\n /** Override baseUrl (defaults to canonical hosted server). */\n mcpUrlFlag?: string;\n /** Test seam: replaces globalThis.fetch. */\n fetch?: typeof globalThis.fetch;\n /** Test seam: replaces createTokenStore. */\n createStore?: typeof createTokenStore;\n /** Test seam: input stream (default process.stdin). */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: output writer (default process.stdout.write). */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer. */\n stderr?: (s: string) => void;\n /** Test seam: process.exit replacement. */\n exit?: (code: number) => never;\n /** Test seam: replaces Date.now in AuthFetcher. */\n now?: () => number;\n /** Test seam: keychain service override (forwarded to TokenStore). */\n keychainService?: string;\n /** Test seam: forced TokenStore backend (forwarded). */\n forceBackend?: 'keychain' | 'file';\n}\n\nconst DEFAULT_BASE_URL = 'https://cleargate-mcp.soula.ge';\n\nexport async function mcpServeHandler(opts: McpServeOptions): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n const cfg = loadConfig({\n flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },\n });\n const baseUrl = cfg.mcpUrl ?? DEFAULT_BASE_URL;\n\n const store = await (opts.createStore ?? createTokenStore)({\n ...(opts.keychainService !== undefined ? { keychainService: opts.keychainService } : {}),\n ...(opts.forceBackend !== undefined ? { forceBackend: opts.forceBackend } : {}),\n });\n\n const fetcher = new AuthFetcher({\n baseUrl,\n loadRefresh: () => store.load(opts.profile),\n saveRefresh: (t) => store.save(opts.profile, t),\n ...(opts.fetch !== undefined ? { fetch: opts.fetch } : {}),\n ...(opts.now !== undefined ? { now: opts.now } : {}),\n });\n\n // Boot-time refresh so failures surface early (Claude Code shows auth-failed).\n try {\n await fetcher.getAccessToken();\n } catch (err) {\n if (err instanceof RefreshError) {\n stderr(\n `cleargate mcp serve: refresh failed (${err.status} ${err.code}). ` +\n `Run \\`cleargate join <invite-url>\\` to re-authenticate.\\n`,\n );\n } else {\n stderr(\n `cleargate mcp serve: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n return exit(1);\n }\n\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n for await (const line of rl) {\n if (!line.trim()) continue;\n try {\n await proxyOne(line, baseUrl, fetcher, fetchFn, stdout, stderr);\n } catch (err) {\n // Emit an internal-error JSON-RPC response if we have an id; otherwise\n // log and continue.\n const errMsg = err instanceof Error ? err.message : String(err);\n stderr(`cleargate mcp serve: proxy error: ${errMsg}\\n`);\n const id = extractId(line);\n if (id !== undefined) {\n stdout(\n JSON.stringify({\n jsonrpc: '2.0',\n id,\n error: { code: -32603, message: `proxy error: ${errMsg}` },\n }) + '\\n',\n );\n }\n }\n }\n}\n\nasync function proxyOne(\n line: string,\n baseUrl: string,\n fetcher: AuthFetcher,\n fetchFn: typeof globalThis.fetch,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n): Promise<void> {\n let parsed: { id?: unknown };\n try {\n parsed = JSON.parse(line) as { id?: unknown };\n } catch {\n stderr(`cleargate mcp serve: ignoring non-JSON line: ${line.slice(0, 80)}\\n`);\n return;\n }\n const isNotification = !('id' in parsed) || parsed.id === undefined || parsed.id === null;\n\n let access = await fetcher.getAccessToken();\n let res = await postFrame(baseUrl, line, access, fetchFn);\n\n if (res.status === 401) {\n fetcher.invalidate();\n access = await fetcher.getAccessToken();\n res = await postFrame(baseUrl, line, access, fetchFn);\n }\n\n if (isNotification) {\n // No response body expected. Drain to free the connection.\n await res.arrayBuffer().catch(() => undefined);\n return;\n }\n\n const ct = res.headers.get('content-type') ?? '';\n if (ct.includes('text/event-stream')) {\n await streamSse(res, stdout);\n } else {\n const text = await res.text();\n if (text.length > 0) stdout(text + '\\n');\n }\n}\n\nasync function postFrame(\n baseUrl: string,\n body: string,\n accessToken: string,\n fetchFn: typeof globalThis.fetch,\n): Promise<Response> {\n return fetchFn(`${baseUrl}/mcp`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json, text/event-stream',\n Authorization: `Bearer ${accessToken}`,\n },\n body,\n });\n}\n\nasync function streamSse(res: Response, stdout: (s: string) => void): Promise<void> {\n if (!res.body) return;\n const reader = res.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buf = '';\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl: number;\n while ((nl = buf.indexOf('\\n')) !== -1) {\n const ln = buf.slice(0, nl);\n buf = buf.slice(nl + 1);\n if (ln.startsWith('data:')) {\n const payload = ln.slice(5).trim();\n if (payload) stdout(payload + '\\n');\n }\n // ignore comments + event/id lines for our minimal proxy\n }\n }\n}\n\nfunction extractId(line: string): unknown {\n try {\n const obj = JSON.parse(line) as { id?: unknown };\n return 'id' in obj ? obj.id : undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * refresh.ts — token refresh exchange against the cleargate MCP server.\n *\n * `cleargate join` saves a refresh token to the OS keychain. To talk to the\n * `/mcp` HTTP endpoint Claude Code (or any client) needs a short-lived\n * access token. This module exchanges refresh→access via POST /auth/refresh,\n * persists the rotated refresh token back to the keychain, and exposes a\n * small `AuthFetcher` that lazy-refreshes before token expiry and on 401.\n *\n * BUG-019 — used by `cleargate mcp serve` (and any future surfaces).\n */\n\nexport interface RefreshExchangeResponse {\n token_type: 'Bearer';\n access_token: string;\n expires_in: number;\n refresh_token: string;\n}\n\nexport interface RefreshDeps {\n fetch?: typeof globalThis.fetch;\n /** Wall-clock for tests. Default: () => Date.now(). */\n now?: () => number;\n}\n\n/**\n * One-shot refresh. Throws on non-2xx. Caller is responsible for persisting\n * the rotated refresh token.\n */\nexport async function refreshAccessToken(\n baseUrl: string,\n refreshToken: string,\n deps: RefreshDeps = {},\n): Promise<RefreshExchangeResponse> {\n const fetchFn = deps.fetch ?? globalThis.fetch;\n const res = await fetchFn(`${baseUrl}/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: string };\n throw new RefreshError(res.status, body.error ?? 'unknown_error');\n }\n\n const json = (await res.json()) as RefreshExchangeResponse;\n if (\n typeof json.access_token !== 'string' ||\n typeof json.refresh_token !== 'string' ||\n typeof json.expires_in !== 'number'\n ) {\n throw new RefreshError(500, 'malformed_response');\n }\n return json;\n}\n\nexport class RefreshError extends Error {\n constructor(public status: number, public code: string) {\n super(`refresh failed: ${status} ${code}`);\n this.name = 'RefreshError';\n }\n}\n\n/**\n * Caching wrapper. Maintains a single live access token. Refreshes when:\n * - no token cached yet\n * - cached token is within `skewSeconds` of expiry (default 60s)\n * - caller signals 401 via {@link AuthFetcher.invalidate}.\n *\n * Persists the rotated refresh token via the injected `saveRefresh` callback.\n */\nexport interface AuthFetcherOptions extends RefreshDeps {\n baseUrl: string;\n loadRefresh: () => Promise<string | null>;\n saveRefresh: (token: string) => Promise<void>;\n /** Refresh `skewSeconds` before access expiry. Default: 60. */\n skewSeconds?: number;\n}\n\nexport class AuthFetcher {\n private accessToken: string | null = null;\n private accessExpiresAt = 0;\n private inflight: Promise<string> | null = null;\n\n constructor(private readonly opts: AuthFetcherOptions) {}\n\n /** Returns a fresh access token, refreshing if needed. */\n async getAccessToken(): Promise<string> {\n const now = (this.opts.now ?? (() => Date.now()))();\n const skewMs = (this.opts.skewSeconds ?? 60) * 1000;\n if (this.accessToken && now < this.accessExpiresAt - skewMs) {\n return this.accessToken;\n }\n if (this.inflight) return this.inflight;\n\n this.inflight = this.refreshNow().finally(() => {\n this.inflight = null;\n });\n return this.inflight;\n }\n\n /** Force the next call to refresh. Used after a 401. */\n invalidate(): void {\n this.accessToken = null;\n this.accessExpiresAt = 0;\n }\n\n private async refreshNow(): Promise<string> {\n const stored = await this.opts.loadRefresh();\n if (!stored) {\n throw new RefreshError(401, 'no_refresh_token');\n }\n const exchanged = await refreshAccessToken(this.opts.baseUrl, stored, {\n ...(this.opts.fetch ? { fetch: this.opts.fetch } : {}),\n ...(this.opts.now ? { now: this.opts.now } : {}),\n });\n await this.opts.saveRefresh(exchanged.refresh_token);\n const now = (this.opts.now ?? (() => Date.now()))();\n this.accessToken = exchanged.access_token;\n this.accessExpiresAt = now + exchanged.expires_in * 1000;\n return exchanged.access_token;\n }\n}\n"],"mappings":"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","names":["resolve","body","readline","hostname","readNextLine","resolve","fs","os","path","fs","path","fs","yaml","raw","fm","stampOpts","result","fs","path","fileURLToPath","spawnSync","fs","path","fs","path","fs","path","fs","path","fs","path","state","__dirname","fs","path","fileURLToPath","compile","state","resolveDefaultTemplateDir","__dirname","fileURLToPath","fs","path","fileURLToPath","compile","state","resolveDefaultTemplateDir","__dirname","fileURLToPath","fs","path","fileURLToPath","compile","state","resolveDefaultTemplateDir","isSet","__dirname","fileURLToPath","compile","story","readFile","writeFile","existsSync","readFileSync","path","readFile","existsSync","readFileSync","readFile","state","writeFile","readline","resolve","fs","path","os","spawnSync","hostname","fileURLToPath","spawnSync","fs","path","spawnSync","EXCLUDED_SUFFIXES","BUCKET_ORDER","BUCKET_LABELS","rename","buildParentRef","buildChildrenRefs","buildPageBody","spawnSync","compile","path","fs","path","fs","path","spawnSync","yaml","PREFIX_MAP","basename","spawnSync","yaml","fs","path","yaml","wiki","fs","path","fs","path","readline","resolve","fs","path","spawnSync","state","parseCachedGateResult","spawnSync","gate","fs","path","spawnSync","fs","path","yaml","fs","path","fs","yaml","yaml","yaml","gate","code","spawnSync","spawnSync","spawnSync","fs","path","spawnSync","yaml","TERMINAL_STATUSES","spawnSync","yaml","spawnSync","state","fs","path","spawnSync","defaultExit","resolveRunScript","spawnSync","state","path","spawnSync","defaultExit","resolveRunScript","spawnSync","fs","path","fs","path","basename","path","BLOCK_REGEX","state","resolve","resolve","writeAtomic","state","newSha","fs","fsp","path","execSync","execSync","writeAtomic","resolve","fsPromises","path","fs","fsPromises","path","classify","fs","os","path","resolve","fs","text","fsPromises","path","fsPromises","path","mcp","proposalId","slug","num","filename","targetPath","fs","fsPromises","path","fsPromises","path","fsPromises","path","writeAtomic","nowIso","mcp","classify","writeAtomic","fsPromises","path","mcp","entry","getItemId","writeAtomic","fsPromises","path","itemId","getItemId","mcp","writeAtomic","fsPromises","path","fs","path","os","fs","path","defaultExit","readline"]}
1
+ {"version":3,"sources":["../src/cli.ts","../package.json","../src/commands/scaffold-lint.ts","../src/lib/scaffold-blocklist.ts","../src/commands/join.ts","../src/auth/identity-flow.ts","../src/commands/stamp.ts","../src/lib/codebase-version.ts","../src/lib/stamp-frontmatter.ts","../src/wiki/parse-frontmatter.ts","../src/lib/frontmatter-yaml.ts","../src/commands/init.ts","../src/init/copy-payload.ts","../src/init/merge-settings.ts","../src/init/inject-claude-md.ts","../src/init/inject-mcp-json.ts","../src/commands/wiki-build.ts","../src/wiki/scan.ts","../src/wiki/derive-bucket.ts","../src/wiki/derive-repo.ts","../src/wiki/git-sha.ts","../src/wiki/page-schema.ts","../src/wiki/synthesis/active-sprint.ts","../src/wiki/synthesis/render.ts","../src/wiki/synthesis/open-gates.ts","../src/wiki/synthesis/product-state.ts","../src/wiki/synthesis/roadmap.ts","../src/lib/manifest.ts","../src/lib/sha256.ts","../src/lib/prompts.ts","../src/lib/identity.ts","../src/lib/scaffold-source.ts","../src/commands/wiki-ingest.ts","../src/lib/wiki/contradict.ts","../src/commands/wiki-lint.ts","../src/wiki/load-wiki.ts","../src/wiki/lint-checks.ts","../src/lib/work-item-type.ts","../src/lib/wiki-config.ts","../src/commands/wiki-query.ts","../src/commands/wiki-audit-status.ts","../src/commands/wiki-contradict.ts","../src/commands/doctor.ts","../src/lib/pricing.ts","../src/lib/registry-check.ts","../src/commands/gate.ts","../src/commands/execution-mode.ts","../src/lib/readiness-predicates.ts","../src/lib/frontmatter-cache.ts","../src/commands/gate-run.ts","../src/commands/sprint.ts","../src/lib/lifecycle-reconcile.ts","../src/commands/story.ts","../src/commands/state.ts","../src/commands/stamp-tokens.ts","../src/lib/ledger-reader.ts","../src/commands/upgrade.ts","../src/lib/changelog.ts","../src/lib/claude-md-surgery.ts","../src/lib/settings-json-surgery.ts","../src/lib/merge-ui.ts","../src/lib/editor.ts","../src/commands/uninstall.ts","../src/commands/sync.ts","../src/lib/sync-log.ts","../src/lib/conflict-detector.ts","../src/lib/merge-helper.ts","../src/lib/mcp-client.ts","../src/lib/intake.ts","../src/lib/slug.ts","../src/lib/active-criteria.ts","../src/lib/comments-cache.ts","../src/lib/wiki-comments-render.ts","../src/lib/sync/work-items.ts","../src/lib/admin-url.ts","../src/commands/sync-work-items.ts","../src/commands/pull.ts","../src/commands/push.ts","../src/commands/conflicts.ts","../src/commands/sync-log.ts","../src/commands/admin-login.ts","../src/commands/hotfix.ts","../src/commands/mcp-serve.ts","../src/auth/refresh.ts"],"sourcesContent":["// SPRINT-14 M5 dogfood smoke — STORY-099-01\n// CR-011: preAction membership gating + --all flag + help-text filtering\nimport { Command, type Command as CommandType } from 'commander';\nimport pkg from '../package.json' with { type: 'json' };\nimport { scaffoldLintHandler } from './commands/scaffold-lint.js';\nimport { joinHandler } from './commands/join.js';\nimport { stampHandler } from './commands/stamp.js';\nimport { initHandler } from './commands/init.js';\nimport { wikiBuildHandler } from './commands/wiki-build.js';\nimport { wikiIngestHandler } from './commands/wiki-ingest.js';\nimport { wikiLintHandler } from './commands/wiki-lint.js';\nimport { wikiQueryHandler } from './commands/wiki-query.js';\nimport { wikiAuditStatusHandler } from './commands/wiki-audit-status.js';\nimport { wikiContradictHandler } from './commands/wiki-contradict.js';\nimport { doctorHandler } from './commands/doctor.js';\nimport { gateCheckHandler, gateExplainHandler, gateQaHandler, gateArchHandler } from './commands/gate.js';\nimport { gateRunHandler } from './commands/gate-run.js';\nimport { sprintInitHandler, sprintCloseHandler, sprintArchiveHandler, reconcileLifecycleCliHandler, sprintPreflightHandler } from './commands/sprint.js';\nimport { storyStartHandler, storyCompleteHandler } from './commands/story.js';\nimport { stateUpdateHandler, stateValidateHandler } from './commands/state.js';\nimport { stampTokensHandler } from './commands/stamp-tokens.js';\nimport { upgradeHandler } from './commands/upgrade.js';\nimport { uninstallHandler } from './commands/uninstall.js';\nimport { syncHandler, syncCheckHandler } from './commands/sync.js';\nimport { syncWorkItemsHandler } from './commands/sync-work-items.js';\nimport { pullHandler } from './commands/pull.js';\nimport { pushHandler } from './commands/push.js';\nimport { conflictsHandler } from './commands/conflicts.js';\nimport { syncLogHandler } from './commands/sync-log.js';\nimport { adminLoginHandler } from './commands/admin-login.js';\nimport { hotfixNewHandler } from './commands/hotfix.js';\nimport { mcpServeHandler } from './commands/mcp-serve.js';\nimport { getMembershipState } from './lib/membership.js';\n\n// CR-011: commands that are gated (require membership).\n// All other commands are in the open subset.\nconst GATED_COMMANDS = new Set([\n 'push',\n 'pull',\n 'sync',\n 'sync check',\n 'sync-log',\n 'conflicts',\n 'admin bootstrap-root',\n 'admin create-project',\n 'admin invite',\n 'admin issue-token',\n 'admin revoke-token',\n]);\n\n// CR-011: resolve the full subcommand name from a Commander command.\n// Handles nested subcommands like `admin login`, `wiki build`, etc.\nfunction resolveCommandName(cmd: CommandType): string {\n const name = cmd.name();\n const parent = cmd.parent;\n if (parent && parent.name() !== 'cleargate' && parent.name() !== '') {\n return `${parent.name()} ${name}`;\n }\n return name;\n}\n\nconst program = new Command();\n\nprogram\n .name('cleargate')\n .description('ClearGate CLI — connects AI agent teams to the ClearGate MCP server')\n .version(pkg.version, '-V, --version')\n .option('--profile <name>', 'configuration profile to use', 'default')\n .option('--mcp-url <url>', 'MCP server URL (overrides config file and env)')\n .option('--all', 'show all commands in help, including those requiring membership')\n .showHelpAfterError('(use `cleargate --help`)');\n\n// CR-011: preAction membership gating hook.\n// Fires before every command action. Reads program-level --profile (globals).\n// FLASHCARD #cli #commander: read program.opts().profile NOT cmd.opts().profile —\n// Commander v12 places global options on program, not on subcommands.\nprogram.hook('preAction', (_thisCommand: CommandType, actionCommand: CommandType) => {\n const cmdName = resolveCommandName(actionCommand);\n\n // Only gate commands in the GATED_COMMANDS set.\n if (!GATED_COMMANDS.has(cmdName)) return;\n\n // Read profile from program-level globals (Commander v12: globals live on program).\n const programOpts = program.opts<{ profile: string }>();\n const profile = programOpts.profile ?? 'default';\n\n const membershipState = getMembershipState({ profile });\n if (membershipState.state === 'pre-member') {\n process.stderr.write(\n `cleargate ${cmdName}: requires membership. Run: cleargate join <invite-url>\\n`\n );\n process.exit(2);\n }\n});\n\n// CR-011: help-text filtering.\n// When state is pre-member (and --all is NOT set), hide gated commands from help.\nprogram.configureHelp({\n visibleCommands: (cmd: CommandType): CommandType[] => {\n // cmd.commands returns readonly Command[]; spread to get a mutable copy.\n const allCommands: CommandType[] = [...cmd.commands];\n const programOpts = program.opts<{ all?: boolean; profile?: string }>();\n\n // If --all flag is present, show everything.\n if (programOpts.all) return allCommands;\n\n // Check membership state (cheap-path, no network).\n const profile = programOpts.profile ?? 'default';\n const membershipState = getMembershipState({ profile });\n\n // In member state, show all commands.\n if (membershipState.state === 'member') return allCommands;\n\n // In pre-member state, hide gated top-level commands.\n // Gated top-level command names (first word only for top-level filtering).\n const gatedTopLevel = new Set(['push', 'pull', 'sync', 'sync-log', 'conflicts', 'admin']);\n\n return allCommands.filter((c) => !gatedTopLevel.has(c.name()));\n },\n});\n\nprogram\n .command('join <invite-url>')\n .description('join a ClearGate workspace using an invite URL')\n .option('--auth <provider>', 'identity provider: github | email')\n .option('--non-interactive', 'fail instead of prompting (CI mode)')\n .option('--code <code>', 'OTP code for non-interactive email auth')\n .action(async (inviteUrl: string, _opts: Record<string, unknown>, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n const cmdOpts = command.opts<{ auth?: string; nonInteractive?: boolean; code?: string }>();\n await joinHandler({\n inviteUrl,\n profile: globals.profile,\n mcpUrlFlag: globals.mcpUrl,\n // FLASHCARD #cli #commander #optional-key: only set keys when defined\n ...(cmdOpts.auth !== undefined ? { auth: cmdOpts.auth } : {}),\n ...(cmdOpts.nonInteractive === true ? { nonInteractive: true } : {}),\n ...(cmdOpts.code !== undefined ? { code: cmdOpts.code } : {}),\n });\n });\n\nprogram\n .command('init')\n .description('initialise a repo with ClearGate scaffold (CLAUDE.md block, hook config, agents, templates)')\n .option('--force', 'overwrite existing files that differ from the bundled payload')\n .option('--yes', 'non-interactive: accept all defaults without prompting')\n .option('--pin <ver>', 'CR-009: pin hook resolver to a specific cleargate CLI version (default: package version)')\n .option('--from-source <path>', 'install scaffold from a local directory instead of the published npm package (meta-repo dogfood)')\n .action(async (opts: { force?: boolean; yes?: boolean; pin?: string; fromSource?: string }) => {\n await initHandler({ force: opts.force ?? false, yes: opts.yes ?? false, pin: opts.pin, fromSource: opts.fromSource });\n });\n\nprogram\n .command('whoami')\n .description('print the currently authenticated agent identity')\n .option('--json', 'CR-011: emit membership state as JSON (no network call)')\n .action(async (opts: { json?: boolean }) => {\n const { whoamiHandler } = await import('./commands/whoami.js');\n const parentOpts = program.opts<{ profile: string; mcpUrl?: string }>();\n await whoamiHandler({\n profile: parentOpts.profile,\n mcpUrlFlag: parentOpts.mcpUrl,\n json: opts.json,\n });\n });\n\nprogram\n .command('stamp <file>')\n .description('stamp ClearGate metadata fields into a file\\'s frontmatter')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampHandler(file, { dryRun: opts.dryRun });\n });\n\nconst wiki = program\n .command('wiki')\n .description('query or update the workspace wiki');\n\nwiki\n .command('build')\n .description('full rebuild of .cleargate/wiki/ from raw delivery items')\n .action(async () => {\n await wikiBuildHandler();\n });\n\nwiki\n .command('ingest <file>')\n .description('ingest a single raw delivery file into the wiki')\n .action(async (file: string) => {\n await wikiIngestHandler({ rawPath: file });\n });\n\nwiki\n .command('lint')\n .description('check wiki pages for drift vs raw sources')\n .option('--suggest', 'advisory mode — exit 0, emit suggestions only')\n .helpOption('--help', [\n 'Usage: cleargate wiki lint [--suggest]',\n '',\n 'Enforcement mode (default): exits 1 on any finding.',\n 'Suggest mode (--suggest): exits 0, prefixes findings with [advisory],',\n ' and emits Karpathy cross-ref discovery candidates.',\n ].join('\\n'))\n .action(async (_opts: Record<string, unknown>, command: Command) => {\n const cmdOpts = command.opts<{ suggest?: boolean }>();\n await wikiLintHandler({\n mode: cmdOpts.suggest ? 'suggest' : 'enforce',\n });\n });\n\nwiki\n .command('query <terms...>')\n .description('search the wiki index for matching work items')\n .option('--persist', 'write result as a topic page under wiki/topics/')\n .addHelpText('after', [\n '',\n 'NOTE: CLI synthesis is grep-and-list. For NL synthesis with the',\n 'cleargate-wiki-query subagent, invoke from a Claude Code session.',\n 'This diverges from PROPOSAL-002 §2.2 intentionally for testability',\n 'and offline/scripted use.',\n ].join('\\n'))\n .action(async (terms: string[], opts: { persist?: boolean }) => {\n await wikiQueryHandler({\n query: terms.join(' '),\n persist: opts.persist ?? false,\n });\n });\n\nwiki\n .command('audit-status')\n .description('detect raw-item status/location drift; --fix applies safe corrections')\n .option('--fix', 'apply safe status corrections to frontmatter')\n .option('--yes', 'required together with --fix to confirm writes')\n .option('--quiet', 'suppress diff output')\n .action(async (opts: { fix?: boolean; yes?: boolean; quiet?: boolean }) => {\n await wikiAuditStatusHandler(opts);\n });\n\nwiki\n .command('contradict <file>')\n .description('run the wiki contradiction check against a single page (advisory)')\n .option('--dry-run', 'print findings without mutating wiki/contradictions.md or stamping last_contradict_sha')\n .addHelpText('after', [\n '',\n 'Two-step flow (Mode A — in-agent-session):',\n ' 1. cleargate wiki contradict <file>',\n ' Runs deterministic prep (status filter, SHA idempotency, neighborhood',\n ' collection, prompt) and emits a `phase4:` JSON line to stdout.',\n ' The calling agent reads this, spawns cleargate-wiki-contradict via Task,',\n ' and receives findings.',\n ' 2. The agent calls commitPhase4Findings (or a follow-up commit subcommand)',\n ' to write findings to wiki/contradictions.md and stamp last_contradict_sha.',\n '',\n 'With --dry-run: print findings (or skipped notice) without any state mutation.',\n ].join('\\n'))\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await wikiContradictHandler({\n filePath: file,\n ...(opts.dryRun === true ? { dryRun: true } : {}),\n });\n });\n\nconst gate = program\n .command('gate')\n .description('evaluate readiness gates for a ClearGate work-item file');\n\ngate\n .command('check <file>')\n .description('evaluate readiness criteria and write result to frontmatter')\n .option('-v, --verbose', 'show full expected-vs-actual detail per criterion')\n .option('--transition <name>', 'override auto-detected transition name')\n .action(async (file: string, opts: { verbose?: boolean; transition?: string }) => {\n await gateCheckHandler(file, { verbose: opts.verbose, transition: opts.transition });\n });\n\ngate\n .command('explain <file>')\n .description('render cached gate result in ≤50 agent tokens (read-only)')\n .action(async (file: string) => {\n await gateExplainHandler(file);\n });\n\ngate\n .command('qa <worktree> <branch>')\n .description('run QA pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateQaHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\ngate\n .command('arch <worktree> <branch>')\n .description('run Architect pre-gate scanner on a story worktree (v2 only — inert under v1)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((worktree: string, branch: string, opts: { sprint?: string }) => {\n gateArchHandler({ worktree, branch }, { sprintId: opts.sprint });\n });\n\n// STORY-018-03: config-driven gates. Commander v12 does NOT treat `<name>` as a\n// catch-all fallback when sibling literal subcommands exist (QA'd on 2026-04-25\n// — it emits \"unknown command\" before a parameterized handler can fire). Since\n// the gate names are a closed set, enumerate them explicitly.\nfor (const gateName of ['precommit', 'test', 'typecheck', 'lint'] as const) {\n gate\n .command(gateName)\n .description(`run the configured ${gateName} gate command`)\n .option('--strict', 'exit non-zero if gate not configured')\n .action((opts: { strict?: boolean }) => {\n gateRunHandler(gateName, { strict: opts.strict === true ? true : undefined });\n });\n}\n\nprogram\n .command('scaffold-lint')\n .description('grep cleargate-planning/ for stack-specific strings; fail on leaks')\n .option('--fix-hint', 'emit placeholder suggestions per finding')\n .option('--versions', 'also flag semver-shaped strings')\n .option('--quiet', 'suppress per-finding output; exit code only')\n .action(async (opts: { fixHint?: boolean; versions?: boolean; quiet?: boolean }) => {\n await scaffoldLintHandler(opts);\n });\n\nconst sprint = program\n .command('sprint')\n .description('sprint lifecycle commands (v2 only — inert under v1)');\n\nsprint\n .command('init <sprint-id>')\n .description('initialise a new sprint — creates state.json and worktree skeleton')\n .requiredOption('--stories <csv>', 'comma-separated story IDs for this sprint')\n .option('--allow-drift', 'CR-017: permit lifecycle drift at sprint kickoff (v1 warn-only); does NOT waive decomposition gate')\n .action((sprintId: string, opts: { stories: string; allowDrift?: boolean }) => {\n sprintInitHandler({ sprintId, stories: opts.stories, allowDrift: opts.allowDrift });\n });\n\nsprint\n .command('close <sprint-id>')\n .description('close a sprint — validates all stories are terminal, runs prefill + suggest_improvements')\n .option('--assume-ack', 'skip the \"waiting for Reporter\" gate and flip state to Completed directly')\n .action((sprintId: string, opts: { assumeAck?: boolean }) => {\n const handlerOpts: { sprintId: string; assumeAck?: boolean } = { sprintId };\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n if (opts.assumeAck === true) {\n handlerOpts.assumeAck = true;\n }\n sprintCloseHandler(handlerOpts);\n });\n\n// CR-017: `cleargate sprint reconcile-lifecycle <sprint-id>`\n// Pure wrapper around reconcileLifecycle. Used by close_sprint.mjs (Step 2.6).\nsprint\n .command('reconcile-lifecycle <sprint-id>')\n .description('CR-017: check lifecycle status of artifacts referenced in this sprint\\'s commits (exits 1 on drift)')\n .option('--since <iso-date>', 'start of git log range (default: sprint start_date or 90 days ago)')\n .option('--until <iso-date>', 'end of git log range (default: now)')\n .action((sprintId: string, opts: { since?: string; until?: string }) => {\n reconcileLifecycleCliHandler({ sprintId, since: opts.since, until: opts.until });\n });\n\nsprint\n .command('archive <sprint-id>')\n .description('archive a completed sprint — move pending-sync files, clear .active, merge + delete sprint branch')\n .option('--dry-run', 'print the archive plan without making any changes')\n .option('--allow-wiki-lint-debt', 'CR-022 M5: waive wiki-lint findings during archive (mirrors --allow-drift pattern)')\n .action(async (sprintId: string, opts: { dryRun?: boolean; allowWikiLintDebt?: boolean }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const handlerOpts: { sprintId: string; dryRun?: boolean; allowWikiLintDebt?: boolean } = { sprintId };\n if (opts.dryRun === true) {\n handlerOpts.dryRun = true;\n }\n if (opts.allowWikiLintDebt === true) {\n handlerOpts.allowWikiLintDebt = true;\n }\n await sprintArchiveHandler(handlerOpts);\n });\n\n// CR-021: `cleargate sprint preflight <sprint-id>`\n// Runs the four Gate 3 (Sprint Execution) environment-health checks.\nsprint\n .command('preflight <sprint-id>')\n .description('CR-021: run four Gate 3 checks before transitioning a sprint Ready → Active')\n .addHelpText('after', [\n '',\n 'Checks (run in order):',\n ' 1. Previous sprint Completed — no prior sprint may be in Active/Ready state.',\n ' 2. No leftover worktrees — .worktrees/STORY-* directories must not exist.',\n ' 3. sprint/S-NN ref free — the sprint branch ref must not already exist in git.',\n ' 4. main is clean — working tree on main must have no uncommitted changes.',\n '',\n 'Exit codes:',\n ' 0 All four checks pass.',\n ' 1 One or more checks failed (punch list printed to stderr).',\n ' 2 Usage error (missing or invalid sprint-id argument).',\n '',\n 'Example:',\n ' cleargate sprint preflight SPRINT-18',\n ].join('\\n'))\n .action((sprintId: string) => {\n sprintPreflightHandler({ sprintId });\n });\n\nconst story = program\n .command('story')\n .description('story lifecycle commands (v2 only — inert under v1)');\n\nstory\n .command('start <story-id>')\n .description('create a git worktree for a story on the sprint branch')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyStartHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nstory\n .command('complete <story-id>')\n .description('mark a story complete and clean up its worktree (stub — requires complete_story.mjs)')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup')\n .action((storyId: string, opts: { sprint?: string }) => {\n storyCompleteHandler({ storyId }, { sprintId: opts.sprint });\n });\n\nconst state = program\n .command('state')\n .description('state.json management commands (v2 only — inert under v1)');\n\nstate\n .command('update <story-id> <new-state>')\n .description('update a story\\'s state in state.json')\n .option('--sprint <id>', 'sprint ID for execution_mode lookup (overrides .active sentinel)')\n .action((storyId: string, newState: string, opts: { sprint?: string }) => {\n // FLASHCARD #cli #commander #optional-key: omit key when undefined\n const cliOpts: Parameters<typeof stateUpdateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateUpdateHandler({ storyId, newState }, cliOpts);\n });\n\nstate\n .command('validate <sprint-id>')\n .description('validate all story states in a sprint\\'s state.json')\n .option('--sprint <id>', 'override sprint ID for execution_mode lookup')\n .action((sprintId: string, opts: { sprint?: string }) => {\n const cliOpts: Parameters<typeof stateValidateHandler>[1] = {};\n if (opts.sprint !== undefined) {\n cliOpts.sprintId = opts.sprint;\n }\n stateValidateHandler({ sprintId: opts.sprint ?? sprintId }, cliOpts);\n });\n\nprogram\n .command('stamp-tokens <file>')\n .description('stamp draft_tokens from token-ledger into a work-item file (hook-invoked)')\n .option('--dry-run', 'print planned changes without writing')\n .action(async (file: string, opts: { dryRun?: boolean }) => {\n await stampTokensHandler(file, { dryRun: opts.dryRun });\n });\n\nconst admin = program\n .command('admin')\n .description('administrative operations (login, create-project, invite, issue-token, revoke-token)');\n\nadmin\n .command('login')\n .description('log in as a ClearGate admin via GitHub OAuth device flow')\n .option('--mcp-url <url>', 'MCP server URL (overrides CLEARGATE_MCP_URL and config file)')\n .action(async (opts: { mcpUrl?: string }, command: Command) => {\n const globals = command.parent!.parent!.opts<{ mcpUrl?: string }>();\n await adminLoginHandler({\n mcpUrl: opts.mcpUrl ?? globals.mcpUrl,\n });\n });\n\nadmin\n .command('bootstrap-root <handle>')\n .description('seed the first root admin in admin_users (idempotent)')\n .option('--database-url <url>', 'Postgres connection string; falls back to DATABASE_URL env')\n .option('--force', 'override second-root guard / promote non-root user to root')\n .action(async (handle: string, opts: { databaseUrl?: string; force?: boolean }) => {\n const { bootstrapRootHandler } = await import('./commands/bootstrap-root.js');\n await bootstrapRootHandler({ handle, databaseUrl: opts.databaseUrl, force: opts.force ?? false });\n });\n\nprogram\n .command('doctor')\n .description('diagnose scaffold drift, hook health, blocked items, and token cost')\n .option('--check-scaffold', 'check scaffold files for drift against install snapshot')\n .option('--session-start-mode', 'hidden: enables daily throttle (used by session-start hook)', false)\n .option('--session-start', 'emit blocked pending-sync items summary (used by SessionStart hook)')\n .option('--pricing <file>', 'compute USD cost estimate from a work item\\'s draft_tokens')\n .option('--can-edit <file>', 'CR-008: exit 0 if editing file is allowed, exit 1 if planning required')\n .option('--cwd <dir>', 'working directory for the doctor check (default: process.cwd())')\n .option('-v, --verbose', 'show per-file drift detail')\n .addHelpText('after', [\n '',\n 'Modes (mutually exclusive):',\n ' --check-scaffold Compute drift for all tracked scaffold files.',\n ' Writes .cleargate/.drift-state.json.',\n ' --session-start List blocked pending-sync items (≤10, ≤100 tokens).',\n ' --pricing <file> Compute USD estimate from a work item\\'s draft_tokens.',\n ' --can-edit <file> Check if editing a file requires a planning work item.',\n ' (default) Print a minimal hook-config health report.',\n '',\n 'Exit codes:',\n ' 0 Clean — no blockers, no config errors.',\n ' 1 Blocked items or advisory issues — see stdout.',\n ' 2 ClearGate misconfigured or partially installed — see stdout for remediation.',\n ].join('\\n'))\n .action(async (opts: { checkScaffold?: boolean; sessionStartMode?: boolean; sessionStart?: boolean; pricing?: string; canEdit?: string; cwd?: string; verbose?: boolean }) => {\n await doctorHandler({\n checkScaffold: opts.checkScaffold,\n sessionStartMode: opts.sessionStartMode,\n sessionStart: opts.sessionStart,\n pricing: !!opts.pricing,\n pricingFile: opts.pricing,\n canEdit: !!opts.canEdit,\n canEditFile: opts.canEdit,\n verbose: opts.verbose,\n }, opts.cwd ? { cwd: opts.cwd } : undefined);\n });\n\nprogram\n .command('upgrade')\n .description('three-way merge scaffold files with upstream changes')\n .option('--dry-run', 'print plan without making any changes')\n .option('--yes', 'auto-accept \"take theirs\" for all merge-3way files (non-interactive)')\n .option('--only <tier>', 'restrict to a specific scaffold tier (protocol/template/agent/hook/skill/cli-config)')\n .addHelpText('after', [\n '',\n 'Overwrite policies:',\n ' always — silent overwrite with package content',\n ' never — silent skip',\n ' preserve — silent skip',\n ' merge-3way — interactive: [k]eep mine / [t]ake theirs / [e]dit in $EDITOR',\n '',\n '--yes auto-accepts [t]ake theirs for all merge-3way files.',\n ].join('\\n'))\n .action(async (opts: { dryRun?: boolean; yes?: boolean; only?: string }) => {\n await upgradeHandler({ dryRun: opts.dryRun, yes: opts.yes, only: opts.only });\n });\n\nprogram\n .command('uninstall')\n .description('remove ClearGate scaffold from a project (preservation-first)')\n .option('--dry-run', 'preview planned actions without making any changes (CI-safe)')\n .option('--preserve <tiers>', 'comma-separated tier ids to force-preserve (default: user-artifact)')\n .option('--remove <tiers>', 'comma-separated tier ids to force-remove; use \"all\" to remove everything including user artifacts (DANGEROUS)')\n .option('--yes', 'skip typed project-name confirmation (dangerous — use in scripts/CI)')\n .option('--path <dir>', 'target directory (must contain .cleargate/.install-manifest.json); defaults to cwd')\n .option('--force', 'bypass uncommitted-changes safety check (not applicable for non-git targets)')\n .addHelpText('after', [\n '',\n 'Preservation defaults:',\n ' user-artifact tier → kept (FLASHCARD.md, archive, pending-sync, sprint REPORT.md)',\n ' framework tiers → removed (protocol, template, agent, hook, skill, cli-config)',\n '',\n 'Always removed (no prompt): .claude/agents/*.md, ClearGate hooks,',\n ' .claude/skills/flashcard/, CLAUDE.md CLEARGATE block,',\n ' `cleargate` from package.json, .install-manifest.json, .drift-state.json.',\n '',\n 'Non-git targets: uncommitted-changes check is skipped silently.',\n ].join('\\n'))\n .action(async (opts: {\n dryRun?: boolean;\n preserve?: string;\n remove?: string;\n yes?: boolean;\n path?: string;\n force?: boolean;\n }) => {\n await uninstallHandler({\n dryRun: opts.dryRun,\n preserve: opts.preserve ? opts.preserve.split(',').map((s) => s.trim()) : undefined,\n remove: opts.remove ? opts.remove.split(',').map((s) => s.trim()) : undefined,\n yes: opts.yes,\n path: opts.path,\n force: opts.force,\n });\n });\n\nconst syncCmd = program\n .command('sync')\n .description('pull remote updates, resolve conflicts, push local changes (or `sync work-items` to push items to MCP)')\n .option('--dry-run', 'print plan without making any changes or sync-log entries')\n .option('--check', 'read-only drift probe — prints JSON, no mutation, hook-safe')\n .action(async (opts: { dryRun?: boolean; check?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n if (opts.check) {\n await syncCheckHandler({ profile: globals.profile });\n return;\n }\n await syncHandler({ dryRun: opts.dryRun ?? false, profile: globals.profile });\n });\n\n// STORY-023-01: `cleargate sync work-items` — push all local work items to MCP\nsyncCmd\n .command('work-items')\n .description('push all local work items (every status) to the MCP server (status-blind, idempotent)')\n .action(async (_opts: Record<string, unknown>, command: Command) => {\n const globals = command.parent!.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await syncWorkItemsHandler({ profile: globals.profile });\n });\n\nprogram\n .command('pull <id-or-remote-id>')\n .description('pull a single item from the remote PM tool by local ID or remote_id')\n .option('--comments', 'also pull comments for this item (STORY-010-06; not yet implemented)')\n .action(async (idOrRemoteId: string, opts: { comments?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pullHandler(idOrRemoteId, { comments: opts.comments, profile: globals.profile });\n });\n\nprogram\n .command('push <file>')\n .description('push a local work item to the MCP server (requires approved: true in frontmatter)')\n .option('--revert <id-or-remote-id>', 'soft-revert a pushed item by setting status to archived-without-shipping')\n .option('--force', 'bypass the \"done\" status guard when reverting')\n .addHelpText('after', [\n '',\n 'Push mode:',\n ' Reads local frontmatter. Requires approved: true — exits 1 without network call otherwise.',\n ' On success: writes pushed_by + pushed_at from server back to local frontmatter.',\n ' Appends sync-log entry op=push.',\n '',\n 'Revert mode (--revert <id-or-remote-id>):',\n ' Calls cleargate_sync_status with status=archived-without-shipping.',\n ' Does NOT delete the remote item or remove local remote_id.',\n ' Refuses if local status=done unless --force is passed.',\n ' Appends sync-log entry op=push-revert.',\n ].join('\\n'))\n .action(async (file: string, opts: { revert?: string; force?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await pushHandler(file, { revert: opts.revert, force: opts.force, profile: globals.profile });\n });\n\nprogram\n .command('conflicts')\n .description('list unresolved sync conflicts from .cleargate/.conflicts.json')\n .option('--refresh', 'force a new /auth/refresh even if the cached token is still valid')\n .action(async (opts: { refresh?: boolean }, command: Command) => {\n const globals = command.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await conflictsHandler({ refresh: opts.refresh, profile: globals.profile });\n });\n\nprogram\n .command('sync-log')\n .description('filter and print sync-log entries')\n .option('--actor <email>', 'filter by actor email')\n .option('--op <op>', 'filter by operation (push|pull|pull-intake|...)')\n .option('--target <id>', 'filter by target work item ID')\n .option('--limit <n>', 'maximum number of entries to show (default 50)', '50')\n .action(async (opts: { actor?: string; op?: string; target?: string; limit?: string }) => {\n await syncLogHandler({\n actor: opts.actor,\n op: opts.op,\n target: opts.target,\n limit: opts.limit !== undefined ? parseInt(opts.limit, 10) : 50,\n });\n });\n\nconst hotfix = program\n .command('hotfix')\n .description('hotfix lane commands (off-sprint trivial fix scaffolding)');\n\n// FLASHCARD #cli #commander #subcommand-routing (2026-04-25): Commander v12\n// does NOT treat `<verb>` as a catch-all fallback when sibling literal\n// subcommands exist. Since `new` is the only verb for now, enumerate it\n// explicitly as a literal subcommand.\nhotfix\n .command('new <slug>')\n .description('scaffold a new HOTFIX-NNN_<slug>.md in pending-sync/')\n .action((slug: string) => {\n hotfixNewHandler({ slug });\n });\n\n// BUG-019: stdio↔HTTP MCP proxy with auto-refresh auth.\nconst mcp = program\n .command('mcp')\n .description('MCP-server bridge commands (stdio shim, registration helpers)');\n\nmcp\n .command('serve')\n .description('run a stdio MCP server that proxies to the cleargate HTTP /mcp endpoint with auto-refresh Bearer auth')\n .action(async (_opts, command: Command) => {\n const globals = command.parent!.parent!.opts<{ profile: string; mcpUrl?: string }>();\n await mcpServeHandler({\n profile: globals.profile,\n ...(globals.mcpUrl !== undefined ? { mcpUrlFlag: globals.mcpUrl } : {}),\n });\n });\n\nvoid program.parseAsync(process.argv);\n","{\n \"name\": \"cleargate\",\n \"version\": \"0.10.0\",\n \"private\": false,\n \"type\": \"module\",\n \"description\": \"Planning framework for Claude Code agents — sprint/epic/story protocol, four-agent loop (architect/developer/qa/reporter), Karpathy-style awareness wiki.\",\n \"license\": \"MIT\",\n \"bin\": {\n \"cleargate\": \"dist/cli.js\"\n },\n \"main\": \"./dist/cli.cjs\",\n \"module\": \"./dist/cli.js\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/cli.d.ts\",\n \"import\": \"./dist/cli.js\",\n \"require\": \"./dist/cli.cjs\"\n },\n \"./admin-api\": {\n \"types\": \"./dist/admin-api/index.d.ts\",\n \"import\": \"./dist/admin-api/index.js\",\n \"require\": \"./dist/admin-api/index.cjs\"\n },\n \"./lib/ledger\": {\n \"types\": \"./dist/lib/ledger.d.ts\",\n \"import\": \"./dist/lib/ledger.js\",\n \"require\": \"./dist/lib/ledger.cjs\"\n },\n \"./lib/ledger.js\": {\n \"types\": \"./dist/lib/ledger.d.ts\",\n \"import\": \"./dist/lib/ledger.js\",\n \"require\": \"./dist/lib/ledger.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"templates\",\n \"README.md\",\n \"LICENSE\",\n \"CHANGELOG.md\"\n ],\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"scripts\": {\n \"prebuild\": \"tsx scripts/build-manifest.ts && node scripts/copy-planning-payload.mjs\",\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"pretest\": \"npm run build\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@napi-rs/keyring\": \"^1.2.0\",\n \"commander\": \"^12\",\n \"diff\": \"^5.2.2\",\n \"js-yaml\": \"^4.1.0\",\n \"pg\": \"^8.12.0\",\n \"zod\": \"^4.3.0\"\n },\n \"devDependencies\": {\n \"@types/diff\": \"^5.2.3\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^24.0.0\",\n \"@types/pg\": \"^8.11.10\",\n \"tsup\": \"^8\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.8.0\",\n \"vitest\": \"^2.1.0\"\n }\n}\n","/**\n * scaffold-lint.ts — `cleargate scaffold-lint` command handler.\n *\n * STORY-018-04: Scans cleargate-planning/ for stack-specific strings that\n * should not appear in the installable scaffold.\n *\n * Exit codes: 0 = clean, 1 = findings, 2 = config/parse error.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n DEFAULT_BLOCKLIST,\n getTermCategory,\n CATEGORY_PLACEHOLDERS,\n parseAllowlist,\n parseUserBlocklist,\n type AllowlistEntry,\n} from '../lib/scaffold-blocklist.js';\n\n// ─── Scan extensions ──────────────────────────────────────────────────────────\n\nconst SCAN_EXTENSIONS = new Set(['.md', '.sh', '.mjs', '.json']);\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ScaffoldLintOptions {\n fixHint?: boolean;\n versions?: boolean;\n quiet?: boolean;\n cwd?: string;\n planningDir?: string; // override for tests; defaults to path.join(cwd, 'cleargate-planning')\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n}\n\ninterface Finding {\n file: string; // relative to planningDir parent\n line: number;\n term: string;\n context: string; // matched line truncated to 80 chars\n}\n\n// ─── scaffoldLintHandler ──────────────────────────────────────────────────────\n\nexport function scaffoldLintHandler(opts: ScaffoldLintOptions): void {\n const stdoutFn = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n opts.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = opts.cwd ?? process.cwd();\n const planningDir = opts.planningDir ?? path.join(cwd, 'cleargate-planning');\n\n const result = runScaffoldLint(opts, cwd, planningDir, stdoutFn, stderrFn);\n\n if (result.exitCode === 0) {\n stdoutFn('scaffold-lint: clean');\n }\n\n if (result.exitCode !== 0) {\n exitFn(result.exitCode);\n }\n}\n\ninterface LintResult {\n exitCode: number;\n}\n\nfunction runScaffoldLint(\n opts: ScaffoldLintOptions,\n cwd: string,\n planningDir: string,\n stdoutFn: (s: string) => void,\n stderrFn: (s: string) => void,\n): LintResult {\n // ── 1. Load user blocklist (fail-fast on malformed) ─────────────────────────\n const userBlocklistPath = path.join(cwd, '.cleargate', 'scaffold-blocklist.txt');\n let userTerms: string[] = [];\n\n if (fs.existsSync(userBlocklistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(userBlocklistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: error reading ${userBlocklistPath}: ${String(err)}`);\n return { exitCode: 2 };\n }\n const parsed = parseUserBlocklist(raw, userBlocklistPath, stderrFn);\n if (parsed === null) {\n return { exitCode: 2 };\n }\n userTerms = parsed;\n }\n\n // ── 2. Load allowlist (skip + warn on malformed) ──────────────────────────\n const allowlistPath = path.join(cwd, '.cleargate', 'scaffold-allowlist.txt');\n let allowlistEntries: AllowlistEntry[] = [];\n\n if (fs.existsSync(allowlistPath)) {\n let raw: string;\n try {\n raw = fs.readFileSync(allowlistPath, 'utf8');\n } catch (err) {\n stderrFn(`scaffold-lint: warning: error reading ${allowlistPath}: ${String(err)}`);\n }\n try {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n allowlistEntries = parseAllowlist(raw!, allowlistPath, stderrFn);\n } catch {\n // parseAllowlist is synchronous and doesn't throw in normal usage\n }\n }\n\n // ── 3. Build combined blocklist ───────────────────────────────────────────\n const allTerms = [...DEFAULT_BLOCKLIST, ...userTerms];\n\n // ── 4. Walk cleargate-planning/ ───────────────────────────────────────────\n if (!fs.existsSync(planningDir)) {\n // No planning dir — nothing to scan, exit clean\n stdoutFn('scaffold-lint: clean');\n return { exitCode: 0 };\n }\n\n const files = walkDir(planningDir);\n\n // ── 5. Scan files ─────────────────────────────────────────────────────────\n const findings: Finding[] = [];\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!SCAN_EXTENSIONS.has(ext)) continue;\n\n let content: string;\n try {\n content = fs.readFileSync(filePath, 'utf8');\n } catch {\n continue; // skip unreadable files\n }\n\n // Relative path from cwd for reporting\n const relPath = path.relative(cwd, filePath).replace(/\\\\/g, '/');\n\n const lines = content.split('\\n');\n for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {\n const lineNum = lineIdx + 1;\n const lineText = lines[lineIdx];\n\n for (const term of allTerms) {\n const re = new RegExp(escapeRegex(term), 'gi');\n if (!re.test(lineText)) continue;\n\n // Check allowlist\n if (isAllowlisted(relPath, term, allowlistEntries)) continue;\n\n findings.push({\n file: relPath,\n line: lineNum,\n term: term.toLowerCase(),\n context: lineText.slice(0, 80),\n });\n // Only record the FIRST matching term per line per occurrence\n // (break inner term loop to avoid duplicate lines for the same match)\n break;\n }\n }\n }\n\n // ── 6. Sort findings by file asc, line asc ───────────────────────────────\n findings.sort((a, b) => {\n if (a.file < b.file) return -1;\n if (a.file > b.file) return 1;\n return a.line - b.line;\n });\n\n // ── 7. Emit findings ──────────────────────────────────────────────────────\n if (findings.length === 0) {\n return { exitCode: 0 };\n }\n\n if (!opts.quiet) {\n for (const f of findings) {\n const line = `${f.file}:${f.line}: ${f.term} — example context: ${f.context}`;\n stderrFn(line);\n\n if (opts.fixHint) {\n const category = getTermCategory(f.term);\n const placeholder = category ? CATEGORY_PLACEHOLDERS.get(category) : undefined;\n if (placeholder) {\n stderrFn(` hint: replace with ${placeholder}`);\n } else {\n stderrFn(` hint: replace with <your-replacement>`);\n }\n }\n }\n }\n\n return { exitCode: 1 };\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction walkDir(dir: string): string[] {\n const results: string[] = [];\n\n function recurse(current: string): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(current, { withFileTypes: true }) as fs.Dirent[];\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = path.join(current, entry.name);\n if (entry.isDirectory()) {\n recurse(fullPath);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n }\n\n recurse(dir);\n return results;\n}\n\nfunction escapeRegex(term: string): string {\n return term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction isAllowlisted(\n relPath: string,\n term: string,\n entries: AllowlistEntry[],\n): boolean {\n const termLower = term.toLowerCase();\n\n for (const entry of entries) {\n if (entry.term !== termLower) continue;\n\n if (!entry.glob) {\n // Global suppression for this term\n return true;\n }\n\n // Glob is relative to repo root (cwd)\n // Use simple star matching via path.matchesGlob (Node 24)\n if (matchesGlob(relPath, entry.glob)) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Minimal glob matcher using Node 24's path.matchesGlob when available,\n * with a fallback star-replace regex for older environments.\n */\nfunction matchesGlob(filePath: string, glob: string): boolean {\n // Node 24 built-in\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pathModule = path as any;\n if (typeof pathModule.matchesGlob === 'function') {\n return pathModule.matchesGlob(filePath, glob);\n }\n } catch {\n // fall through\n }\n\n // Fallback: simple star matching\n const regexStr = glob\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '§DSTAR§')\n .replace(/\\*/g, '[^/]*')\n .replace(/§DSTAR§/g, '.*');\n\n return new RegExp(`^${regexStr}$`).test(filePath);\n}\n","/**\n * scaffold-blocklist.ts — Default blocklist for `cleargate scaffold-lint`.\n *\n * STORY-018-04: Stack-leak detection. These terms should not appear in the\n * installable scaffold under cleargate-planning/ — they indicate ClearGate's\n * own dogfooding vocabulary leaking into the template that downstream users\n * receive.\n *\n * Categories: ORMs, web frameworks, infra, DB engines, cache/queue, styling.\n */\n\n// ─── Blocklist categories ─────────────────────────────────────────────────────\n\nconst ORMS = ['drizzle', 'prisma', 'sequelize', 'typeorm'] as const;\n\nconst WEB_FRAMEWORKS = [\n 'fastify',\n 'express',\n 'hono',\n 'svelte',\n 'sveltekit',\n 'react',\n 'next.js',\n 'nextjs',\n 'nuxt',\n 'remix',\n 'vue',\n] as const;\n\nconst INFRA = ['coolify', 'vercel', 'netlify', 'heroku', 'render.com', 'fly.io'] as const;\n\nconst DB_ENGINES = ['postgres', 'postgresql', 'mysql', 'sqlite', 'mongodb', 'dynamodb'] as const;\n\nconst CACHE_QUEUE = ['redis', 'ioredis', 'memcached', 'rabbitmq', 'kafka'] as const;\n\nconst STYLING = ['daisyui', 'tailwind', 'bootstrap', 'mui'] as const;\n\n// ─── Category map (for --fix-hint placeholders) ───────────────────────────────\n\nexport type BlocklistCategory = 'orm' | 'framework' | 'infra' | 'db' | 'cache' | 'styling';\n\nconst TERM_CATEGORY_MAP: ReadonlyMap<string, BlocklistCategory> = new Map<string, BlocklistCategory>([\n ...ORMS.map((t): [string, BlocklistCategory] => [t, 'orm']),\n ...WEB_FRAMEWORKS.map((t): [string, BlocklistCategory] => [t, 'framework']),\n ...INFRA.map((t): [string, BlocklistCategory] => [t, 'infra']),\n ...DB_ENGINES.map((t): [string, BlocklistCategory] => [t, 'db']),\n ...CACHE_QUEUE.map((t): [string, BlocklistCategory] => [t, 'cache']),\n ...STYLING.map((t): [string, BlocklistCategory] => [t, 'styling']),\n]);\n\nexport const CATEGORY_PLACEHOLDERS: ReadonlyMap<BlocklistCategory, string> = new Map([\n ['orm', '<your-orm>'],\n ['framework', '<your-framework>'],\n ['infra', '<your-infra>'],\n ['db', '<your-db>'],\n ['cache', '<your-cache>'],\n ['styling', '<your-styling>'],\n]);\n\n// ─── Public exports ───────────────────────────────────────────────────────────\n\nexport const DEFAULT_BLOCKLIST: readonly string[] = [\n ...ORMS,\n ...WEB_FRAMEWORKS,\n ...INFRA,\n ...DB_ENGINES,\n ...CACHE_QUEUE,\n ...STYLING,\n];\n\n/**\n * Get the category for a blocklist term (default or user-supplied).\n * Returns undefined for user-supplied terms that don't map to a category.\n */\nexport function getTermCategory(term: string): BlocklistCategory | undefined {\n return TERM_CATEGORY_MAP.get(term.toLowerCase());\n}\n\n// ─── Allowlist parser ─────────────────────────────────────────────────────────\n\nexport interface AllowlistEntry {\n term: string;\n glob?: string;\n}\n\nexport interface ParsedAllowlist {\n entries: AllowlistEntry[];\n warnings: string[];\n}\n\n/**\n * Parse `.cleargate/scaffold-allowlist.txt` content.\n *\n * Format per line: `<term>[ <file-glob>]`\n * - Lines starting with `#` are comments.\n * - Blank lines are skipped.\n * - Term is case-insensitive substring.\n * - Glob is optional; when absent, suppresses ALL files.\n *\n * Returns parsed entries + any warnings (for malformed lines — per orchestrator\n * decision: skip + warn rather than fail; malformed-allowlist exit-2 is\n * handled by the blocklist file, not the allowlist).\n *\n * NOTE: Per M2.md \"malformed lines: fail fast\" applies to the *blocklist* file\n * format only. For allowlist, skip + warn to stderr per orchestrator Q2 decision.\n */\nexport function parseAllowlist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): AllowlistEntry[] {\n const entries: AllowlistEntry[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n // Skip blank lines and comments\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // Split on whitespace — first token is term, optional second is glob\n const parts = trimmed.split(/\\s+/);\n\n if (parts.length === 0 || parts[0] === '') {\n stderrFn(`scaffold-lint: warning: malformed line ${i + 1} in ${filePath}: ${raw}`);\n continue;\n }\n\n const entry: AllowlistEntry = { term: parts[0].toLowerCase() };\n if (parts.length >= 2) {\n entry.glob = parts[1];\n }\n entries.push(entry);\n }\n\n return entries;\n}\n\n/**\n * Parse `.cleargate/scaffold-blocklist.txt` (user-extensible).\n * Fail-fast on malformed lines (exit code 2).\n * Returns null if a parse error was encountered (caller should exit 2).\n */\nexport function parseUserBlocklist(\n content: string,\n filePath: string,\n stderrFn: (s: string) => void,\n): string[] | null {\n const terms: string[] = [];\n const lines = content.split('\\n');\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i];\n const trimmed = raw.trim();\n\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n // A valid term is a single non-whitespace token\n if (/\\s/.test(trimmed)) {\n stderrFn(`scaffold-lint: malformed line ${i + 1} in ${filePath}: ${raw}`);\n return null; // signal exit 2\n }\n\n terms.push(trimmed.toLowerCase());\n }\n\n return terms;\n}\n","/**\n * `cleargate join` — two-step identity-bound workspace invitation redemption.\n *\n * Flow:\n * 1. Parse invite URL/token.\n * 2. Pick provider (--auth flag, interactive picker, or error in non-interactive mode).\n * 3. POST /join/:token/challenge { provider } → get challengeId + clientHints.\n * 4. Drive provider flow:\n * - GitHub: client-side device-flow polling (via identity-flow.startDeviceFlow).\n * - Email: OTP prompt with 3-in-process retries (OD-3 resolution).\n * 5. POST /join/:token/complete { challenge_id, proof } → get refresh_token.\n * 6. Seat token via TokenStore.\n *\n * CR-006 EPIC-019.\n */\nimport * as os from 'node:os';\nimport { loadConfig } from '../config.js';\nimport { createTokenStore } from '../auth/factory.js';\nimport {\n pickProvider,\n startDeviceFlow,\n mapProviderError,\n DeviceFlowError,\n IdentityFlowError,\n type Provider,\n} from '../auth/identity-flow.js';\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\nconst UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n// GitHub device-flow token endpoint (same as admin-login)\nconst GITHUB_DEVICE_FLOW_URL = 'https://github.com/login/oauth/access_token';\n\nexport interface JoinOptions {\n inviteUrl: string;\n profile: string;\n mcpUrlFlag?: string;\n /** Identity provider (--auth flag): 'github' | 'email'. */\n auth?: string;\n /** Non-interactive CI mode (--non-interactive flag). */\n nonInteractive?: boolean;\n /** Pre-seeded OTP code for non-interactive email auth (--code flag). */\n code?: string;\n /** Test seam: replaces globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n /** Test seam: replaces createTokenStore */\n createStore?: typeof createTokenStore;\n /** Test seam: replaces os.hostname() */\n hostname?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: replaces process.stdin for interactive prompts */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: whether stdin is a TTY (default: process.stdin.isTTY) */\n isTTY?: boolean;\n /** Test seam: sleepFn for device-flow polling (default: real setTimeout) */\n sleepFn?: (ms: number) => Promise<void>;\n /** Test seam: override device-flow poll interval in ms (0 = instant in tests) */\n intervalOverrideMs?: number;\n}\n\nexport async function joinHandler(opts: JoinOptions): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const hostname = opts.hostname ?? (() => os.hostname());\n const isTTY = opts.isTTY ?? (process.stdin.isTTY === true);\n\n // ── Parse inviteUrl → { token, baseUrl } ──────────────────────────────────\n let token: string;\n let baseUrl: string;\n\n try {\n if (UUID_V4_RE.test(opts.inviteUrl)) {\n // Bare UUID form — requires mcpUrl from config\n token = opts.inviteUrl;\n const cfg = loadConfig({\n flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },\n });\n if (!cfg.mcpUrl) {\n stderr(\n 'cleargate: bare invite token requires mcpUrl. Pass --mcp-url <url> or set CLEARGATE_MCP_URL.\\n',\n );\n exit(5);\n return;\n }\n baseUrl = cfg.mcpUrl;\n } else {\n // Full URL form: https://host/join/<uuid>\n const url = new URL(opts.inviteUrl);\n const m = url.pathname.match(/^\\/join\\/([0-9a-f-]{36})$/i);\n if (!m || !UUID_V4_RE.test(m[1]!)) {\n throw new Error('bad path');\n }\n token = m[1]!;\n baseUrl = url.origin;\n }\n } catch {\n stderr('cleargate: invalid invite URL or token format.\\n');\n exit(5);\n return;\n }\n\n // ── Non-interactive guards ────────────────────────────────────────────────\n if (opts.nonInteractive && !opts.auth) {\n stderr('cleargate: --auth required in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'email' && !opts.code) {\n stderr('cleargate: --code required for email provider in non-interactive mode\\n');\n exit(1);\n return;\n }\n if (opts.nonInteractive && opts.auth === 'github') {\n stderr('cleargate: GitHub auth requires browser interaction; use `--auth email` for non-interactive flows\\n');\n exit(1);\n return;\n }\n\n // ── Pick provider ─────────────────────────────────────────────────────────\n let provider: Provider;\n try {\n provider = await pickProvider({\n flag: opts.auth,\n isTTY: !opts.nonInteractive && isTTY,\n available: ['github', 'email'],\n stdin: opts.stdin,\n stdout,\n });\n } catch (err) {\n if (err instanceof IdentityFlowError) {\n stderr(`${err.message}\\n`);\n exit(1);\n return;\n }\n throw err;\n }\n\n // ── POST /challenge ────────────────────────────────────────────────────────\n let challengeRes: Response;\n try {\n challengeRes = await fetchFn(`${baseUrl}/join/${token}/challenge`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ provider }),\n });\n } catch (err) {\n stderr(\n `cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`,\n );\n exit(2);\n return;\n }\n\n if (!challengeRes.ok) {\n const body = (await challengeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n challengeRes.status,\n body.error ?? '',\n parseRetryAfter(challengeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n let challengeBody: {\n challenge_id: string;\n provider: Provider;\n expires_in: number;\n client_hints: Record<string, unknown>;\n };\n try {\n challengeBody = (await challengeRes.json()) as typeof challengeBody;\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n\n const challengeId = challengeBody.challenge_id;\n const clientHints = challengeBody.client_hints;\n\n // ── Drive provider-specific completion + POST /complete ────────────────────\n let completeRawBody: unknown;\n\n if (provider === 'github') {\n // ── GitHub device-flow: poll GitHub client-side, then POST /complete ──────\n const deviceCode = clientHints['device_code'] as string;\n const userCode = clientHints['user_code'] as string;\n const verificationUri = clientHints['verification_uri'] as string;\n const expiresIn = typeof clientHints['expires_in'] === 'number' ? clientHints['expires_in'] as number : 900;\n const interval = typeof clientHints['interval'] === 'number' ? clientHints['interval'] as number : 5;\n\n stdout(`Open the following URL in your browser and enter the code:\\n`);\n stdout(` URL: ${verificationUri}\\n`);\n stdout(` Code: ${userCode}\\n`);\n stdout(` (Code expires in ${Math.floor(expiresIn / 60)} minutes)\\n`);\n stdout('Waiting for authorization...\\n');\n\n let accessToken: string;\n try {\n const result = await startDeviceFlow({\n deviceCode,\n interval,\n expiresIn,\n fetchPoll: async (dc) => {\n const res = await fetchFn(GITHUB_DEVICE_FLOW_URL, {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: dc,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n });\n return {\n status: res.status,\n json: () => res.json() as Promise<unknown>,\n };\n },\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n });\n accessToken = result.accessToken;\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: access denied — you declined authorization in the browser.\\n');\n exit(5);\n return;\n case 'expired_token':\n stderr('cleargate: device code expired — please re-run `cleargate join <url>`.\\n');\n exit(5);\n return;\n case 'unreachable':\n stderr('cleargate: cannot reach GitHub. Check your connection and retry.\\n');\n exit(2);\n return;\n default:\n stderr(`cleargate: GitHub device flow error: ${err.code}\\n`);\n exit(6);\n return;\n }\n }\n stderr('cleargate: unexpected error during GitHub device flow\\n');\n exit(6);\n return;\n }\n\n // POST /complete with access_token proof (OD-2 fix: server member-mode accepts access_token)\n // FLASHCARD 2026-04-18 #cli #plaintext-redact: named field access only, never log accessToken\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { access_token: accessToken } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // ── Email magic-link: prompt for OTP with 3 retries (OD-3) ──────────────\n const sentTo = typeof clientHints['sent_to'] === 'string' ? clientHints['sent_to'] as string : '(unknown)';\n const maxRetries = 3;\n\n stdout(`We sent a 6-digit code to ${sentTo}.\\n`);\n\n // CI mode: use pre-seeded code directly\n if (opts.code !== undefined) {\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: opts.code } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (!completeRes.ok) {\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const { message, exitCode } = mapProviderError(\n completeRes.status,\n body.error ?? '',\n parseRetryAfter(completeRes),\n );\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n } else {\n // Interactive: prompt up to maxRetries times\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Queue-based line reading (pattern from identity-flow.ts)\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) return Promise.resolve(lineQueue.shift()!);\n if (rlClosed) return Promise.resolve('');\n return new Promise<string>((resolve) => { lineWaiters.push(resolve); });\n }\n\n let succeeded = false;\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n stdout('Enter code: ');\n const otpCode = (await readNextLine()).trim();\n\n let completeRes: Response;\n try {\n completeRes = await fetchFn(`${baseUrl}/join/${token}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ challenge_id: challengeId, proof: { code: otpCode } }),\n });\n } catch (err) {\n stderr(`cleargate: cannot reach ${baseUrl} (${err instanceof Error ? err.message : String(err)}).\\n`);\n exit(2);\n return;\n }\n\n if (completeRes.ok) {\n try {\n completeRawBody = await completeRes.json();\n } catch {\n stderr('cleargate: server returned non-JSON response.\\n');\n exit(7);\n return;\n }\n succeeded = true;\n break;\n }\n\n const body = (await completeRes.json().catch(() => ({}))) as { error?: string };\n const errorCode = body.error ?? '';\n\n // Terminal errors (don't retry)\n if (completeRes.status === 410 || errorCode === 'challenge_expired') {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n if (completeRes.status === 403 || (completeRes.status >= 400 && completeRes.status < 500 && errorCode !== 'provider_error')) {\n const { message, exitCode } = mapProviderError(completeRes.status, errorCode, parseRetryAfter(completeRes));\n stderr(`${message}\\n`);\n exit(exitCode);\n return;\n }\n\n // Retryable (502 provider_error = wrong code)\n if (attempt < maxRetries) {\n stderr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n if (!succeeded) {\n stderr(`cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.\\n`);\n exit(12);\n return;\n }\n }\n }\n\n // ── Seat the refresh token ─────────────────────────────────────────────────\n const b = completeRawBody as {\n refresh_token?: unknown;\n project_name?: unknown;\n member_role?: unknown;\n };\n\n if (typeof b.refresh_token !== 'string' || typeof b.project_name !== 'string') {\n stderr('cleargate: server returned unexpected response shape.\\n');\n exit(7);\n return;\n }\n\n // Named field access — b.refresh_token is a bare string, never logged\n const refreshToken: string = b.refresh_token;\n const projectName: string = b.project_name;\n\n try {\n const store = await (opts.createStore ?? createTokenStore)();\n await store.save(opts.profile, refreshToken);\n\n // ── Success output ─────────────────────────────────────────────────────\n stdout(`joined project '${projectName}' as '${hostname()}'\\n`);\n stdout(`refresh token saved to ${store.backend}.\\n`);\n } catch (err) {\n stderr(\n `cleargate: internal error: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n exit(99);\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const hdr = res.headers?.get?.('retry-after');\n if (!hdr) return undefined;\n const n = parseInt(hdr, 10);\n return isNaN(n) ? undefined : n;\n}\n","/**\n * Shared identity-flow helpers for `cleargate join` and `cleargate admin login`.\n *\n * Exposes:\n * - pickProvider — flag > required_provider pin > interactive picker > error\n * - promptPicker — numbered list via node:readline (no new dep)\n * - startDeviceFlow — GitHub device-flow poll loop (admin-login + member join)\n * - promptEmailOTP — 6-digit OTP prompt with up to 3 retries (OD-3)\n * - mapProviderError — HTTP-status + error-code → { message, exitCode, retryable }\n * - IdentityFlowError, DeviceFlowError typed error classes\n *\n * Test seams: functions that read from stdin or prompt accept\n * `{ stdin: NodeJS.ReadableStream, stdout: (s: string) => void }` as an\n * options object so tests can inject a pre-seeded Readable and capture output\n * without real TTY interaction.\n *\n * No new npm dependencies — uses only node:readline (built-in).\n *\n * CR-006 EPIC-019.\n */\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Error classes\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport class IdentityFlowError extends Error {\n constructor(\n public readonly code: string,\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'IdentityFlowError';\n }\n}\n\nexport class DeviceFlowError extends Error {\n constructor(\n public readonly code:\n | 'access_denied'\n | 'expired_token'\n | 'not_admin'\n | 'timeout'\n | 'unreachable'\n | 'server_error',\n message?: string,\n ) {\n super(message ?? code);\n this.name = 'DeviceFlowError';\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type Provider = 'github' | 'email';\n\nexport interface PickProviderOptions {\n /** Explicit --auth flag value (wins over everything). */\n flag?: string;\n /** Is the process running in a real TTY? */\n isTTY?: boolean;\n /** Available providers (default: ['github', 'email']). */\n available?: Provider[];\n /** Stdin stream for interactive picker (test seam). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function for interactive picker (test seam). */\n stdout?: (s: string) => void;\n}\n\nexport interface StartDeviceFlowOptions {\n /** The device_code from the challenge client_hints. */\n deviceCode: string;\n /** Poll interval in seconds (from client_hints). */\n interval: number;\n /** How long until the device code expires in seconds. */\n expiresIn: number;\n /**\n * Function to POST to the poll endpoint.\n * Returns a Response-like object with status + json().\n */\n fetchPoll: (deviceCode: string) => Promise<{ status: number; json: () => Promise<unknown> }>;\n /**\n * Sleep implementation — injected in tests to avoid real waits.\n * Defaults to real setTimeout.\n */\n sleepFn?: (ms: number) => Promise<void>;\n /**\n * Override the base interval in milliseconds (used in tests — set to 0 to skip waits).\n * When provided, overrides `interval` from the server. Bump logic still applies if sleepFn is also provided.\n */\n intervalOverrideMs?: number;\n /**\n * Grace period beyond expiresIn before declaring timeout.\n * Default: 10_000ms (10 seconds for round-trip latency).\n */\n deadlineGraceMs?: number;\n}\n\nexport interface StartDeviceFlowResult {\n /** GitHub access_token returned by device-flow polling. */\n accessToken: string;\n}\n\nexport interface PromptEmailOTPOptions {\n /** Masked email shown to user, e.g. \"a***@example.com\". */\n sentTo: string;\n /** Pre-seeded code (CI / --code flag). If provided, skips prompt entirely. */\n code?: string;\n /** Number of in-process retries. Default: 3. */\n maxRetries?: number;\n /**\n * Function that performs one POST /complete attempt with the given OTP code.\n * Returns an object indicating success or a known error code.\n */\n attemptComplete: (code: string) => Promise<{ success: boolean; errorCode?: string }>;\n /** Stdin stream (test seam — default: process.stdin). */\n stdin?: NodeJS.ReadableStream;\n /** Stdout function (test seam). */\n stdout?: (s: string) => void;\n /** Stderr function (test seam). */\n stderr?: (s: string) => void;\n}\n\nexport interface MapProviderErrorResult {\n message: string;\n exitCode: number;\n retryable: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// pickProvider\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolves which provider to use for a join flow.\n *\n * Priority: flag > interactive picker > error (non-TTY)\n * Throws IdentityFlowError('provider_required') when non-interactive and no flag.\n * Throws IdentityFlowError('provider_unknown') when flag names an unrecognised provider.\n */\nexport async function pickProvider(opts: PickProviderOptions): Promise<Provider> {\n const available: Provider[] = opts.available ?? ['github', 'email'];\n\n if (opts.flag !== undefined) {\n const flagLower = opts.flag.toLowerCase() as Provider;\n if (!available.includes(flagLower)) {\n throw new IdentityFlowError(\n 'provider_unknown',\n `cleargate: unknown provider '${opts.flag}'. Available: ${available.join(', ')}`,\n );\n }\n return flagLower;\n }\n\n // No flag — need TTY for interactive picker\n if (!opts.isTTY) {\n throw new IdentityFlowError(\n 'provider_required',\n 'cleargate: --auth required in non-interactive mode',\n );\n }\n\n // Auto-select if only one provider\n if (available.length === 1) {\n return available[0]!;\n }\n\n return promptPicker(available, opts);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptPicker\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PROVIDER_LABELS: Record<Provider, string> = {\n github: 'GitHub OAuth',\n email: 'Email magic-link',\n};\n\n/**\n * Interactive numbered-list picker using node:readline.\n * Renders \"How would you like to verify your email?\" + numbered list.\n */\nexport async function promptPicker(\n options: Provider[],\n { stdin, stdout }: { stdin?: NodeJS.ReadableStream; stdout?: (s: string) => void } = {},\n): Promise<Provider> {\n const write = stdout ?? ((s: string) => process.stdout.write(s));\n\n write('How would you like to verify your email?\\n');\n options.forEach((p, i) => {\n write(` ${i + 1}. ${PROVIDER_LABELS[p]}\\n`);\n });\n write(`Choice [1-${options.length}]: `);\n\n const inputStream = (stdin as Readable | undefined) ?? process.stdin;\n\n return new Promise<Provider>((resolve, reject) => {\n let settled = false;\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n rl.once('line', (line) => {\n settled = true;\n rl.close();\n const idx = parseInt(line.trim(), 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= options.length) {\n reject(\n new IdentityFlowError(\n 'invalid_choice',\n `cleargate: invalid choice '${line.trim()}'. Enter a number between 1 and ${options.length}.`,\n ),\n );\n return;\n }\n resolve(options[idx]!);\n });\n\n rl.once('error', (err) => {\n if (!settled) {\n settled = true;\n reject(err);\n }\n });\n rl.once('close', () => {\n // Only reject if we haven't already resolved/rejected from the line event\n if (!settled) {\n settled = true;\n reject(new IdentityFlowError('provider_required', 'cleargate: no provider selected'));\n }\n });\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// startDeviceFlow\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Polls fetchPoll until the device is authorized or an error terminal state is reached.\n *\n * Used for both admin-login (polls /admin-api/v1/auth/device/poll) and\n * member-join GitHub flow (polls https://github.com/login/oauth/access_token).\n *\n * Returns { accessToken } on success.\n * Throws DeviceFlowError on terminal failure.\n */\nexport async function startDeviceFlow(opts: StartDeviceFlowOptions): Promise<StartDeviceFlowResult> {\n const sleepFn = opts.sleepFn ?? defaultSleep;\n let currentIntervalMs =\n opts.intervalOverrideMs !== undefined\n ? opts.intervalOverrideMs\n : Math.max(opts.interval, 5) * 1000;\n\n const expiresAtMs = Date.now() + opts.expiresIn * 1000;\n const deadline = expiresAtMs + (opts.deadlineGraceMs ?? 10_000);\n\n while (Date.now() < deadline) {\n await sleepFn(currentIntervalMs);\n\n let pollRes: { status: number; json: () => Promise<unknown> };\n try {\n pollRes = await opts.fetchPoll(opts.deviceCode);\n } catch {\n throw new DeviceFlowError('unreachable');\n }\n\n if (pollRes.status === 403) {\n let body: Record<string, unknown> = {};\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n // ignore parse failure\n }\n if (body['error'] === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n // not_admin\n throw new DeviceFlowError('not_admin');\n }\n\n if (pollRes.status === 410) {\n throw new DeviceFlowError('expired_token');\n }\n\n if (!pollRes.status || pollRes.status < 200 || pollRes.status >= 300) {\n if (pollRes.status >= 500 || pollRes.status < 100) {\n throw new DeviceFlowError('server_error');\n }\n }\n\n let body: Record<string, unknown>;\n try {\n body = (await pollRes.json()) as Record<string, unknown>;\n } catch {\n throw new DeviceFlowError('server_error');\n }\n\n // Handle GitHub's device-flow pending responses\n const errorField = body['error'];\n if (typeof errorField === 'string') {\n if (errorField === 'authorization_pending') {\n // keep polling\n continue;\n }\n if (errorField === 'slow_down') {\n // bump interval if retry_after is larger\n const retryAfter = body['interval'];\n if (typeof retryAfter === 'number') {\n const bumped = retryAfter * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n } else {\n // default slow_down bump: +5s\n currentIntervalMs += 5_000;\n }\n continue;\n }\n if (errorField === 'access_denied') {\n throw new DeviceFlowError('access_denied');\n }\n if (errorField === 'expired_token') {\n throw new DeviceFlowError('expired_token');\n }\n // unknown error — treat as server error\n throw new DeviceFlowError('server_error');\n }\n\n // Handle MCP server's pending response shape { pending: true, retry_after? }\n if (body['pending'] === true) {\n const shouldApplyBump =\n opts.sleepFn !== undefined || opts.intervalOverrideMs === undefined;\n if (shouldApplyBump && typeof body['retry_after'] === 'number') {\n const bumped = (body['retry_after'] as number) * 1000;\n if (bumped > currentIntervalMs) {\n currentIntervalMs = bumped;\n }\n }\n continue;\n }\n\n // Success — extract access_token (GitHub device-flow: access_token field)\n if (typeof body['access_token'] === 'string') {\n return { accessToken: body['access_token'] as string };\n }\n\n // MCP server success shape (admin_token for admin-login)\n if (typeof body['admin_token'] === 'string') {\n return { accessToken: body['admin_token'] as string };\n }\n\n throw new DeviceFlowError('server_error');\n }\n\n throw new DeviceFlowError('timeout');\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// promptEmailOTP\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Prompts user for a 6-digit OTP code and attempts /complete up to maxRetries (default: 3) times.\n *\n * OD-3 resolution: 3 in-process retries for interactive UX; same challenge_id is reused each time.\n * Server MagicLinkProvider treats each /complete as independent — 502 on first attempt\n * does NOT burn the challenge (verified mcp/src/routes/join.ts:244-255).\n *\n * If `code` is provided (CI / --code flag), it is used directly without any prompt or retry.\n *\n * Returns the final code string on success.\n * Throws IdentityFlowError('otp_max_retries') after maxRetries failures.\n * Throws IdentityFlowError('otp_expired') on 410 challenge_expired.\n */\nexport async function promptEmailOTP(opts: PromptEmailOTPOptions): Promise<string> {\n const write = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const writeErr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const maxRetries = opts.maxRetries ?? 3;\n\n write(`We sent a 6-digit code to ${opts.sentTo}.\\n`);\n\n // CI mode: use provided code directly (no prompt, no retry)\n if (opts.code !== undefined) {\n const result = await opts.attemptComplete(opts.code);\n if (result.success) {\n return opts.code;\n }\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n throw new IdentityFlowError(\n 'otp_failed',\n `cleargate: code didn't match. Re-run \\`cleargate join <url>\\` to try again`,\n );\n }\n\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n\n // Create a single readline interface that persists across all retry attempts.\n // Each readLineFromRl() call reads exactly one line from the interface.\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n // Collect queued lines from the rl interface\n const lineQueue: string[] = [];\n const lineWaiters: Array<(line: string) => void> = [];\n let rlClosed = false;\n\n rl.on('line', (line) => {\n const waiter = lineWaiters.shift();\n if (waiter) {\n waiter(line);\n } else {\n lineQueue.push(line);\n }\n });\n\n rl.once('close', () => {\n rlClosed = true;\n // Drain any pending waiters with empty string (stream ended)\n for (const waiter of lineWaiters.splice(0)) {\n waiter('');\n }\n });\n\n function readNextLine(): Promise<string> {\n if (lineQueue.length > 0) {\n return Promise.resolve(lineQueue.shift()!);\n }\n if (rlClosed) {\n return Promise.resolve('');\n }\n return new Promise<string>((resolve) => {\n lineWaiters.push(resolve);\n });\n }\n\n try {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n write('Enter code: ');\n const code = (await readNextLine()).trim();\n\n const result = await opts.attemptComplete(code);\n\n if (result.success) {\n return code;\n }\n\n if (result.errorCode === 'challenge_expired') {\n throw new IdentityFlowError(\n 'otp_expired',\n `cleargate: code expired. Re-run \\`cleargate join <url>\\` to start over`,\n );\n }\n\n if (attempt < maxRetries) {\n writeErr(`cleargate: code didn't match. ${maxRetries - attempt} attempt${maxRetries - attempt === 1 ? '' : 's'} remaining.\\n`);\n }\n }\n } finally {\n rl.close();\n }\n\n throw new IdentityFlowError(\n 'otp_max_retries',\n `cleargate: code didn't match after ${maxRetries} tries. Run \\`cleargate join <url>\\` again to get a new code.`,\n );\n}\n\n// readLine is no longer used (replaced by shared rl in promptEmailOTP)\n// Kept as a dead-code stub for any future one-shot callers.\n\n// ─────────────────────────────────────────────────────────────────────────────\n// mapProviderError\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Maps server HTTP status + error code pairs into user-readable messages and exit codes.\n *\n * Error table per M3 §2:\n * 400 invalid_request → exit 7\n * 400 provider_not_allowed → exit 9\n * 400 provider_unknown → exit 9\n * 400 identity_proof_required → exit 11 (stale CLI upgrade hint)\n * 403 email_mismatch → exit 10\n * 404 not_found → exit 4\n * 410 invite_expired → exit 3\n * 410 invite_already_consumed → exit 3\n * 410 challenge_expired → exit 3\n * 429 → exit 8\n * 502 provider_error → exit 12\n * >=500 (other) → exit 6\n */\nexport function mapProviderError(\n httpStatus: number,\n errorCode: string,\n retryAfterSeconds?: number,\n): MapProviderErrorResult {\n if (httpStatus === 400) {\n switch (errorCode) {\n case 'provider_not_allowed':\n return {\n message:\n 'cleargate: this invite requires a different provider — re-run with `--auth <pinned>`',\n exitCode: 9,\n retryable: true,\n };\n case 'provider_unknown':\n return {\n message:\n 'cleargate: server does not have that provider registered — contact the project admin',\n exitCode: 9,\n retryable: false,\n };\n case 'identity_proof_required':\n return {\n message: 'cleargate: this CLI is out of date — please upgrade and retry (`npm i -g cleargate@latest`)',\n exitCode: 11,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invalid request to server (please file a bug)',\n exitCode: 7,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 403 && errorCode === 'email_mismatch') {\n return {\n message:\n 'cleargate: verified email does not match the invitee — ask your admin to re-issue the invite',\n exitCode: 10,\n retryable: false,\n };\n }\n\n if (httpStatus === 404) {\n return {\n message: 'cleargate: invite not found',\n exitCode: 4,\n retryable: false,\n };\n }\n\n if (httpStatus === 410) {\n switch (errorCode) {\n case 'invite_expired':\n return {\n message: 'cleargate: invite expired. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'invite_already_consumed':\n return {\n message: 'cleargate: invite already consumed. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n case 'challenge_expired':\n return {\n message: 'cleargate: code expired. Re-run `cleargate join <url>` to start over',\n exitCode: 3,\n retryable: false,\n };\n default:\n return {\n message: 'cleargate: invite no longer valid. Request a new invite',\n exitCode: 3,\n retryable: false,\n };\n }\n }\n\n if (httpStatus === 429) {\n const retryHint = retryAfterSeconds !== undefined ? `${retryAfterSeconds}` : '900';\n return {\n message: `cleargate: too many requests. Retry after ${retryHint}s`,\n exitCode: 8,\n retryable: true,\n };\n }\n\n if (httpStatus === 502 && errorCode === 'provider_error') {\n return {\n message: \"cleargate: code didn't match. Try again, or restart with `cleargate join <url>`\",\n exitCode: 12,\n retryable: true,\n };\n }\n\n if (httpStatus >= 500) {\n return {\n message: `cleargate: server error ${httpStatus}`,\n exitCode: 6,\n retryable: false,\n };\n }\n\n return {\n message: `cleargate: unexpected error ${httpStatus} ${errorCode}`,\n exitCode: 7,\n retryable: false,\n };\n}\n","/**\n * stamp.ts — `cleargate stamp <file>` command handler\n *\n * Wraps M1 helpers: getCodebaseVersion + stampFrontmatter.\n * Do NOT hand-roll YAML or shell git — use the M1 helpers.\n *\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #commander #optional-key: conditionally assign `now`/`version` opts.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { getCodebaseVersion, type CodebaseVersion } from '../lib/codebase-version.js';\nimport { stampFrontmatter, type StampOptions } from '../lib/stamp-frontmatter.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\n\nexport interface StampCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: inject a fixed CodebaseVersion instead of calling getCodebaseVersion. */\n getVersion?: () => CodebaseVersion;\n}\n\n/**\n * Build a unified-diff-style preview of the frontmatter changes.\n * Prints context lines (unchanged) with a leading space,\n * removed lines with `-`, added lines with `+`.\n */\nfunction buildDiffPreview(\n filePath: string,\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const lines: string[] = [`--- ${filePath}`, `+++ ${filePath} (after stamp)`];\n\n // Use insertion order of keys — include both before and after\n const seenKeys = new Set<string>();\n for (const key of [...Object.keys(before), ...Object.keys(after)]) {\n if (seenKeys.has(key)) continue;\n seenKeys.add(key);\n const bVal = before[key];\n const aVal = after[key];\n if (bVal === aVal) {\n lines.push(` ${key}: ${String(bVal)}`);\n } else {\n if (key in before) {\n lines.push(`-${key}: ${String(bVal)}`);\n }\n if (key in after) {\n lines.push(`+${key}: ${String(aVal)}`);\n }\n }\n }\n return lines.join('\\n');\n}\n\nexport async function stampHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Verify file exists before calling helpers\n if (!fs.existsSync(absPath)) {\n process.stderr.write(`[cleargate stamp] error: file not found: ${absPath}\\n`);\n return exitFn(1);\n }\n\n const version = cli?.getVersion ? cli.getVersion() : getCodebaseVersion({ cwd });\n\n if (opts.dryRun) {\n // For dry-run: operate on a temp file copy so the real file is never written.\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-stamp-dry-'));\n try {\n const tmpFile = path.join(tmpDir, path.basename(absPath));\n fs.copyFileSync(absPath, tmpFile);\n\n // Read current frontmatter for the diff's before-side\n let before: Record<string, unknown> = {};\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n if (raw.trimStart().startsWith('---')) {\n ({ fm: before } = parseFrontmatter(raw));\n }\n } catch {\n // before stays empty if parse fails\n }\n\n // Build stamp options — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(tmpFile, stampOpts);\n\n const diff = buildDiffPreview(file, before, result.frontmatterAfter);\n stdoutFn(diff);\n if (result.reason === 'noop-archive' || result.reason === 'noop-unchanged') {\n stdoutFn(`[dry-run] no changes (${result.reason})`);\n }\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n return;\n }\n\n // Real stamp — conditionally assign now per FLASHCARD #cli #commander #optional-key\n const stampOpts: StampOptions = { version };\n if (cli?.now) {\n stampOpts.now = cli.now;\n }\n\n const result = await stampFrontmatter(absPath, stampOpts);\n stdoutFn(`[stamped] ${file} (${result.reason})`);\n}\n","import { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nexport interface CodebaseVersion {\n sha: string | null;\n dirty: boolean;\n tag: string | null;\n package_version: string | null;\n version_string: string; // e.g. \"a3f2e91\", \"a3f2e91-dirty\", \"1.4.2\", \"unknown\"\n}\n\nexport type ExecFn = (cmd: string, args: string[]) => { stdout: string; code: number };\n\nexport interface CodebaseVersionOpts {\n cwd?: string;\n exec?: ExecFn;\n}\n\nfunction makeDefaultExec(cwd: string): ExecFn {\n return (cmd: string, args: string[]): { stdout: string; code: number } => {\n try {\n const stdout = execSync([cmd, ...args].join(' '), {\n cwd,\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return { stdout: stdout.trim(), code: 0 };\n } catch (err: unknown) {\n const e = err as { stdout?: string | Buffer; status?: number };\n return {\n stdout: typeof e.stdout === 'string' ? e.stdout.trim() : '',\n code: typeof e.status === 'number' ? e.status : 1,\n };\n }\n };\n}\n\nfunction findPackageJson(startDir: string): string | null {\n let current = startDir;\n while (true) {\n const candidate = path.join(current, 'package.json');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n return null;\n }\n current = parent;\n }\n}\n\nexport function getCodebaseVersion(opts?: CodebaseVersionOpts): CodebaseVersion {\n const cwd = opts?.cwd ?? process.cwd();\n const execFn = opts?.exec ?? makeDefaultExec(cwd);\n\n // Try git SHA\n const shaResult = execFn('git', ['rev-parse', '--short', 'HEAD']);\n if (shaResult.code === 0 && shaResult.stdout.length > 0) {\n const sha = shaResult.stdout.trim();\n\n // Check dirty\n const statusResult = execFn('git', ['status', '--porcelain']);\n const dirty = statusResult.code === 0 && statusResult.stdout.trim().length > 0;\n\n const version_string = dirty ? `${sha}-dirty` : sha;\n\n return {\n sha,\n dirty,\n tag: null,\n package_version: null,\n version_string,\n };\n }\n\n // Fallback: find nearest package.json\n const pkgPath = findPackageJson(cwd);\n if (pkgPath !== null) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: string };\n const package_version = typeof pkg.version === 'string' ? pkg.version : null;\n if (package_version !== null) {\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version,\n version_string: package_version,\n };\n }\n } catch {\n // fall through to unknown\n }\n }\n\n // Unknown\n console.warn('[cleargate] codebase-version: could not determine version (no git, no package.json)');\n return {\n sha: null,\n dirty: false,\n tag: null,\n package_version: null,\n version_string: 'unknown',\n };\n}\n","import * as fs from 'fs/promises';\nimport type { CodebaseVersion } from './codebase-version.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface StampOptions {\n now?: () => Date;\n version?: CodebaseVersion;\n /** Default: /\\/\\.cleargate\\/delivery\\/archive\\// */\n archivePathMatcher?: (absPath: string) => boolean;\n}\n\nexport interface StampResult {\n changed: boolean;\n frontmatterBefore: Record<string, unknown>;\n frontmatterAfter: Record<string, unknown>;\n reason: 'created' | 'updated' | 'noop-archive' | 'noop-unchanged';\n}\n\n/** Write-template marker keys (epic/story/bug/CR/proposal). */\nconst WRITE_TEMPLATE_KEYS = new Set([\n 'story_id',\n 'epic_id',\n 'proposal_id',\n 'cr_id',\n 'bug_id',\n]);\n\nconst DEFAULT_ARCHIVE_MATCHER = (absPath: string): boolean =>\n /\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath);\n\nexport async function stampFrontmatter(absPath: string, opts?: StampOptions): Promise<StampResult> {\n const isArchive = (opts?.archivePathMatcher ?? DEFAULT_ARCHIVE_MATCHER)(absPath);\n if (isArchive) {\n // Read to get frontmatter for result shape, but do not write\n const raw = await fs.readFile(absPath, 'utf8');\n let fm: Record<string, unknown> = {};\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n // If parse fails, return empty frontmatter snapshot\n }\n return {\n changed: false,\n frontmatterBefore: fm,\n frontmatterAfter: fm,\n reason: 'noop-archive',\n };\n }\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n // Determine if the file has frontmatter at all\n const hasFrontmatter = raw.trimStart().startsWith('---');\n\n let fm: Record<string, unknown> = {};\n let body = raw;\n\n if (hasFrontmatter) {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n }\n\n const frontmatterBefore = { ...fm };\n\n const nowFn = opts?.now ?? (() => new Date());\n const now = nowFn();\n const nowIso = toIsoSecond(now);\n\n const version = opts?.version ?? { sha: null, dirty: false, tag: null, package_version: null, version_string: 'unknown' };\n const versionString = version.version_string;\n\n // Determine if this is a first stamp or re-stamp\n const hasCreatedAt = 'created_at' in fm && fm['created_at'] !== undefined && fm['created_at'] !== '' && fm['created_at'] !== null;\n\n // Determine if it's a write-template (needs server_pushed_at_version)\n const isWriteTemplate = WRITE_TEMPLATE_KEYS.has(Object.keys(fm).find((k) => WRITE_TEMPLATE_KEYS.has(k)) ?? '');\n\n // Build the new frontmatter:\n // 1. Preserve existing key order\n // 2. Append new keys in canonical order: created_at, updated_at, created_at_version, updated_at_version, server_pushed_at_version\n const newFm: Record<string, unknown> = {};\n\n // Copy all existing keys first (preserves order)\n for (const [k, v] of Object.entries(fm)) {\n newFm[k] = v;\n }\n\n if (!hasCreatedAt) {\n // First stamp: set all 4 fields\n // If the keys exist already (placeholder values), update them in-place order\n // If not, append at the end in canonical order\n newFm['created_at'] = nowIso;\n newFm['updated_at'] = nowIso;\n newFm['created_at_version'] = versionString;\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n } else {\n // Re-stamp: preserve created_at + created_at_version, advance updated_at + updated_at_version\n // created_at stays\n newFm['updated_at'] = nowIso;\n // created_at_version stays\n newFm['updated_at_version'] = versionString;\n\n if (isWriteTemplate && !('server_pushed_at_version' in newFm)) {\n newFm['server_pushed_at_version'] = null;\n }\n }\n\n // Check noop-unchanged: if nothing changed, return early\n const unchanged =\n newFm['updated_at'] === fm['updated_at'] &&\n newFm['updated_at_version'] === fm['updated_at_version'] &&\n newFm['created_at'] === fm['created_at'] &&\n newFm['created_at_version'] === fm['created_at_version'];\n\n if (unchanged && hasCreatedAt) {\n return {\n changed: false,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: 'noop-unchanged',\n };\n }\n\n // Serialize and write\n const fmBlock = serializeFrontmatter(newFm);\n // Reconstruct: frontmatter block + newline + body\n // body from parseFrontmatter does NOT have a leading blank line (it strips one)\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n\n return {\n changed: true,\n frontmatterBefore,\n frontmatterAfter: newFm,\n reason: hasCreatedAt ? 'updated' : 'created',\n };\n}\n","/**\n * YAML frontmatter parser backed by js-yaml with CORE_SCHEMA (YAML 1.2 core).\n *\n * Parses `---\\n<yaml>\\n---\\n<body>` into a typed frontmatter map + body string.\n * Preserves native types (null, boolean, number, string), nested maps, and\n * arrays. Uses CORE_SCHEMA so ISO-8601 timestamp strings are NOT coerced to\n * Date objects (YAML 1.1's quirk).\n *\n * Historical note: an earlier hand-rolled parser flattened indented nested\n * maps into top-level keys and stringified null/boolean scalars. See\n * BUG-001 and FLASHCARD entry `#yaml #frontmatter`.\n */\n\nimport yaml from 'js-yaml';\n\nexport function parseFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') {\n throw new Error('parseFrontmatter: input does not start with ---');\n }\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) {\n throw new Error('parseFrontmatter: missing closing ---');\n }\n\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n // strip one leading blank line if present\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n\n if (yamlText.trim() === '') {\n return { fm: {}, body };\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`parseFrontmatter: invalid YAML: ${(err as Error).message}`);\n }\n\n if (parsed === null || parsed === undefined) {\n return { fm: {}, body };\n }\n if (typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('parseFrontmatter: frontmatter is not a YAML mapping');\n }\n\n return { fm: parsed as Record<string, unknown>, body };\n}\n","/**\n * Frontmatter YAML serializer backed by js-yaml with CORE_SCHEMA.\n *\n * Emits a `---\\n<yaml>\\n---` block. Preserves key insertion order (JS\n * objects iterate non-numeric keys in insertion order by spec, and js-yaml\n * follows Object.keys), nested maps, arrays, and native scalar types.\n *\n * Partner of parse-frontmatter.ts — the two round-trip losslessly.\n */\n\nimport yaml from 'js-yaml';\n\n/**\n * Serialize a frontmatter record to a YAML block (including the --- delimiters).\n * Key order is preserved as supplied.\n */\nexport function serializeFrontmatter(fm: Record<string, unknown>): string {\n // Empty object → emit a two-delimiter block with no body\n if (Object.keys(fm).length === 0) {\n return '---\\n---';\n }\n\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n\n // js-yaml always ends with \\n; trim so we control the framing\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---`;\n}\n\n/**\n * Format a Date as ISO 8601 UTC with second precision: \"YYYY-MM-DDTHH:MM:SSZ\"\n */\nexport function toIsoSecond(d: Date): string {\n return d.toISOString().replace(/\\.\\d{3}Z$/, 'Z');\n}\n","/**\n * init.ts — `cleargate init` command handler\n *\n * Steps:\n * 1. Validate cwd exists and is writable\n * 2. Resolve payloadDir (bundled cleargate-planning/ templates)\n * 3. copyPayload: copy scaffold files to target cwd\n * 4. mergeSettings: merge PostToolUse hook into .claude/settings.json\n * 5. injectClaudeMd: bounded-block inject into CLAUDE.md\n * 6. Bootstrap pass: if delivery/ has items, run wiki build\n * 7. Print Done\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawnSync } from 'node:child_process';\nimport { copyPayload } from '../init/copy-payload.js';\nimport { mergeSettings, type SettingsJson } from '../init/merge-settings.js';\nimport { injectClaudeMd, extractBlock } from '../init/inject-claude-md.js';\nimport { injectMcpJson } from '../init/inject-mcp-json.js';\nimport { wikiBuildHandler, type WikiBuildOptions } from './wiki-build.js';\nimport { loadPackageManifest, type ManifestFile } from '../lib/manifest.js';\nimport { promptYesNo as defaultPromptYesNo, promptEmail as defaultPromptEmail } from '../lib/prompts.js';\nimport { resolveIdentity, readParticipant, writeParticipant, type ResolveIdentityOpts } from '../lib/identity.js';\nimport { resolveScaffoldRoot, ScaffoldSourceError } from '../lib/scaffold-source.js';\n\n/**\n * The PostToolUse hook config to merge — updated in STORY-008-06 to use\n * stamp-and-gate.sh (replaces legacy SPRINT-04 inline wiki ingest command).\n * Uses ${CLAUDE_PROJECT_DIR} so the path is project-relative at runtime.\n * mergeSettings deduplicates by exact command string — safe to re-run.\n */\nconst HOOK_ADDITION: SettingsJson = {\n hooks: {\n PreToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-edit-gate.sh',\n },\n ],\n },\n ],\n PostToolUse: [\n {\n matcher: 'Edit|Write',\n hooks: [\n {\n type: 'command',\n command: '${CLAUDE_PROJECT_DIR}/.claude/hooks/stamp-and-gate.sh',\n },\n ],\n },\n ],\n },\n};\n\nexport interface InitOptions {\n /** Target working directory (the repo being initialised). Default: process.cwd() */\n cwd?: string;\n /** Overwrite files that differ from payload. Default: false */\n force?: boolean;\n /** Accept all defaults non-interactively (same as stdin not a TTY). */\n yes?: boolean;\n /** Test seam: path to bundled cleargate-planning/ payload directory */\n payloadDir?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: runs wiki build (default: wikiBuildHandler) */\n runWikiBuild?: (opts: WikiBuildOptions) => Promise<void>;\n /**\n * Test seam: replaces the real Y/n prompt for restore flow.\n * STORY-009-03: injectable so integration tests don't block on stdin.\n */\n promptYesNo?: (question: string, defaultYes: boolean) => Promise<boolean>;\n /**\n * Test seam: replaces loadPackageManifest() call for snapshot step.\n * STORY-009-03: allows tests to supply a known ManifestFile without a real MANIFEST.json.\n */\n readInstallManifest?: () => ManifestFile;\n /**\n * Test seam: replaces the email prompt for participant identity flow.\n * STORY-010-01: injectable so tests don't block on stdin.\n */\n promptEmail?: (question: string, defaultValue: string) => Promise<string>;\n /**\n * Test seam: identity resolver options (gitEmail, hostname, username, env overrides).\n * STORY-010-01: used to inject a deterministic git email in tests.\n */\n identityOpts?: ResolveIdentityOpts;\n /**\n * Test seam: override process.stdin.isTTY for participant prompt decision.\n * STORY-010-01: in test environment stdin is not a TTY; inject true to force interactive path.\n */\n stdinIsTTY?: boolean;\n /**\n * CR-009: pin version to stamp into hook scripts. Overrides the default of\n * reading the version from `cleargate-cli/package.json`. Supports\n * `cleargate init --pin 0.6.0-beta` and test seam injection.\n */\n pin?: string;\n /**\n * Test seam: replaces child_process.spawnSync for the resolver probe (Step 7.6).\n * Injected in tests to avoid real npx invocations.\n */\n spawnSyncFn?: typeof spawnSync;\n /**\n * STORY-016-05: install scaffold from a local directory instead of the published npm package.\n * Resolved relative to `cwd` (or process.cwd()). Requires `.claude/`, `.cleargate/`, and\n * `CLAUDE.md` at the path root. Enables meta-repo dogfood: `cleargate init --from-source ./cleargate-planning`.\n */\n fromSource?: string;\n}\n\n/** Shape of the .cleargate/.uninstalled marker written by STORY-009-07 `uninstall`. */\ninterface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n}\n\n/** Resolve default payloadDir from the installed package structure.\n *\n * tsup bundles all modules into dist/cli.js (single entry point).\n * As a result, import.meta.url inside ANY module resolves to dist/cli.js.\n * So dirname(import.meta.url) = dist/. One level up = package root.\n * See flashcard: #tsup #bundle #import-meta.\n */\nexport function resolveDefaultPayloadDir(): string {\n const thisFile = fileURLToPath(import.meta.url);\n // dist/cli.js → dirname = dist/ → one level up = package root\n const pkgRoot = path.resolve(path.dirname(thisFile), '..');\n return path.join(pkgRoot, 'templates', 'cleargate-planning');\n}\n\n/** Glob delivery dir for .md files, excluding .gitkeep */\nfunction countDeliveryItems(cwd: string): number {\n const pendingSync = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(cwd, '.cleargate', 'delivery', 'archive');\n let count = 0;\n for (const dir of [pendingSync, archive]) {\n if (!fs.existsSync(dir)) continue;\n const entries = fs.readdirSync(dir);\n for (const f of entries) {\n if (f.endsWith('.md') && f !== '.gitkeep') count++;\n }\n }\n return count;\n}\n\n/** Write file atomically: write to tmp, then rename. */\nfunction writeAtomic(filePath: string, content: string): void {\n const tmpPath = filePath + '.tmp.' + Date.now();\n fs.writeFileSync(tmpPath, content, 'utf8');\n fs.renameSync(tmpPath, filePath);\n}\n\n/**\n * CR-009: Read the version from a package.json file at `packageJsonPath`.\n * Returns null when the file is absent or malformed.\n */\nfunction readPackageVersion(packageJsonPath: string): string | null {\n try {\n const raw = fs.readFileSync(packageJsonPath, 'utf8');\n const pkg = JSON.parse(raw) as { version?: unknown };\n if (typeof pkg.version === 'string' && pkg.version.length > 0) {\n return pkg.version;\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nexport async function initHandler(opts: InitOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const force = opts.force ?? false;\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const runWikiBuild = opts.runWikiBuild ?? wikiBuildHandler;\n const promptYesNoFn = opts.promptYesNo ?? defaultPromptYesNo;\n const promptEmailFn = opts.promptEmail ?? defaultPromptEmail;\n const spawnSyncFn = opts.spawnSyncFn ?? spawnSync;\n\n // Step 1: Validate cwd\n if (!fs.existsSync(cwd)) {\n stderr(`[cleargate init] ERROR: target directory does not exist: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n // Check writable by attempting to create a tmp file\n const testWritePath = path.join(cwd, `.cleargate-init-write-test-${Date.now()}`);\n try {\n fs.writeFileSync(testWritePath, '');\n fs.unlinkSync(testWritePath);\n } catch {\n stderr(`[cleargate init] ERROR: target directory is not writable: ${cwd}\\n`);\n exit(1);\n return;\n }\n\n stdout(`[cleargate init] Target: ${cwd}\\n`);\n\n // Step 2: Resolve payloadDir\n // STORY-016-05: when --from-source is provided, use the local path instead of the npm package.\n let payloadDir: string;\n if (opts.payloadDir) {\n // Explicit test-seam override — highest priority, used by existing tests.\n payloadDir = opts.payloadDir;\n } else if (opts.fromSource) {\n // --from-source flag: validate and use the local path.\n try {\n const resolved = resolveScaffoldRoot({ fromSource: opts.fromSource, cwd });\n payloadDir = resolved.payloadDir;\n } catch (e) {\n if (e instanceof ScaffoldSourceError) {\n stderr(`${e.message}\\n`);\n exit(2);\n return;\n }\n throw e;\n }\n } else {\n payloadDir = resolveDefaultPayloadDir();\n }\n\n if (!fs.existsSync(payloadDir)) {\n stderr(`[cleargate init] ERROR: payload directory not found: ${payloadDir}\\n`);\n stderr(`[cleargate init] Run \\`npm run prebuild\\` to copy the payload first.\\n`);\n exit(1);\n return;\n }\n\n // Step 3: Copy scaffold payload\n // Step 3.5 (pre-copy): Detect .uninstalled marker and prompt restore.\n // Must run before any writes so the user sees the restore prompt first.\n const uninstalledMarkerPath = path.join(cwd, '.cleargate', '.uninstalled');\n let uninstalledMarker: UninstalledMarker | null = null;\n let userChoseRestore = false;\n\n if (fs.existsSync(uninstalledMarkerPath)) {\n try {\n const raw = fs.readFileSync(uninstalledMarkerPath, 'utf8');\n uninstalledMarker = JSON.parse(raw) as UninstalledMarker;\n } catch {\n stderr(`[cleargate init] WARNING: .uninstalled marker is malformed; ignoring it.\\n`);\n }\n\n if (uninstalledMarker !== null) {\n const { uninstalled_at, prior_version, preserved } = uninstalledMarker;\n const question =\n `[cleargate init] Detected previous ClearGate install` +\n ` (uninstalled ${uninstalled_at}, prior version ${prior_version}).` +\n ` Restore preserved items? [Y/n]`;\n userChoseRestore = await promptYesNoFn(question, true);\n\n if (userChoseRestore) {\n // Blind copy: just verify each preserved file still exists on disk\n // (it was preserved as intended). Log status; never touch content.\n for (const preservedPath of preserved) {\n const absPreserved = path.isAbsolute(preservedPath)\n ? preservedPath\n : path.join(cwd, preservedPath);\n if (fs.existsSync(absPreserved)) {\n stdout(`[cleargate init] [preserved] ${preservedPath}\\n`);\n } else {\n stdout(`[cleargate init] [warn] preserved path missing on disk: ${preservedPath}\\n`);\n }\n }\n } else {\n stdout(\n `[cleargate init] discarding preservation; preserved files untouched on disk\\n`,\n );\n }\n // Marker removal happens AFTER bootstrap (Step 6) completes — tracked below.\n }\n }\n\n // CR-009: Resolve pin version for hook script substitution.\n // Priority: explicit --pin flag → package.json next to payloadDir → package.json next to dist → fallback 'latest'\n let pinVersion: string | undefined;\n if (opts.pin) {\n pinVersion = opts.pin;\n } else {\n // payloadDir is `.../templates/cleargate-planning`; package.json is at `.../package.json`\n const payloadParent = path.resolve(payloadDir, '..', '..');\n pinVersion =\n readPackageVersion(path.join(payloadParent, 'package.json')) ??\n readPackageVersion(path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json')) ??\n 'latest';\n }\n\n const copyReport = copyPayload(payloadDir, cwd, { force, pinVersion });\n for (const action of copyReport.actions) {\n const verb =\n action.action === 'created'\n ? 'Created'\n : action.action === 'overwritten'\n ? 'Overwritten'\n : 'Skipped (exists)';\n stdout(`[cleargate init] ${verb} ${action.relPath}\\n`);\n }\n\n // Step 4: Merge PostToolUse hook into .claude/settings.json\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n let existingSettings: SettingsJson | null = null;\n if (fs.existsSync(settingsPath)) {\n try {\n existingSettings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')) as SettingsJson;\n } catch {\n stderr(`[cleargate init] WARNING: could not parse ${settingsPath}; treating as empty.\\n`);\n }\n }\n\n const mergedSettings = mergeSettings(existingSettings, HOOK_ADDITION);\n fs.mkdirSync(path.dirname(settingsPath), { recursive: true });\n writeAtomic(settingsPath, JSON.stringify(mergedSettings, null, 2) + '\\n');\n stdout(`[cleargate init] Updated .claude/settings.json: merged PostToolUse hook\\n`);\n\n // Step 5: Inject bounded block into CLAUDE.md\n const claudeMdPath = path.join(cwd, 'CLAUDE.md');\n const claudeMdSrcPath = path.join(payloadDir, 'CLAUDE.md');\n\n let claudeMdBlock: string;\n try {\n const claudeMdSrc = fs.readFileSync(claudeMdSrcPath, 'utf8');\n claudeMdBlock = extractBlock(claudeMdSrc);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not read CLAUDE.md block from payload: ${String(e)}\\n`);\n claudeMdBlock = '<!-- CLEARGATE:START -->\\n<!-- CLEARGATE:END -->';\n }\n\n const existingClaudeMd = fs.existsSync(claudeMdPath)\n ? fs.readFileSync(claudeMdPath, 'utf8')\n : null;\n\n const newClaudeMd = injectClaudeMd(existingClaudeMd, claudeMdBlock);\n writeAtomic(claudeMdPath, newClaudeMd);\n\n if (existingClaudeMd === null) {\n stdout(`[cleargate init] Created CLAUDE.md (with bounded block)\\n`);\n } else if (existingClaudeMd !== newClaudeMd) {\n stdout(`[cleargate init] Updated CLAUDE.md (bounded block injected/replaced)\\n`);\n } else {\n stdout(`[cleargate init] CLAUDE.md unchanged (block already up to date)\\n`);\n }\n\n // Step 5b (BUG-017 + BUG-019 + post-0.8.0): register cleargate in `.mcp.json`\n // as a stdio MCP server. Use `npx -y cleargate@<pin> mcp serve` so users\n // without a global install (i.e. anyone who ran `npx cleargate init`) still\n // get a working spawn. Pin to the cleargate version that wrote the entry,\n // matching the CR-009 hook resolver pattern.\n try {\n const action = injectMcpJson(cwd, pinVersion ?? 'latest');\n if (action === 'created') {\n stdout(\n `[cleargate init] Created .mcp.json (cleargate MCP server registered) — restart Claude Code to load it.\\n`,\n );\n } else if (action === 'updated') {\n stdout(\n `[cleargate init] Updated .mcp.json (cleargate MCP server entry merged) — restart Claude Code to pick up changes.\\n`,\n );\n } else {\n stdout(`[cleargate init] .mcp.json unchanged (cleargate entry already present)\\n`);\n }\n } catch (e) {\n stderr(`[cleargate init] WARNING: ${String(e instanceof Error ? e.message : e)}\\n`);\n }\n\n // Step 6: Bootstrap pass\n const itemCount = countDeliveryItems(cwd);\n if (itemCount > 0) {\n stdout(`[cleargate init] Bootstrap: running wiki build (${itemCount} items found)...\\n`);\n await runWikiBuild({ cwd, now });\n stdout(`[cleargate init] Bootstrap: ran wiki build (${itemCount} items ingested)\\n`);\n } else {\n stdout(`[cleargate init] Bootstrap: no items to ingest, skipping build\\n`);\n }\n\n // Step 7: Write install snapshot atomically to .cleargate/.install-manifest.json.\n // Must be the FINAL step after all scaffold files are written (blueprint §1.2).\n const cleargateDir = path.join(cwd, '.cleargate');\n fs.mkdirSync(cleargateDir, { recursive: true });\n\n const snapshotPath = path.join(cleargateDir, '.install-manifest.json');\n try {\n const readManifest = opts.readInstallManifest ?? (() => loadPackageManifest({ packageRoot: payloadDir }));\n const pkgManifest = readManifest();\n const snapshot: ManifestFile = {\n ...pkgManifest,\n installed_at: now(),\n // BUG-023: stamp the pin version so computeCurrentSha can reverse-substitute\n // the placeholder when classifying pin-aware hook files during doctor --check-scaffold.\n pin_version: pinVersion,\n };\n writeAtomic(snapshotPath, JSON.stringify(snapshot, null, 2) + '\\n');\n stdout(`[cleargate init] Wrote install snapshot: .cleargate/.install-manifest.json\\n`);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not write install snapshot: ${String(e)}\\n`);\n }\n\n // Remove .uninstalled marker AFTER bootstrap + snapshot complete (whether user chose Y or N).\n // This prevents repeated prompting on subsequent init runs.\n if (uninstalledMarker !== null && fs.existsSync(uninstalledMarkerPath)) {\n try {\n fs.unlinkSync(uninstalledMarkerPath);\n } catch (e) {\n stderr(`[cleargate init] WARNING: could not remove .uninstalled marker: ${String(e)}\\n`);\n }\n }\n\n // Step 7.6 (CR-009): Resolver probe — run the three-branch resolver and print\n // a visible green/red status line. Converts \"invisible silent no-op at first\n // hook fire\" into \"loud failure at install time, when the user is watching\".\n {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n type ResolverBranch = { cmd: string; args: string[] } | null;\n\n // Mirror the bash resolver order: dist first (dogfood), then PATH, then npx.\n let branch: ResolverBranch = null;\n let branchLabel = '';\n\n if (fs.existsSync(distCliPath)) {\n branch = { cmd: 'node', args: [distCliPath, '--version'] };\n branchLabel = `local dist (${distCliPath})`;\n } else {\n // Try `cleargate --version` via PATH\n const whichResult = spawnSyncFn('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n branch = { cmd: 'cleargate', args: ['--version'] };\n branchLabel = 'PATH (global install)';\n } else {\n // Fall back to npx invocation\n branch = { cmd: 'npx', args: ['-y', `cleargate@${pinVersion}`, '--version'] };\n branchLabel = `npx cleargate@${pinVersion} (cold-start ~600ms first call)`;\n }\n }\n\n if (branch !== null) {\n const probeResult = spawnSyncFn(branch.cmd, branch.args, {\n encoding: 'utf8',\n timeout: 15000,\n });\n\n if (probeResult.status === 0) {\n stdout(`[cleargate init] \\u{1F7E2} cleargate CLI resolved via ${branchLabel}\\n`);\n } else {\n // Resolver chain exhausted — the hooks will no-op until cleargate is reachable.\n // Per BUG-015 (2026-04-27): convert from exit(1) to warn-not-block. The probe is a\n // best-effort signal; transient registry issues (CI race conditions, network blips)\n // shouldn't hard-fail init. Hooks will surface their own resolver-failure banners\n // at runtime if the issue persists. User can run `cleargate doctor` to investigate.\n stdout(\n `[cleargate init] \\u{1F7E1} cleargate CLI: not resolvable in this environment.\\n` +\n `[cleargate init] Attempted: ${branchLabel}\\n` +\n `[cleargate init] This is a warning, not a fatal error. Hooks will no-op until resolved.\\n` +\n `[cleargate init] Fix: npm i -g cleargate@${pinVersion} or npx cleargate@${pinVersion} doctor\\n`,\n );\n // Continue init. The resolver-status was emitted; hooks will surface their own\n // failure banners at runtime if needed.\n }\n }\n }\n\n // Step 7.5: Participant identity\n // Skip if .cleargate/.participant.json already exists (idempotent re-init).\n const existingParticipant = readParticipant(cwd);\n if (existingParticipant === null) {\n // Resolve git email as default (no env / host fallback during init — init needs a concrete prompt).\n const identityOpts = opts.identityOpts ?? {};\n\n // Resolve just the git rung: call resolveIdentity with env={} to skip env rung,\n // then check the source.\n const gitOnlyIdentity = resolveIdentity(cwd, {\n ...identityOpts,\n env: {}, // force skip env rung\n });\n const gitEmail =\n gitOnlyIdentity.source === 'git' ? gitOnlyIdentity.email : null;\n\n const stdinIsTTY = opts.stdinIsTTY ?? process.stdin.isTTY ?? false;\n const isNonInteractive = opts.yes === true || !stdinIsTTY;\n\n if (isNonInteractive) {\n // Non-interactive: use git email; if unavailable use host fallback via resolveIdentity\n const finalEmail =\n gitEmail ??\n resolveIdentity(cwd, identityOpts).email;\n\n await writeParticipant(cwd, finalEmail, 'inferred', now);\n stdout(`[cleargate init] Participant identity: ${finalEmail} (inferred)\\n`);\n } else {\n // Interactive: prompt for email.\n // BUG-007: the prior prompt reused the `[cleargate init]` info-log prefix\n // and was followed by a newline, so it visually merged with preceding log\n // lines and the cursor sat on a blank line below — users walked away\n // believing install had finished. Fix:\n // 1. Drop the `[cleargate init]` prefix on the prompt itself so it\n // reads as an interactive question, not a status message.\n // 2. Print a blank separator line above so the eye catches the change.\n // 3. Reject GitHub `users.noreply.github.com` git emails as a default —\n // they're unrouteable identities; users who blindly press Enter end\n // up with a participant identity that can't receive invites.\n const isNoreply = gitEmail !== null && /@users\\.noreply\\.github\\.com$/i.test(gitEmail);\n const defaultEmail = (gitEmail !== null && !isNoreply) ? gitEmail : 'user@localhost';\n stdout('\\n');\n const question = `Participant email (press Enter for default) [${defaultEmail}]:`;\n const answer = await promptEmailFn(question, defaultEmail);\n await writeParticipant(cwd, answer, 'prompted', now);\n stdout(`[cleargate init] Participant identity: ${answer} (prompted)\\n`);\n }\n }\n\n // Step 8: Done\n stdout(\n `[cleargate init] Done. Read CLAUDE.md and .cleargate/knowledge/cleargate-protocol.md to learn the protocol.\\n`,\n );\n\n void now; // suppress unused warning if not used after this\n}\n","/**\n * copy-payload.ts — recursively copy cleargate-planning/ payload to target cwd.\n *\n * Handles overwrite policy:\n * - by default: skip files that already exist with identical content\n * - force=true: overwrite all\n *\n * Does NOT prompt interactively; skip-or-overwrite is determined by `force`.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface CopyReport {\n created: number;\n skipped: number;\n overwritten: number;\n /** Per-file action lines for verbose printing */\n actions: Array<{ action: 'created' | 'skipped' | 'overwritten'; relPath: string }>;\n}\n\nexport interface CopyPayloadOptions {\n force: boolean;\n /**\n * CR-009: When set, substitute `__CLEARGATE_VERSION__` placeholder in hook scripts\n * with this version string. Applies to `.claude/hooks/stamp-and-gate.sh` and\n * `.claude/hooks/session-start.sh`. Use a sed-friendly format: `0.5.0`.\n */\n pinVersion?: string;\n}\n\n/** Hook files that carry the `__CLEARGATE_VERSION__` placeholder (CR-009). */\nconst PIN_PLACEHOLDER = '__CLEARGATE_VERSION__';\nconst HOOK_FILES_WITH_PIN = new Set([\n '.claude/hooks/stamp-and-gate.sh',\n '.claude/hooks/session-start.sh',\n]);\n\n/**\n * Files that copyPayload must NOT copy verbatim. The init pipeline owns these\n * via a downstream step that strips canonical-source preamble or otherwise\n * transforms content. Listing here ensures `listFilesRecursive` skips them.\n *\n * - CLAUDE.md (BUG-016): top-level CLAUDE.md in cleargate-planning/ carries a\n * meta-doc preamble describing the bounded-block contract. Step 5 of init\n * (`injectClaudeMd`) writes only `extractBlock(src)`. Copying the raw source\n * here would leave the preamble in the user repo because step 5 only\n * replaces the inner block region.\n */\nconst SKIP_FILES = new Set<string>(['CLAUDE.md']);\n\n/**\n * Recursively enumerate files under `dir`.\n * Returns paths relative to `dir`.\n */\nfunction listFilesRecursive(dir: string): string[] {\n const results: string[] = [];\n function walk(current: string, rel: string): void {\n const entries = fs.readdirSync(current, { withFileTypes: true });\n for (const entry of entries) {\n const entryRel = rel ? `${rel}/${entry.name}` : entry.name;\n const entryAbs = path.join(current, entry.name);\n if (entry.isDirectory()) {\n walk(entryAbs, entryRel);\n } else {\n results.push(entryRel);\n }\n }\n }\n walk(dir, '');\n return results;\n}\n\n/**\n * Copy all files from `payloadDir` to `targetCwd`, preserving directory structure.\n * Dotfiles and dot-directories (e.g. `.claude/`, `.cleargate/`) are preserved.\n */\nexport function copyPayload(\n payloadDir: string,\n targetCwd: string,\n opts: CopyPayloadOptions,\n): CopyReport {\n const report: CopyReport = { created: 0, skipped: 0, overwritten: 0, actions: [] };\n\n if (!fs.existsSync(payloadDir)) {\n throw new Error(`copyPayload: payloadDir does not exist: ${payloadDir}`);\n }\n\n const files = listFilesRecursive(payloadDir).filter((r) => !SKIP_FILES.has(r));\n\n for (const relPath of files) {\n const srcPath = path.join(payloadDir, relPath);\n const dstPath = path.join(targetCwd, relPath);\n\n // Ensure target directory exists\n fs.mkdirSync(path.dirname(dstPath), { recursive: true });\n\n let srcContent: Buffer | string = fs.readFileSync(srcPath);\n\n // CR-009: substitute __CLEARGATE_VERSION__ placeholder in hook scripts\n if (opts.pinVersion && HOOK_FILES_WITH_PIN.has(relPath)) {\n const text = srcContent.toString('utf8').replaceAll(PIN_PLACEHOLDER, opts.pinVersion);\n srcContent = text;\n }\n\n // Compare: convert srcContent to Buffer for comparison when it's a string\n const srcBuffer = typeof srcContent === 'string' ? Buffer.from(srcContent, 'utf8') : srcContent;\n\n // BUG-018: preserve executable bit. Source `.sh` files are 0o755 in the bundled\n // payload but `fs.writeFileSync` defaults to 0o644 — SessionStart fired\n // \"Permission denied\". Read source mode and propagate any +x bits.\n const srcMode = fs.statSync(srcPath).mode;\n const needsExec = (srcMode & 0o111) !== 0 || relPath.endsWith('.sh');\n\n if (fs.existsSync(dstPath)) {\n const dstContent = fs.readFileSync(dstPath);\n if (srcBuffer.equals(dstContent)) {\n // Identical — skip the write but still re-assert exec bit; the user may\n // have lost it via copy/chmod outside our control.\n if (needsExec && process.platform !== 'win32') {\n fs.chmodSync(dstPath, 0o755);\n }\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n if (!opts.force) {\n // Different content, no force — skip the write but still re-assert exec bit\n // for the same reason as the identical-content skip above (BUG-018 follow-up,\n // HOTFIX-001): a previous in-place edit may have stripped +x.\n if (needsExec && process.platform !== 'win32') {\n fs.chmodSync(dstPath, 0o755);\n }\n report.skipped++;\n report.actions.push({ action: 'skipped', relPath });\n continue;\n }\n // Different + force — overwrite\n fs.writeFileSync(dstPath, srcBuffer);\n if (needsExec && process.platform !== 'win32') {\n fs.chmodSync(dstPath, 0o755);\n }\n report.overwritten++;\n report.actions.push({ action: 'overwritten', relPath });\n } else {\n // New file\n fs.writeFileSync(dstPath, srcBuffer);\n if (needsExec && process.platform !== 'win32') {\n fs.chmodSync(dstPath, 0o755);\n }\n report.created++;\n report.actions.push({ action: 'created', relPath });\n }\n }\n\n return report;\n}\n","/**\n * merge-settings.ts — JSON merge for .claude/settings.json\n *\n * Algorithm (from M4 blueprint):\n * - if existing is null: return addition\n * - otherwise deep-clone existing and merge addition.hooks into result.hooks\n * - For each event (e.g. PostToolUse):\n * - find matching entry by `matcher` field\n * - if absent: push entire new entry\n * - if present: de-dup merge inner hooks[] by exact `command` string match\n * - Preserves all other top-level keys (SubagentStop, permissions, etc.)\n */\n\nexport interface HookCommand {\n type: string;\n command: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n [key: string]: unknown;\n}\n\nexport interface HooksConfig {\n [event: string]: HookEntry[];\n}\n\nexport interface SettingsJson {\n hooks?: HooksConfig;\n [key: string]: unknown;\n}\n\n/**\n * Deep-clone a plain JSON-serializable object.\n */\nfunction deepClone<T>(obj: T): T {\n return JSON.parse(JSON.stringify(obj)) as T;\n}\n\n/**\n * Merge `addition` hook config into `existing` settings.\n * Returns a new merged object; does not mutate either argument.\n *\n * @param existing - parsed .claude/settings.json content, or null if file absent\n * @param addition - the hook config to merge in (must have `hooks` key)\n */\nexport function mergeSettings(\n existing: SettingsJson | null,\n addition: SettingsJson,\n): SettingsJson {\n if (existing === null) {\n return deepClone(addition);\n }\n\n const result: SettingsJson = deepClone(existing);\n\n // Ensure result.hooks exists\n if (!result.hooks) {\n result.hooks = {};\n }\n\n // Merge each event from addition\n for (const [eventName, eventArray] of Object.entries(addition.hooks ?? {})) {\n if (!result.hooks[eventName]) {\n result.hooks[eventName] = [];\n }\n\n for (const newEntry of eventArray) {\n const matchingIdx = result.hooks[eventName].findIndex(\n (e) => e.matcher === newEntry.matcher,\n );\n\n if (matchingIdx === -1) {\n // No matching entry — push entire new entry\n result.hooks[eventName].push(deepClone(newEntry));\n } else {\n // Matcher exists — merge inner hooks[] by de-dup on `command`\n const existingEntry = result.hooks[eventName][matchingIdx];\n const existingInner: HookCommand[] = Array.isArray(existingEntry.hooks)\n ? (existingEntry.hooks as HookCommand[])\n : [];\n\n for (const newInner of newEntry.hooks ?? []) {\n if (!existingInner.some((h) => h.command === newInner.command)) {\n existingInner.push(deepClone(newInner) as HookCommand);\n }\n }\n\n result.hooks[eventName][matchingIdx] = {\n ...existingEntry,\n hooks: existingInner,\n };\n }\n }\n }\n\n return result;\n}\n","/**\n * inject-claude-md.ts — bounded-block injection for CLAUDE.md\n *\n * Block format: <!-- CLEARGATE:START -->\\n<content>\\n<!-- CLEARGATE:END -->\n * Detection regex: /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/ (greedy, see below)\n *\n * Rules:\n * - If existing === null: create file with block as full content (+ trailing newline)\n * - If existing matches: replace the bounded block in place (idempotent)\n * - If existing no match: append block with 2 leading newlines (preserve user content above)\n */\n\n// Greedy match: from first <!-- CLEARGATE:START --> to LAST <!-- CLEARGATE:END -->.\n// Greedy is correct here because:\n// (a) cleargate-planning/CLAUDE.md body text mentions both markers inline as code references,\n// and non-greedy would stop at the inline END before the real one.\n// (b) We assume at most one cleargate block per file (idempotency requires it).\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->[\\s\\S]*<!-- CLEARGATE:END -->/;\n\n/**\n * Extract the bounded block from a source file (e.g. cleargate-planning/CLAUDE.md).\n * Returns the text from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive.\n * Throws if the markers are not found.\n */\nexport function extractBlock(sourceContent: string): string {\n const match = BLOCK_REGEX.exec(sourceContent);\n if (!match) {\n throw new Error('inject-claude-md: CLEARGATE:START/END markers not found in source content');\n }\n return match[0];\n}\n\n/**\n * Inject or update the bounded block in an existing CLAUDE.md.\n *\n * @param existing - current content of CLAUDE.md, or null if file doesn't exist\n * @param block - the full block to inject, from <!-- CLEARGATE:START --> to <!-- CLEARGATE:END --> inclusive\n * @returns - new file content (ready to write)\n */\nexport function injectClaudeMd(existing: string | null, block: string): string {\n if (existing === null) {\n // Create new file with block as full content\n return block + '\\n';\n }\n\n if (BLOCK_REGEX.test(existing)) {\n // Replace existing block in place\n return existing.replace(BLOCK_REGEX, block);\n }\n\n // Append block with 2 leading newlines\n return existing.trimEnd() + '\\n\\n' + block + '\\n';\n}\n","/**\n * inject-mcp-json.ts — write or merge `.mcp.json` so Claude Code registers the\n * cleargate MCP server. Called from `init` Step 7. (BUG-017)\n *\n * Behavior:\n * - Greenfield: writes `{ mcpServers: { cleargate: { type, url } } }`.\n * - Existing without `mcpServers`: adds `mcpServers.cleargate`.\n * - Existing with other servers: leaves them alone, sets/replaces `mcpServers.cleargate`.\n * - Idempotent: re-running on identical state produces byte-identical output.\n *\n * NOTE: this writes the HTTP transport entry. Claude Code's HTTP MCP transport\n * cannot drive auth against the cleargate server today (BUG-019). The 0.7.0\n * shipment is knowingly transitional — the long-term fix is a stdio shim\n * (`cleargate mcp serve`) that this same file will switch to once shipped.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface McpServerEntry {\n type?: 'http' | 'sse' | 'stdio';\n url?: string;\n command?: string;\n args?: string[];\n headers?: Record<string, string>;\n}\n\nexport interface McpJsonShape {\n mcpServers?: Record<string, McpServerEntry>;\n [k: string]: unknown;\n}\n\n/**\n * Pure merge: returns the new file content given the previous parse and the\n * desired cleargate entry. Stable JSON formatting (2-space indent + trailing\n * newline) for byte-equality on idempotent re-runs.\n */\nexport function mergeMcpJson(existing: McpJsonShape | null, entry: McpServerEntry): string {\n const next: McpJsonShape = existing ? { ...existing } : {};\n const servers: Record<string, McpServerEntry> = { ...(next.mcpServers ?? {}) };\n servers.cleargate = entry;\n next.mcpServers = servers;\n return JSON.stringify(next, null, 2) + '\\n';\n}\n\n/**\n * Build the stdio entry for a given pinned cleargate version.\n *\n * BUG-019 + post-0.8.0 fix: prior 0.8.0 used `command: \"cleargate\"` which\n * required a global install. Most users `npx cleargate init` — no global\n * binary on PATH — so Claude Code's spawn failed. Switching to `npx -y\n * cleargate@<pin>` works for both populations (cached after first hit).\n * Pinning the version is the CR-009 resolver pattern: `.mcp.json` doesn't\n * float past the cleargate version that wrote it.\n */\nexport function buildStdioEntry(pinVersion: string): McpServerEntry {\n return {\n command: 'npx',\n args: ['-y', `cleargate@${pinVersion}`, 'mcp', 'serve'],\n };\n}\n\n/**\n * Default stdio entry exported for tests. Production callers should pass an\n * explicit pin (init reads pkg.version).\n */\nexport const STDIO_ENTRY_DEFAULT: McpServerEntry = buildStdioEntry('latest');\n\n/**\n * Filesystem-side entry point. Reads `<cwd>/.mcp.json` if present, merges,\n * writes back. Returns one of {created, updated, unchanged} for caller logging.\n *\n * @param cwd target directory\n * @param pinVersion cleargate version to pin in the npx invocation; the\n * init pipeline passes pkg.version. Defaults to `latest`\n * for callers that don't have a version handy (tests).\n */\nexport function injectMcpJson(\n cwd: string,\n pinVersion: string = 'latest',\n): 'created' | 'updated' | 'unchanged' {\n const dst = path.join(cwd, '.mcp.json');\n const entry: McpServerEntry = buildStdioEntry(pinVersion);\n\n let existing: McpJsonShape | null = null;\n let existingRaw: string | null = null;\n if (fs.existsSync(dst)) {\n existingRaw = fs.readFileSync(dst, 'utf8');\n try {\n existing = JSON.parse(existingRaw) as McpJsonShape;\n } catch {\n // Malformed — surface to caller via stderr; safest move is to refuse\n // overwrite. Caller decides whether to abort or continue with a warning.\n throw new Error(\n `inject-mcp-json: ${dst} is not valid JSON; refusing to overwrite. Fix or remove the file and re-run init.`,\n );\n }\n }\n\n const next = mergeMcpJson(existing, entry);\n\n if (existingRaw !== null && existingRaw === next) {\n return 'unchanged';\n }\n\n fs.writeFileSync(dst, next);\n return existingRaw === null ? 'created' : 'updated';\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\n\nexport interface WikiBuildOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp for last_ingest field (defaults to new Date().toISOString()) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n}\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\n/** Terminal statuses — items with these statuses go to the Archive section. */\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n/**\n * Rollup threshold: if an active epic has >= ROLLUP_THRESHOLD active stories,\n * collapse them into a single summary line instead of individual rows.\n */\nconst ROLLUP_THRESHOLD = 3;\n\n/**\n * Active index bucket order: epics → sprints → proposals → crs → bugs → orphan stories.\n * Topics always skipped (written by query --persist only).\n */\nconst ACTIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\nexport async function wikiBuildHandler(opts: WikiBuildOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const templateDir = opts.templateDir;\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`wiki build: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n // Ensure wiki directory structure exists\n for (const bucket of BUCKET_ORDER) {\n fs.mkdirSync(path.join(wikiRoot, bucket), { recursive: true });\n }\n\n // Step 2: scan raw items\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Step 3: write per-item wiki pages\n const timestamp = now();\n let pagesWritten = 0;\n\n for (const item of items) {\n const sha = getGitSha(item.rawPath, gitRunner) ?? '';\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n\n const wikiPage: WikiPage = {\n type: item.type,\n id: item.id,\n parent,\n children,\n status: String(item.fm['status'] ?? ''),\n remote_id: String(item.fm['remote_id'] ?? ''),\n raw_path: item.rawPath,\n last_ingest: timestamp,\n last_ingest_commit: sha,\n repo: item.repo,\n };\n\n const body = buildPageBody(item, wikiPage);\n const content = serializePage(wikiPage, body);\n\n const pageDir = path.join(wikiRoot, item.bucket);\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(path.join(pageDir, `${item.id}.md`), content, 'utf8');\n pagesWritten++;\n }\n\n // Step 4: build index.md\n const indexContent = buildIndex(items);\n fs.writeFileSync(path.join(wikiRoot, 'index.md'), indexContent, 'utf8');\n\n // Step 5: build log.md\n const logContent = buildLog(items, timestamp);\n fs.writeFileSync(path.join(wikiRoot, 'log.md'), logContent, 'utf8');\n\n // Step 6: write synthesis pages\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n\n // Step 7: report\n stdout(`wiki build: OK (${pagesWritten} pages written)\\n`);\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: RawItem, page: WikiPage): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.').slice(0, 200);\n\n const blastParts: string[] = [];\n if (page.parent) blastParts.push(page.parent);\n for (const child of page.children) blastParts.push(child);\n\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction buildIndex(items: RawItem[]): string {\n const header = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n ];\n\n // Empty-delivery case\n if (items.length === 0) {\n return [\n ...header,\n '## Active',\n '',\n '_No active items._',\n '',\n '## Archive',\n '',\n '_No archived items._',\n '',\n ].join('\\n');\n }\n\n // 1. Partition items into active and archived\n const active: RawItem[] = [];\n const archived: RawItem[] = [];\n for (const item of items) {\n const status = String(item.fm['status'] ?? '');\n if (TERMINAL_STATUSES.has(status)) {\n archived.push(item);\n } else {\n active.push(item);\n }\n }\n\n // 2. Build storiesByEpic: active stories grouped by parent epic id.\n // Only include stories whose parent epic is itself active.\n const activeEpicIds = new Set(\n active.filter((i) => i.bucket === 'epics').map((i) => i.id),\n );\n const storiesByEpic = new Map<string, RawItem[]>();\n\n for (const item of active) {\n if (item.bucket !== 'stories') continue;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n // Strip [[ ]] wrappers if present\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n if (epicId && activeEpicIds.has(epicId)) {\n const list = storiesByEpic.get(epicId) ?? [];\n list.push(item);\n storiesByEpic.set(epicId, list);\n }\n }\n\n // Orphan active stories: no parent_epic_ref OR parent epic not active\n const orphanStories = active.filter((item) => {\n if (item.bucket !== 'stories') return false;\n const rawRef = String(item.fm['parent_epic_ref'] ?? '');\n const epicId = rawRef.startsWith('[[') && rawRef.endsWith(']]')\n ? rawRef.slice(2, -2)\n : rawRef;\n return !epicId || !activeEpicIds.has(epicId);\n });\n\n // 3. Emit ## Active section\n const activeLines: string[] = ['## Active', ''];\n\n for (const bucket of ACTIVE_BUCKET_ORDER) {\n if (bucket === 'stories') {\n // Orphan stories only\n const sorted = orphanStories.slice().sort((a, b) => a.id.localeCompare(b.id));\n for (const item of sorted) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n } else if (bucket === 'epics') {\n const epicItems = active\n .filter((i) => i.bucket === 'epics')\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const epic of epicItems) {\n const status = String(epic.fm['status'] ?? '');\n activeLines.push(`- [[${epic.id}]] (${epic.type}) — ${status}`);\n\n // 4. Emit story rollup or individual rows under the epic\n const epicStories = (storiesByEpic.get(epic.id) ?? [])\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n if (epicStories.length >= ROLLUP_THRESHOLD) {\n // Build status breakdown: count per status, sort by count desc then status asc\n const counts = new Map<string, number>();\n for (const s of epicStories) {\n const st = String(s.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n const breakdown = [...counts.entries()]\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n // Extract epic numeric prefix for rollup label (e.g. EPIC-014 → 014)\n const epicNum = epic.id.replace(/^EPIC-/, '');\n activeLines.push(` - STORY-${epicNum}-xx (${epicStories.length} stories) — ${breakdown}`);\n } else {\n for (const story of epicStories) {\n const st = String(story.fm['status'] ?? '');\n activeLines.push(` - [[${story.id}]] (${story.type}) — ${st}`);\n }\n }\n }\n } else {\n const bucketItems = active\n .filter((i) => i.bucket === bucket)\n .slice()\n .sort((a, b) => a.id.localeCompare(b.id));\n\n for (const item of bucketItems) {\n const status = String(item.fm['status'] ?? '');\n activeLines.push(`- [[${item.id}]] (${item.type}) — ${status}`);\n }\n }\n }\n\n activeLines.push('');\n\n // 5. Emit ## Archive section — per-bucket summary only\n const archiveLines: string[] = ['## Archive', ''];\n // Bucket order for archive summary (excluding topics and stories — stories\n // don't get their own archive summary line per plan)\n const ARCHIVE_BUCKET_ORDER = ['epics', 'sprints', 'proposals', 'crs', 'bugs', 'stories'] as const;\n\n for (const bucket of ARCHIVE_BUCKET_ORDER) {\n const bucketArchived = archived.filter((i) => i.bucket === bucket);\n if (bucketArchived.length === 0) continue;\n\n // Count per status\n const counts = new Map<string, number>();\n for (const item of bucketArchived) {\n const st = String(item.fm['status'] ?? '');\n counts.set(st, (counts.get(st) ?? 0) + 1);\n }\n // Sort by count desc then status asc, omit zero-count statuses\n const parts = [...counts.entries()]\n .filter(([, n]) => n > 0)\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([st, n]) => `${n} ${st}`)\n .join(' · ');\n\n const label = BUCKET_LABELS[bucket] ?? bucket;\n archiveLines.push(`- ${label}: ${parts} · [expand](archive/${bucket}.md)`);\n }\n\n archiveLines.push('');\n\n // 6. Emit ## Contradictions section — links to advisory log\n const contradictionLines: string[] = [\n '## Contradictions',\n '',\n 'Advisory log of detected contradictions between wiki pages. Populated by ingest Phase 4.',\n '',\n 'See [contradictions.md](contradictions.md) for the append-only finding log.',\n 'Human applies `label: true-positive | false-positive | nitpick` per entry.',\n '',\n ];\n\n return [...header, ...activeLines, ...archiveLines, ...contradictionLines].join('\\n');\n}\n\nfunction buildLog(items: RawItem[], timestamp: string): string {\n if (items.length === 0) {\n return '# Wiki Event Log\\n\\n';\n }\n\n const entries = items.map((item) =>\n [\n `- timestamp: \"${timestamp}\"`,\n ` actor: \"cleargate wiki build\"`,\n ` action: \"create\"`,\n ` target: \"${item.id}\"`,\n ` path: \"${item.rawPath}\"`,\n ].join('\\n'),\n );\n\n return ['# Wiki Event Log', '', ...entries, ''].join('\\n');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { deriveBucket } from './derive-bucket.js';\nimport { deriveRepo } from './derive-repo.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface RawItem {\n /** Absolute path on disk */\n absPath: string;\n /** Path relative to repo root */\n rawPath: string;\n id: string;\n bucket: string;\n type: WikiPageType;\n repo: RepoTag;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/**\n * Scan pending-sync/ + archive/ under deliveryRoot for markdown work items.\n * Returns a sorted (by id) list of parsed raw items.\n */\nexport function scanRawItems(deliveryRoot: string, repoRoot: string): RawItem[] {\n const results: RawItem[] = [];\n\n for (const subdir of ['pending-sync', 'archive']) {\n const dir = path.join(deliveryRoot, subdir);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { recursive: true, encoding: 'utf8' }) as string[];\n for (const rel of entries) {\n if (!rel.endsWith('.md')) continue;\n if (rel.includes('~') || rel.startsWith('.')) continue;\n\n const absPath = path.join(dir, rel);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n // Compute rawPath relative to repo root\n const rawPath = path.relative(repoRoot, absPath).replace(/\\\\/g, '/');\n\n // Check exclusions\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) continue;\n\n // Derive bucket info from filename\n const filename = path.basename(absPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch {\n // Not a recognized work-item filename — skip silently\n continue;\n }\n\n // Derive repo from path\n let repo: RepoTag;\n try {\n repo = deriveRepo(rawPath);\n } catch {\n continue;\n }\n\n // Parse frontmatter\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n // Malformed frontmatter — skip\n continue;\n }\n\n results.push({\n absPath,\n rawPath,\n id: bucketInfo.id,\n bucket: bucketInfo.bucket,\n type: bucketInfo.type,\n repo,\n fm,\n body,\n });\n }\n }\n\n // Sort deterministically by id (alphanumeric ascending)\n results.sort((a, b) => a.id.localeCompare(b.id));\n return results;\n}\n","import type { WikiPageType } from './page-schema.js';\n\nexport interface BucketInfo {\n type: WikiPageType;\n id: string;\n bucket: string;\n}\n\nconst PREFIX_MAP: Array<{ prefix: string; type: WikiPageType; bucket: string }> = [\n { prefix: 'EPIC-', type: 'epic', bucket: 'epics' },\n { prefix: 'STORY-', type: 'story', bucket: 'stories' },\n { prefix: 'SPRINT-', type: 'sprint', bucket: 'sprints' },\n { prefix: 'PROPOSAL-', type: 'proposal', bucket: 'proposals' },\n { prefix: 'CR-', type: 'cr', bucket: 'crs' },\n { prefix: 'BUG-', type: 'bug', bucket: 'bugs' },\n];\n\n/**\n * Derive bucket, type, and id from a filename stem.\n * Filename `STORY-042-01_name.md` → `{ type: 'story', id: 'STORY-042-01', bucket: 'stories' }`.\n */\nexport function deriveBucket(filename: string): BucketInfo {\n // Strip path if any\n const base = filename.includes('/') ? filename.split('/').pop()! : filename;\n // Remove .md suffix\n const stem = base.endsWith('.md') ? base.slice(0, -3) : base;\n // id = everything before the first `_`\n const underscoreIdx = stem.indexOf('_');\n const id = underscoreIdx === -1 ? stem : stem.slice(0, underscoreIdx);\n\n for (const { prefix, type, bucket } of PREFIX_MAP) {\n if (id.startsWith(prefix)) {\n return { type, id, bucket };\n }\n }\n\n throw new Error(`deriveBucket: cannot determine bucket for filename: ${filename}`);\n}\n","import type { RepoTag } from './page-schema.js';\n\n/**\n * A1 helper: derive the `repo` tag from a raw file path prefix.\n * Mapping is per §10.4 field notes.\n */\nexport function deriveRepo(rawPath: string): RepoTag {\n if (rawPath.startsWith('cleargate-cli/')) return 'cli';\n if (rawPath.startsWith('mcp/')) return 'mcp';\n if (rawPath.startsWith('.cleargate/') || rawPath.startsWith('cleargate-planning/')) return 'planning';\n throw new Error(`cannot derive repo for path: ${rawPath}`);\n}\n","import { spawnSync } from 'node:child_process';\n\nexport type GitRunner = (cmd: string, args: string[]) => string;\n\n/**\n * A2 helper: return the git SHA of the last commit touching rawPath.\n * Returns null when the file is untracked (empty stdout, exit 0).\n * Accepts an optional `runner` test seam.\n */\nexport function getGitSha(rawPath: string, runner?: GitRunner): string | null {\n const run = runner ?? defaultRunner;\n const out = run('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n return out.length > 0 ? out : null;\n}\n\nfunction defaultRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n return result.stdout ?? '';\n}\n","/**\n * §10.4 Wiki Page Schema — nine required frontmatter fields plus three optional.\n * Lint will flag any extra or missing required fields; last_contradict_sha,\n * parent_cleargate_id, and sprint_cleargate_id are optional.\n */\n\nexport type WikiPageType = 'epic' | 'story' | 'sprint' | 'proposal' | 'cr' | 'bug' | 'topic';\nexport type RepoTag = 'cli' | 'mcp' | 'planning';\n\n/** The nine required + three optional frontmatter fields every wiki page must satisfy. */\nexport interface WikiPage {\n type: WikiPageType;\n id: string;\n parent: string; // \"[[EPIC-042]]\" or \"\" if none\n children: string[]; // [\"[[STORY-042-01]]\", ...]\n status: string;\n remote_id: string;\n raw_path: string;\n last_ingest: string; // ISO 8601 UTC\n last_ingest_commit: string; // git SHA or \"\"\n repo: RepoTag;\n /** Optional — populated by ingest Phase 4 (§10.10). SHA of raw file at last contradict check. */\n last_contradict_sha?: string;\n /** Optional — canonical cleargate-id of the parent work item (§11.7). */\n parent_cleargate_id?: string;\n /** Optional — canonical cleargate-id of the owning sprint (§11.7). */\n sprint_cleargate_id?: string;\n}\n\n/** Serialise a WikiPage frontmatter + body into a markdown string. */\nexport function serializePage(page: WikiPage, body: string): string {\n const childrenYaml =\n page.children.length === 0\n ? '[]'\n : '\\n' + page.children.map((c) => ` - \"${c}\"`).join('\\n');\n\n const lines = [\n '---',\n `type: ${page.type}`,\n `id: \"${page.id}\"`,\n `parent: \"${page.parent}\"`,\n `children: ${childrenYaml}`,\n `status: \"${page.status}\"`,\n `remote_id: \"${page.remote_id}\"`,\n `raw_path: \"${page.raw_path}\"`,\n `last_ingest: \"${page.last_ingest}\"`,\n `last_ingest_commit: \"${page.last_ingest_commit}\"`,\n `repo: \"${page.repo}\"`,\n ];\n // Emit optional fields only when present\n if (page.last_contradict_sha !== undefined) {\n lines.push(`last_contradict_sha: \"${page.last_contradict_sha}\"`);\n }\n if (page.parent_cleargate_id !== undefined) {\n lines.push(`parent_cleargate_id: \"${page.parent_cleargate_id}\"`);\n }\n if (page.sprint_cleargate_id !== undefined) {\n lines.push(`sprint_cleargate_id: \"${page.sprint_cleargate_id}\"`);\n }\n lines.push('---');\n\n const fm = lines.join('\\n');\n return `${fm}\\n\\n${body}`;\n}\n\n/** Parse a serialised wiki page back into a WikiPage. Throws on schema violations. */\nexport function parsePage(raw: string): WikiPage {\n const { fm } = parseFmRaw(raw);\n\n const type = fm['type'] as WikiPageType;\n const id = String(fm['id'] ?? '');\n const parent = String(fm['parent'] ?? '');\n const rawChildren = fm['children'];\n const children: string[] = Array.isArray(rawChildren)\n ? (rawChildren as unknown[]).map(String)\n : [];\n const status = String(fm['status'] ?? '');\n const remote_id = String(fm['remote_id'] ?? '');\n const raw_path = String(fm['raw_path'] ?? '');\n const last_ingest = String(fm['last_ingest'] ?? '');\n const last_ingest_commit = String(fm['last_ingest_commit'] ?? '');\n const repo = fm['repo'] as RepoTag;\n // Optional field — only populated when present (§10.10 Phase 4)\n const last_contradict_sha = fm['last_contradict_sha'] !== undefined\n ? String(fm['last_contradict_sha'])\n : undefined;\n // Optional hierarchy keys (§11.7)\n const parent_cleargate_id = fm['parent_cleargate_id'] !== undefined\n ? String(fm['parent_cleargate_id'])\n : undefined;\n const sprint_cleargate_id = fm['sprint_cleargate_id'] !== undefined\n ? String(fm['sprint_cleargate_id'])\n : undefined;\n\n return { type, id, parent, children, status, remote_id, raw_path, last_ingest, last_ingest_commit, repo, last_contradict_sha, parent_cleargate_id, sprint_cleargate_id };\n}\n\nfunction parseFmRaw(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') throw new Error('parsePage: missing opening ---');\n let close = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { close = i; break; }\n }\n if (close === -1) throw new Error('parsePage: missing closing ---');\n const fmLines = lines.slice(1, close);\n const body = lines.slice(close + 1).join('\\n').replace(/^\\n/, '');\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '[]') { fm[key] = []; continue; }\n if (val === '') { fm[key] = []; continue; }\n // inline list check\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1);\n fm[key] = inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return { fm, body };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the active-sprint synthesis page.\n * Loads template from templates/synthesis/active-sprint.md.\n * Partitions sprints by activated_at / completed_at frontmatter values.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'active-sprint.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n\n // Partition sprints:\n // - active: activated_at is set (non-null, non-empty) AND completed_at is not set\n // - completed: completed_at is set\n // - planned: neither activated_at nor completed_at is set\n const active = sprints.filter((s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n const completed = sprints.filter((s) => isSet(s.fm['completed_at']));\n const planned = sprints.filter((s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']));\n\n const data: Record<string, unknown> = {\n active: active.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_active: active.length === 0 ? [{}] : [],\n planned: planned.map((s) => ({ id: s.id, status: String(s.fm['status'] ?? 'unknown') })),\n no_planned: planned.length === 0 ? [{}] : [],\n completed: completed.slice(0, 3).map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_completed: completed.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // tsup bundles all modules into dist/cli.js.\n // When running the built bundle: import.meta.url = file://.../cleargate-cli/dist/cli.js\n // __dirname = cleargate-cli/dist/\n // ../templates/synthesis = cleargate-cli/templates/synthesis ✓ (source)\n // AND dist/templates/synthesis is also available (copied by onSuccess) ✓\n //\n // When vitest runs source (test seam): templateDir is always passed explicitly,\n // so this default is only used for the built/production case.\n //\n // Strategy: go one level up from the file containing this code (works for both\n // the dist/ bundle and npm-published dist/ layout), then into templates/synthesis.\n // For dist/cli.js: one up = package root → templates/synthesis. ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * Tiny Mustache-lite template renderer.\n * Supports:\n * {{var}} — variable substitution (missing → empty string)\n * {{#section}}...{{/section}} — array iteration; inner {{field}} resolves\n * against the current array element\n *\n * Anything more complex throws an Error.\n * No external dependencies.\n */\n\n/** Render a template string with the given data context. */\nexport function renderTemplate(template: string, data: Record<string, unknown>): string {\n // Validate: no nested sections or unsupported tags\n // We only support {{var}}, {{#section}}...{{/section}}\n const tagRe = /\\{\\{([^}]+)\\}\\}/g;\n const matches = [...template.matchAll(tagRe)].map((m) => m[1].trim());\n for (const tag of matches) {\n if (tag.startsWith('^') || tag.startsWith('>') || tag.startsWith('!') || tag.startsWith('=')) {\n throw new Error(`renderTemplate: unsupported tag type: {{${tag}}}`);\n }\n }\n\n return renderSection(template, data);\n}\n\nfunction renderSection(template: string, ctx: Record<string, unknown>): string {\n // Process {{#section}}...{{/section}} blocks first\n const sectionRe = /\\{\\{#(\\w+)\\}\\}([\\s\\S]*?)\\{\\{\\/\\1\\}\\}/g;\n\n let result = template.replace(sectionRe, (_match, key: string, inner: string) => {\n const val = ctx[key];\n if (!Array.isArray(val)) {\n // Non-array truthy: render inner once with same ctx; falsy: skip\n if (!val) return '';\n return renderSection(inner, ctx);\n }\n if (val.length === 0) return '';\n return val\n .map((item: unknown) => {\n const itemCtx =\n item !== null && typeof item === 'object'\n ? (item as Record<string, unknown>)\n : { '.': item };\n return renderSection(inner, itemCtx);\n })\n .join('');\n });\n\n // Then substitute remaining {{var}} tokens\n result = result.replace(/\\{\\{(\\w+)\\}\\}/g, (_match, key: string) => {\n const val = ctx[key];\n if (val === undefined || val === null) return '';\n return String(val);\n });\n\n return result;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the open-gates (blocked items) synthesis page.\n * Loads template from templates/synthesis/open-gates.md.\n *\n * Three gate buckets (matching real corpus textual statuses):\n * Gate 1 — proposals with approved: false OR status: \"Draft\" / \"Approved\" (not yet shipped)\n * Gate 2 — stories with ambiguity starting with 🟡 or 🔴\n * Gate 3 — any item with status: \"Ready\" AND remote_id empty / null\n *\n * NOTE: The previous implementation filtered on status.includes('🔴') which matched\n * zero items in the real corpus (actual statuses are textual: Draft, Ready, Planned,\n * Active, Completed, Approved). This is the corpus-shape fix for STORY-002-09.\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'open-gates.md'), 'utf8');\n\n // Gate 1: proposals pending approval\n const gate1 = state.filter((i) => {\n if (i.bucket !== 'proposals') return false;\n const status = String(i.fm['status'] ?? '');\n const approved = i.fm['approved'];\n // Proposals that are Draft or explicitly not approved\n return status === 'Draft' || approved === false || approved === 'false';\n });\n\n // Gate 2: stories with elevated ambiguity (🟡 Medium or 🔴 High)\n const gate2 = state.filter((i) => {\n if (i.bucket !== 'stories') return false;\n const ambiguity = String(i.fm['ambiguity'] ?? '');\n return ambiguity.startsWith('🟡') || ambiguity.startsWith('🔴');\n });\n\n // Gate 3: items that are Ready but not yet pushed (remote_id empty or null)\n const gate3 = state.filter((i) => {\n const status = String(i.fm['status'] ?? '');\n if (status !== 'Ready') return false;\n const remoteId = i.fm['remote_id'];\n return remoteId === null || remoteId === undefined || String(remoteId).trim() === '';\n });\n\n const data: Record<string, unknown> = {\n gate1: gate1.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate1: gate1.length === 0 ? [{}] : [],\n gate2: gate2.map((i) => ({ id: i.id, ambiguity: String(i.fm['ambiguity'] ?? '') })),\n no_gate2: gate2.length === 0 ? [{}] : [],\n gate3: gate3.map((i) => ({ id: i.id, status: String(i.fm['status'] ?? '') })),\n no_gate3: gate3.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the product-state synthesis page.\n * Loads template from templates/synthesis/product-state.md.\n *\n * Shipped = items in archive/ subdir (rawPath contains '/archive/')\n * Active = status is Active, In Progress, or 🟢-prefixed\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'product-state.md'), 'utf8');\n\n function countBucket(bucket: string) {\n return state.filter((i) => i.bucket === bucket);\n }\n\n function isShipped(item: RawItem) {\n return item.rawPath.includes('/archive/');\n }\n\n function isActive(item: RawItem) {\n const status = String(item.fm['status'] ?? '');\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n }\n\n const buckets = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs'];\n\n function countFor(bucket: string, predicate: (i: RawItem) => boolean): number {\n return countBucket(bucket).filter(predicate).length;\n }\n\n const epics = countBucket('epics');\n const activeEpicsList = epics.filter(isActive);\n const shippedItems = state.filter(isShipped);\n\n const data: Record<string, unknown> = {\n // Totals\n total_epics: epics.length,\n total_stories: countBucket('stories').length,\n total_sprints: countBucket('sprints').length,\n total_proposals: countBucket('proposals').length,\n total_crs: countBucket('crs').length,\n total_bugs: countBucket('bugs').length,\n\n // Active counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`active_${b}`, countFor(b, isActive)])),\n\n // Shipped counts (per bucket)\n ...Object.fromEntries(buckets.map((b) => [`shipped_${b}`, countFor(b, isShipped)])),\n\n // Active epics list\n active_epics_list: activeEpicsList.map((i) => ({\n id: i.id,\n status: String(i.fm['status'] ?? ''),\n })),\n no_active_epics: activeEpicsList.length === 0 ? [{}] : [],\n\n // Shipped items list\n shipped_items: shippedItems.map((i) => ({\n id: i.id,\n bucket: i.bucket,\n status: String(i.fm['status'] ?? ''),\n })),\n no_shipped: shippedItems.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RawItem } from '../scan.js';\nimport { renderTemplate } from './render.js';\n\n/**\n * Compile the roadmap synthesis page.\n * Loads template from templates/synthesis/roadmap.md.\n *\n * Sprint buckets (by activated_at / completed_at):\n * in-flight: activated_at set AND completed_at not set\n * planned: neither set\n * shipped: completed_at set\n *\n * Epic buckets (by status):\n * active: Active / In Progress / 🟢-prefixed\n * planned: Ready / Planned / Draft\n * shipped: Completed / Approved\n */\nexport function compile(state: RawItem[], templateDir?: string): string {\n const tplDir = templateDir ?? resolveDefaultTemplateDir();\n const tpl = fs.readFileSync(path.join(tplDir, 'roadmap.md'), 'utf8');\n\n const sprints = state.filter((i) => i.bucket === 'sprints');\n const epics = state.filter((i) => i.bucket === 'epics');\n\n // Sprint partitions\n const inFlightSprints = sprints.filter(\n (s) => isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const plannedSprints = sprints.filter(\n (s) => !isSet(s.fm['activated_at']) && !isSet(s.fm['completed_at']),\n );\n const shippedSprints = sprints.filter((s) => isSet(s.fm['completed_at']));\n\n // Epic partitions\n const activeEpics = epics.filter((e) => isActiveStatus(String(e.fm['status'] ?? '')));\n const plannedEpics = epics.filter((e) => isPlannedStatus(String(e.fm['status'] ?? '')));\n const shippedEpics = epics.filter((e) => isShippedStatus(String(e.fm['status'] ?? '')));\n\n const data: Record<string, unknown> = {\n in_flight_sprints: inFlightSprints.map((s) => ({\n id: s.id,\n activated_at: String(s.fm['activated_at'] ?? ''),\n })),\n no_in_flight_sprints: inFlightSprints.length === 0 ? [{}] : [],\n\n planned_sprints: plannedSprints.map((s) => ({\n id: s.id,\n status: String(s.fm['status'] ?? ''),\n })),\n no_planned_sprints: plannedSprints.length === 0 ? [{}] : [],\n\n shipped_sprints: shippedSprints.map((s) => ({\n id: s.id,\n completed_at: String(s.fm['completed_at'] ?? ''),\n })),\n no_shipped_sprints: shippedSprints.length === 0 ? [{}] : [],\n\n active_epics: activeEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_active_epics: activeEpics.length === 0 ? [{}] : [],\n\n planned_epics: plannedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_planned_epics: plannedEpics.length === 0 ? [{}] : [],\n\n shipped_epics: shippedEpics.map((e) => ({ id: e.id, status: String(e.fm['status'] ?? '') })),\n no_shipped_epics: shippedEpics.length === 0 ? [{}] : [],\n };\n\n return renderTemplate(tpl, data);\n}\n\nfunction isSet(val: unknown): boolean {\n if (val === null || val === undefined) return false;\n const s = String(val).trim();\n return s !== '' && s !== 'null';\n}\n\nfunction isActiveStatus(status: string): boolean {\n return (\n status === 'Active' ||\n status === 'In Progress' ||\n status.startsWith('🟢') ||\n status === '🟡 in-flight'\n );\n}\n\nfunction isPlannedStatus(status: string): boolean {\n return status === 'Ready' || status === 'Planned' || status === 'Draft';\n}\n\nfunction isShippedStatus(status: string): boolean {\n return status === 'Completed' || status === 'Approved';\n}\n\nfunction resolveDefaultTemplateDir(): string {\n // Bundle: dist/cli.js → __dirname = dist/, .. = package root → templates/synthesis ✓\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n return path.resolve(__dirname, '..', 'templates', 'synthesis');\n}\n","/**\n * manifest.ts — STORY-009-01\n *\n * Scaffold manifest loading, drift classification, and atomic drift-state writing.\n * Node built-ins only: fs/promises, path.\n *\n * MANIFEST.json does not exist until STORY-009-02 runs `npm run build`.\n * loadPackageManifest throws a clear error when the file is absent — not raw ENOENT.\n */\n\nimport { readFile, writeFile, rename, mkdir } from 'node:fs/promises';\nimport { existsSync, readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { hashNormalized } from './sha256.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type Tier =\n | 'protocol'\n | 'template'\n | 'agent'\n | 'hook'\n | 'script'\n | 'skill'\n | 'cli-config'\n | 'user-artifact'\n | 'derived';\n\nexport type DriftState =\n | 'clean'\n | 'user-modified'\n | 'upstream-changed'\n | 'both-changed'\n | 'untracked';\n\nexport interface ManifestEntry {\n path: string;\n sha256: string | null;\n tier: Tier;\n overwrite_policy: 'always' | 'merge-3way' | 'skip' | 'preserve' | 'pin-aware';\n preserve_on_uninstall: boolean;\n}\n\nexport interface ManifestFile {\n cleargate_version: string;\n generated_at: string;\n files: ManifestEntry[];\n /**\n * Present only in `.cleargate/.install-manifest.json` (the install snapshot).\n * Stamped by `cleargate init` as the FINAL step (STORY-009-03).\n * Not present in the package-shipped MANIFEST.json.\n */\n installed_at?: string;\n /**\n * BUG-023: The pin version that was substituted into hook scripts during install.\n * Present only in install snapshots — not in the package-shipped MANIFEST.json.\n * Used by `computeCurrentSha` to reverse-substitute pin-aware hook files before\n * hashing, so drift classification compares apples to apples.\n * Absent in snapshots predating this fix — backwards-compatible (old installs\n * continue to report user-modified until the user re-runs `cleargate init`).\n */\n pin_version?: string;\n}\n\nexport interface DriftMapEntry {\n state: DriftState;\n entry: ManifestEntry;\n install_sha: string | null;\n current_sha: string | null;\n package_sha: string | null;\n}\n\nexport interface DriftMap {\n [filePath: string]: DriftMapEntry;\n}\n\n/**\n * The on-disk shape of `.cleargate/.drift-state.json`.\n * Wraps the DriftMap with a `last_refreshed` timestamp so the daily-throttle\n * logic in `cleargate doctor --check-scaffold` can skip re-computation.\n */\nexport interface DriftStateFile {\n last_refreshed: string;\n drift: DriftMap;\n}\n\n// ─── Options ──────────────────────────────────────────────────────────────────\n\nexport interface LoadPackageManifestOpts {\n /**\n * Override the root directory where MANIFEST.json is resolved.\n * Default: resolved via import.meta.url (1 level up from dist/ in prod,\n * or cleargate-planning/ in dev).\n *\n * This seam is mandatory per FLASHCARD #tsup #bundle #import-meta — the\n * bundle collapses import.meta.url to the bundle file so default resolution\n * must never be relied upon in tests.\n */\n packageRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\nfunction resolveDefaultPackageRoot(): string {\n // In production (dist/cli.js), import.meta.url points to dist/cli.js.\n // MANIFEST.json is copied to dist/ by the build step — so 0 levels up.\n // In dev, import.meta.url is cleargate-cli/src/lib/manifest.ts — 3 levels up\n // to cleargate-cli/, then ../cleargate-planning/ for the fixture source.\n // Rather than guessing, we prefer the dist/ sibling first; fall back to the\n // source-tree dev path.\n const here = new URL('.', import.meta.url).pathname;\n\n // Try: same directory (dist/ scenario)\n const distCandidate = path.join(here, 'MANIFEST.json');\n if (existsSync(distCandidate)) {\n return here;\n }\n\n // Try: 1 level up (also dist/ scenario when emitted as dist/lib/manifest.js)\n const oneLevelUp = path.join(here, '..', 'MANIFEST.json');\n if (existsSync(oneLevelUp)) {\n return path.join(here, '..');\n }\n\n // Dev fallback: from src/lib walk up to repo root, then cleargate-planning/\n // src/lib → src → cleargate-cli → repo-root → cleargate-planning\n const devCandidate = path.join(here, '..', '..', '..', 'cleargate-planning', 'MANIFEST.json');\n if (existsSync(devCandidate)) {\n return path.join(here, '..', '..', '..', 'cleargate-planning');\n }\n\n // Cannot determine — caller will get a clear error from loadPackageManifest\n return here;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Load MANIFEST.json from the installed package root.\n *\n * Uses `opts.packageRoot` (required in tests) or default resolution.\n * Throws a descriptive error when the file is absent rather than a raw ENOENT.\n */\nexport function loadPackageManifest(opts?: LoadPackageManifestOpts): ManifestFile {\n const packageRoot = opts?.packageRoot ?? resolveDefaultPackageRoot();\n const manifestPath = path.join(packageRoot, 'MANIFEST.json');\n\n if (!existsSync(manifestPath)) {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n let raw: string;\n try {\n // Synchronous read — callers treat loadPackageManifest as synchronous (startup-time).\n raw = readFileSync(manifestPath, 'utf-8');\n } catch {\n throw new Error(\n `MANIFEST.json not found at ${manifestPath}; run 'npm run build' to generate it.`\n );\n }\n\n return JSON.parse(raw) as ManifestFile;\n}\n\n/**\n * Load the install-time snapshot from `<projectRoot>/.cleargate/.install-manifest.json`.\n * Returns null if the file does not exist (first install or pre-manifest era).\n */\nexport async function loadInstallSnapshot(projectRoot: string): Promise<ManifestFile | null> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n try {\n const raw = await readFile(snapshotPath, 'utf-8');\n return JSON.parse(raw) as ManifestFile;\n } catch {\n return null;\n }\n}\n\n/**\n * BUG-023: Reverse the pin substitution that `copyPayload` applied to hook scripts.\n *\n * `copyPayload` replaces `__CLEARGATE_VERSION__` with `pinVersion` in HOOK_FILES_WITH_PIN.\n * To compare the installed file's content to the package SHA (which uses the placeholder),\n * we must reverse that substitution before hashing.\n *\n * Exported for unit tests.\n */\nexport function reverseSubstitutePinAware(\n content: Buffer | string,\n pinVersion: string\n): string {\n const text = Buffer.isBuffer(content) ? content.toString('utf-8') : content;\n return text.replaceAll(pinVersion, '__CLEARGATE_VERSION__');\n}\n\n/**\n * Compute the SHA256 of a tracked file in the current working tree.\n * Returns null when the file does not exist on disk.\n *\n * BUG-023: When `opts.pinVersion` is provided and `file.overwrite_policy === 'pin-aware'`,\n * reverse-substitute `pinVersion` → `__CLEARGATE_VERSION__` before hashing.\n * This ensures that a freshly-installed hook file (which has the real version baked in)\n * hashes identically to the package's SHA (computed from the placeholder form).\n */\nexport async function computeCurrentSha(\n file: ManifestEntry,\n projectRoot: string,\n opts?: { pinVersion?: string }\n): Promise<string | null> {\n const filePath = path.join(projectRoot, file.path);\n try {\n const raw = await readFile(filePath);\n if (file.overwrite_policy === 'pin-aware' && opts?.pinVersion) {\n const reversed = reverseSubstitutePinAware(raw, opts.pinVersion);\n return hashNormalized(reversed);\n }\n return hashNormalized(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Classify the drift state of a single tracked file.\n *\n * Decision table (PROP-006 §2.4):\n *\n * | Tier | Result |\n * |---------------|-------------|\n * | user-artifact | untracked |\n *\n * | pkgSha | installSha | currentSha | Result |\n * |--------|------------|------------|-------------------|\n * | any | any | null | untracked |\n * | A | A | A | clean |\n * | A | A | B (≠A) | user-modified |\n * | B (≠A) | A | A | upstream-changed |\n * | all differ pairwise | both-changed |\n */\nexport function classify(\n pkgSha: string | null,\n installSha: string | null,\n currentSha: string | null,\n tier: Tier\n): DriftState {\n // user-artifact short-circuit (EPIC-009 §6 Q8)\n if (tier === 'user-artifact') {\n return 'untracked';\n }\n\n // Missing current file\n if (currentSha === null) {\n return 'untracked';\n }\n\n const installEqualsPackage = installSha === pkgSha;\n const currentEqualsInstall = currentSha === installSha;\n\n if (installEqualsPackage && currentEqualsInstall) {\n // install == current == package\n return 'clean';\n }\n\n if (installEqualsPackage && !currentEqualsInstall) {\n // install == package, current != install => user modified\n return 'user-modified';\n }\n\n if (!installEqualsPackage && currentEqualsInstall) {\n // install == current, package != install => upstream changed\n return 'upstream-changed';\n }\n\n // All three differ pairwise\n return 'both-changed';\n}\n\n/**\n * Options for writeDriftState.\n */\nexport interface WriteDriftStateOpts {\n /**\n * ISO-8601 timestamp to record as `last_refreshed` in the output file.\n * When omitted, the current time is used (new Date().toISOString()).\n * This seam is mandatory for deterministic tests.\n */\n lastRefreshed?: string;\n}\n\n/**\n * Atomically write the drift-state map to `<projectRoot>/.cleargate/.drift-state.json`.\n *\n * The on-disk format is wrapped: `{ last_refreshed: string, drift: DriftMap }`.\n * This allows the daily-throttle logic in `cleargate doctor --check-scaffold`\n * to read the timestamp without re-computing all SHAs.\n *\n * Uses write-temp-then-rename (atomic on POSIX; best-effort on Windows).\n * Ensures parent directory exists before writing.\n *\n * STORY-009-04 extended the signature from `(projectRoot, DriftMap)` to\n * `(projectRoot, DriftMap, opts?)` — callers passing only two args continue to work.\n */\nexport async function writeDriftState(\n projectRoot: string,\n state: DriftMap,\n opts?: WriteDriftStateOpts\n): Promise<void> {\n const cleargatDir = path.join(projectRoot, '.cleargate');\n const finalPath = path.join(cleargatDir, '.drift-state.json');\n const tmpPath = `${finalPath}.tmp`;\n\n const lastRefreshed = opts?.lastRefreshed ?? new Date().toISOString();\n const fileContent: DriftStateFile = { last_refreshed: lastRefreshed, drift: state };\n\n await mkdir(cleargatDir, { recursive: true });\n await writeFile(tmpPath, JSON.stringify(fileContent, null, 2) + '\\n', 'utf-8');\n await rename(tmpPath, finalPath);\n}\n\n/**\n * Read the drift-state file written by `writeDriftState`.\n * Returns null when the file does not exist or is malformed.\n */\nexport async function readDriftState(projectRoot: string): Promise<DriftStateFile | null> {\n const driftPath = path.join(projectRoot, '.cleargate', '.drift-state.json');\n try {\n const raw = await readFile(driftPath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n // Accept both the new wrapped format {last_refreshed, drift} and the old flat format\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'last_refreshed' in parsed &&\n 'drift' in parsed\n ) {\n return parsed as DriftStateFile;\n }\n return null;\n } catch {\n return null;\n }\n}\n","/**\n * sha256.ts — STORY-009-01\n *\n * Deterministic SHA256 hasher with cross-platform content normalization.\n * Node built-ins only: crypto, fs/promises, path.\n */\n\nimport { createHash } from 'node:crypto';\nimport { readFile } from 'node:fs/promises';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Hash normalized content (string or Buffer) — 64 hex chars.\n *\n * Normalization steps applied in order:\n * 1. Convert Buffer to UTF-8 string.\n * 2. Strip leading BOM (U+FEFF).\n * 3. Normalize CRLF → LF.\n * 4. Enforce trailing newline (append `\\n` if missing).\n */\nexport function hashNormalized(content: string | Buffer): string {\n let text: string =\n Buffer.isBuffer(content) ? content.toString('utf-8') : content;\n\n // 1. Strip leading BOM\n if (text.startsWith('\\ufeff')) {\n text = text.slice(1);\n }\n\n // 2. CRLF → LF\n text = text.replace(/\\r\\n/g, '\\n');\n\n // 3. Enforce trailing newline\n if (!text.endsWith('\\n')) {\n text += '\\n';\n }\n\n return createHash('sha256').update(text, 'utf-8').digest('hex');\n}\n\n/**\n * Read a file, normalize its content, and return the SHA256 hex digest.\n */\nexport async function hashFile(filePath: string): Promise<string> {\n const raw = await readFile(filePath);\n return hashNormalized(raw);\n}\n\n/**\n * Return the first 8 hex characters of a full SHA256 digest for human-readable output.\n */\nexport function shortHash(full: string): string {\n return full.slice(0, 8);\n}\n","/**\n * prompts.ts — minimal readline-based prompt helpers\n *\n * STORY-009-03: created here (cited in story §3 as \"existing\"; verified absent).\n * STORY-010-01: added promptEmail for participant identity flow.\n * Used by init.ts restore flow and future commands that need interactive input.\n */\nimport * as readline from 'node:readline';\n\nexport interface PromptOptions {\n /** Override stdin for testing */\n stdin?: NodeJS.ReadableStream;\n /** Override stdout write for testing */\n stdout?: (s: string) => void;\n}\n\n/**\n * Prompt the user with a yes/no question.\n *\n * @param question The question text to display (without [Y/n] suffix — caller includes it)\n * @param defaultYes Whether Enter with no input means yes\n * @param opts Test seams for stdin/stdout\n * @returns true for yes, false for no\n */\nexport async function promptYesNo(\n question: string,\n defaultYes: boolean,\n opts?: PromptOptions,\n): Promise<boolean> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) keeps the cursor inline with the prompt so\n // the user's typed input is visible adjacent to the question — matches every\n // other CLI prompt convention. See BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<boolean>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim().toLowerCase();\n if (trimmed === '') {\n resolve(defaultYes);\n } else if (trimmed === 'y' || trimmed === 'yes') {\n resolve(true);\n } else {\n resolve(false);\n }\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — treat as default\n resolve(defaultYes);\n }\n });\n });\n}\n\n/**\n * Prompt the user for a text value with an optional default.\n *\n * @param question The question text to display\n * @param defaultValue The value used when the user presses Enter without typing\n * @param opts Test seams for stdin/stdout\n * @returns The entered string, or `defaultValue` if Enter is pressed with no input\n */\nexport async function promptEmail(\n question: string,\n defaultValue: string,\n opts?: PromptOptions,\n): Promise<string> {\n const stdoutFn = opts?.stdout ?? ((s: string) => process.stdout.write(s));\n // Trailing space (not newline) — see promptYesNo above and BUG-007.\n stdoutFn(question + ' ');\n\n const inputStream = opts?.stdin ?? process.stdin;\n\n return new Promise<string>((resolve) => {\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined, // we handle output ourselves\n terminal: false,\n });\n\n let answered = false;\n\n rl.once('line', (line: string) => {\n answered = true;\n rl.close();\n const trimmed = line.trim();\n resolve(trimmed === '' ? defaultValue : trimmed);\n });\n\n rl.once('close', () => {\n if (!answered) {\n // EOF without a line — use default\n resolve(defaultValue);\n }\n });\n });\n}\n","/**\n * identity.ts — participant identity resolver.\n *\n * STORY-010-01: resolveIdentity precedence ladder + writeParticipant atomic write.\n *\n * Resolution order (highest-priority first):\n * 1. .cleargate/.participant.json\n * 2. CLEARGATE_USER env var\n * 3. git config user.email (via child_process.spawnSync — NOT simple-git; see flashcard #cli #simple-git #deps)\n * 4. \"{username}@{hostname}\" host fallback\n *\n * All external sources (env, git, host) are injectable as opts for test hermetics.\n * Do NOT reach into process.env / os.hostname() directly in the happy path.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as fsPromises from 'node:fs/promises';\nimport * as os from 'node:os';\nimport { spawnSync } from 'node:child_process';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type IdentitySource = 'participant-json' | 'env' | 'git' | 'host';\n\nexport interface Identity {\n email: string;\n source: IdentitySource;\n}\n\nexport interface ParticipantFile {\n email: string;\n set_at: string;\n source: 'prompted' | 'inferred';\n}\n\n// ── Read participant file ─────────────────────────────────────────────────────\n\n/**\n * Read .cleargate/.participant.json.\n * Returns null on ENOENT or malformed JSON — caller decides what to do.\n */\nexport function readParticipant(projectRoot: string): ParticipantFile | null {\n const filePath = path.join(projectRoot, '.cleargate', '.participant.json');\n try {\n const raw = fs.readFileSync(filePath, 'utf8');\n return JSON.parse(raw) as ParticipantFile;\n } catch {\n return null;\n }\n}\n\n// ── Write participant file ────────────────────────────────────────────────────\n\n/**\n * Write .cleargate/.participant.json atomically (tmp + rename).\n * Creates .cleargate/ directory if it does not exist.\n */\nexport async function writeParticipant(\n projectRoot: string,\n email: string,\n source: 'prompted' | 'inferred',\n now: () => string = () => new Date().toISOString(),\n): Promise<void> {\n const cleargateDir = path.join(projectRoot, '.cleargate');\n await fsPromises.mkdir(cleargateDir, { recursive: true });\n\n const filePath = path.join(cleargateDir, '.participant.json');\n const tmpPath = filePath + '.tmp.' + Date.now();\n\n const content: ParticipantFile = {\n email,\n set_at: now(),\n source,\n };\n\n await fsPromises.writeFile(tmpPath, JSON.stringify(content, null, 2) + '\\n', 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Resolve identity ──────────────────────────────────────────────────────────\n\nexport interface ResolveIdentityOpts {\n /** Override process.env for test hermetics */\n env?: NodeJS.ProcessEnv;\n /** Override git config user.email resolution. Return null when git unavailable. */\n gitEmail?: () => string | null;\n /** Override os.hostname() */\n hostname?: () => string;\n /** Override os.userInfo().username */\n username?: () => string;\n /** Override new Date().toISOString() (unused here; reserved for callers) */\n now?: () => string;\n}\n\n/**\n * Resolve caller identity via precedence ladder.\n * All external lookups go through opts test seams — never reach process.env\n * or os.hostname() directly so tests can assert precedence without env pollution.\n */\nexport function resolveIdentity(\n projectRoot: string,\n opts: ResolveIdentityOpts = {},\n): Identity {\n // 1. participant-json\n const participant = readParticipant(projectRoot);\n if (participant !== null && participant.email) {\n return { email: participant.email, source: 'participant-json' };\n }\n\n // 2. env\n const envValue = (opts.env ?? process.env)['CLEARGATE_USER'];\n if (envValue && envValue.trim()) {\n return { email: envValue.trim(), source: 'env' };\n }\n\n // 3. git\n const gitEmailFn =\n opts.gitEmail ??\n (() => {\n const result = spawnSync('git', ['config', 'user.email'], {\n encoding: 'utf8',\n timeout: 3000,\n });\n if (result.status === 0 && result.stdout) {\n const trimmed = result.stdout.trim();\n if (trimmed) return trimmed;\n }\n return null;\n });\n\n const gitEmail = gitEmailFn();\n if (gitEmail && gitEmail.trim()) {\n return { email: gitEmail.trim(), source: 'git' };\n }\n\n // 4. host fallback\n const hostnameFn = opts.hostname ?? (() => os.hostname());\n const usernameFn =\n opts.username ??\n (() => {\n try {\n return os.userInfo().username;\n } catch {\n return 'user';\n }\n });\n\n const hostname = hostnameFn();\n const username = usernameFn();\n return { email: `${username}@${hostname}`, source: 'host' };\n}\n","/**\n * scaffold-source.ts — STORY-016-05\n *\n * Resolves the scaffold payload directory for `cleargate init`.\n * When `--from-source <path>` is provided, validates the path and returns it\n * as the payload directory (meta-repo dogfood mode).\n * When absent, delegates to the existing npm-package-resolved path.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { resolveDefaultPayloadDir } from '../commands/init.js';\n\nexport interface ResolveOptions {\n /** When set, resolve scaffold from this local directory instead of the npm package. */\n fromSource?: string;\n /** Current working directory used to resolve relative paths. Default: process.cwd() */\n cwd?: string;\n}\n\nexport interface ResolveResult {\n /** Absolute path to the scaffold root (the directory containing .claude/, .cleargate/, CLAUDE.md). */\n payloadDir: string;\n /** Source of the payload directory. */\n source: 'npm-package' | 'from-source';\n}\n\n/** Required items at the root of a valid scaffold source directory. */\nconst REQUIRED_ITEMS = ['.claude', '.cleargate', 'CLAUDE.md'] as const;\n\n/**\n * Error thrown when a `--from-source` path is invalid.\n */\nexport class ScaffoldSourceError extends Error {\n code: 'PATH_MISSING' | 'LAYOUT_INVALID';\n missing: string[];\n\n constructor(opts: {\n message: string;\n code: 'PATH_MISSING' | 'LAYOUT_INVALID';\n missing: string[];\n }) {\n super(opts.message);\n this.name = 'ScaffoldSourceError';\n this.code = opts.code;\n this.missing = opts.missing;\n }\n}\n\n/**\n * Resolve the scaffold root directory.\n *\n * When `fromSource` is provided:\n * - Resolve the path relative to `cwd` (or process.cwd()).\n * - Validate that the resolved path exists and contains `.claude/`, `.cleargate/`, and `CLAUDE.md`.\n * - Throw `ScaffoldSourceError` on any validation failure.\n *\n * When `fromSource` is absent:\n * - Return the npm-package-resolved payload dir via `resolveDefaultPayloadDir()`.\n * - Behavior is bug-for-bug identical to pre-STORY-016-05 init.ts line 209.\n */\nexport function resolveScaffoldRoot(opts?: ResolveOptions): ResolveResult {\n const fromSource = opts?.fromSource;\n\n if (!fromSource) {\n return {\n payloadDir: resolveDefaultPayloadDir(),\n source: 'npm-package',\n };\n }\n\n // Resolve relative to cwd\n const resolvedCwd = opts?.cwd ?? process.cwd();\n const absolutePath = path.resolve(resolvedCwd, fromSource);\n\n // Validate existence\n if (!fs.existsSync(absolutePath)) {\n throw new ScaffoldSourceError({\n message:\n `cleargate init: --from-source path missing required scaffold layout\\n` +\n ` Path does not exist: ${absolutePath}`,\n code: 'PATH_MISSING',\n missing: [absolutePath],\n });\n }\n\n // Validate required layout items\n const missingItems: string[] = [];\n for (const item of REQUIRED_ITEMS) {\n const itemPath = path.join(absolutePath, item);\n if (!fs.existsSync(itemPath)) {\n missingItems.push(item);\n }\n }\n\n if (missingItems.length > 0) {\n throw new ScaffoldSourceError({\n message:\n `cleargate init: --from-source path missing required scaffold layout\\n` +\n ` Path: ${absolutePath}\\n` +\n ` Missing: ${missingItems.join(', ')}`,\n code: 'LAYOUT_INVALID',\n missing: missingItems,\n });\n }\n\n return {\n payloadDir: absolutePath,\n source: 'from-source',\n };\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { deriveBucket } from '../wiki/derive-bucket.js';\nimport { deriveRepo } from '../wiki/derive-repo.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { serializePage, parsePage, type WikiPage } from '../wiki/page-schema.js';\nimport { compile as compileActiveSprint } from '../wiki/synthesis/active-sprint.js';\nimport { compile as compileOpenGates } from '../wiki/synthesis/open-gates.js';\nimport { compile as compileProductState } from '../wiki/synthesis/product-state.js';\nimport { compile as compileRoadmap } from '../wiki/synthesis/roadmap.js';\nimport { scanRawItems, type RawItem } from '../wiki/scan.js';\n\n// ─── Phase 4 helpers — imported from lib/wiki/contradict.ts (STORY-020-03) ───\n// Both wikiIngestHandler and wikiContradictHandler share a single implementation.\n// Re-export so existing tests (contradict.test.ts) can still import from this file.\nexport {\n preparePhase4,\n commitPhase4Findings,\n getBucketFromId as _getBucketFromId,\n type ContradictFinding,\n type Phase4Skip,\n type Phase4Ready,\n type Phase4Result,\n type PreparePhase4Opts,\n type CommitPhase4Opts,\n} from '../lib/wiki/contradict.js';\nimport {\n preparePhase4,\n commitPhase4Findings,\n getBucketFromId,\n type ContradictFinding,\n} from '../lib/wiki/contradict.js';\n\nexport interface WikiIngestOptions {\n /** Absolute path to the raw delivery file to ingest */\n rawPath: string;\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /** Test seam: replaces fs.rename (for atomic index.md write test) */\n rename?: (src: string, dst: string) => void;\n /** Test seam: override directory for synthesis templates (default resolved via import.meta.url) */\n templateDir?: string;\n /**\n * Test seam: stub for Phase 4 subagent invocation.\n * When provided, replaces the stdout signal (agent-workflow invocation) with a\n * direct call — returning findings synchronously. Used in unit tests only.\n * In production, Phase 4 emits a `phase4:` JSON line; the calling agent reads it\n * and spawns the contradict subagent via Task, then calls commitPhase4Findings.\n */\n phase4SubagentStub?: (draftWikiPath: string, neighborhood: string[]) => ContradictFinding[];\n}\n\n/** Directories under .cleargate/ that are excluded from ingest per §10.3. */\nconst EXCLUDED_SUFFIXES = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\nconst BUCKET_ORDER = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'] as const;\nconst BUCKET_LABELS: Record<string, string> = {\n epics: 'Epics',\n stories: 'Stories',\n sprints: 'Sprints',\n proposals: 'Proposals',\n crs: 'CRs',\n bugs: 'Bugs',\n topics: 'Topics',\n};\n\nexport async function wikiIngestHandler(opts: WikiIngestOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const rename = opts.rename ?? fs.renameSync;\n const templateDir = opts.templateDir;\n\n const rawPath = opts.rawPath;\n\n // Resolve rawPath: if relative, resolve against cwd\n const absRawPath = path.isAbsolute(rawPath) ? rawPath : path.resolve(cwd, rawPath);\n // Compute relative path from cwd (repo root)\n const relRawPath = path.relative(cwd, absRawPath).replace(/\\\\/g, '/');\n\n // Step 1: Validate path resolves under <cwd>/.cleargate/delivery/\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n const deliveryRootNorm = deliveryRoot.replace(/\\\\/g, '/');\n const absDeliveryRoot = deliveryRoot;\n\n // Check: absRawPath must be under absDeliveryRoot\n const relToDelivery = path.relative(absDeliveryRoot, absRawPath);\n if (relToDelivery.startsWith('..') || path.isAbsolute(relToDelivery)) {\n stderr(`wiki ingest: ${rawPath} not under .cleargate/delivery/\\n`);\n exit(2);\n return;\n }\n\n void deliveryRootNorm; // suppress lint warning\n\n // Step 2: Exclusion check (defense-in-depth)\n const isExcluded = EXCLUDED_SUFFIXES.some((excl) => relRawPath.startsWith(excl));\n if (isExcluded) {\n stdout(`wiki ingest: ${rawPath} excluded (skip)\\n`);\n exit(0);\n return;\n }\n\n // Step 3: Derive bucket + id + type + repo\n const filename = path.basename(absRawPath);\n let bucketInfo: ReturnType<typeof deriveBucket>;\n try {\n bucketInfo = deriveBucket(filename);\n } catch (e) {\n stderr(`wiki ingest: cannot determine bucket for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let repo: ReturnType<typeof deriveRepo>;\n try {\n repo = deriveRepo(relRawPath);\n } catch (e) {\n stderr(`wiki ingest: cannot derive repo for ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n const { type, id, bucket } = bucketInfo;\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const pageDir = path.join(wikiRoot, bucket);\n const pagePath = path.join(pageDir, `${id}.md`);\n\n // Read and parse the raw file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absRawPath, 'utf8');\n } catch (e) {\n stderr(`wiki ingest: cannot read ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch (e) {\n stderr(`wiki ingest: malformed frontmatter in ${rawPath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // Step 4: Idempotency guard (A2)\n const currentSha = getGitSha(absRawPath, gitRunner) ?? '';\n const pageExists = fs.existsSync(pagePath);\n\n if (pageExists && currentSha !== '') {\n let isNoOp = false;\n try {\n const existingPageContent = fs.readFileSync(pagePath, 'utf8');\n const existingPage = parsePage(existingPageContent);\n\n if (existingPage.last_ingest_commit === currentSha) {\n // Check if raw file content matches what git shows for that SHA\n const contentUnchanged = checkContentUnchanged(absRawPath, currentSha, relRawPath, gitRunner);\n if (contentUnchanged) {\n isNoOp = true;\n }\n }\n } catch {\n // If we can't parse the existing page, proceed with ingest\n }\n\n if (isNoOp) {\n stdout(`wiki ingest: ${id} unchanged (no-op)\\n`);\n exit(0);\n return;\n }\n }\n\n // Determine action\n const action = pageExists ? 'update' : 'create';\n\n // Preserve last_contradict_sha from the existing wiki page (Phase 4 stamps it there;\n // we must carry it forward when re-writing the page so idempotency survives re-ingest).\n let existingLastContradictSha: string | undefined;\n if (pageExists) {\n try {\n const existingPageContent = fs.readFileSync(pagePath, 'utf8');\n const existingPage = parsePage(existingPageContent);\n existingLastContradictSha = existingPage.last_contradict_sha;\n } catch {\n // If parse fails, leave undefined\n }\n }\n\n // Step 5: Build new WikiPage and write it\n const parent = buildParentRef(fm);\n const children = buildChildrenRefs(fm);\n const timestamp = now();\n\n const wikiPage: WikiPage = {\n type,\n id,\n parent,\n children,\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: relRawPath,\n last_ingest: timestamp,\n last_ingest_commit: currentSha,\n repo,\n // Carry forward last_contradict_sha so Phase 4 idempotency survives re-ingest\n ...(existingLastContradictSha !== undefined ? { last_contradict_sha: existingLastContradictSha } : {}),\n // Hierarchy keys (§11.7): read from raw fm — stamped at raw-side, not wiki-side\n ...(typeof fm['parent_cleargate_id'] === 'string' ? { parent_cleargate_id: fm['parent_cleargate_id'] } : {}),\n ...(typeof fm['sprint_cleargate_id'] === 'string' ? { sprint_cleargate_id: fm['sprint_cleargate_id'] } : {}),\n };\n\n const pageBody = buildPageBody({ id, fm, body });\n const pageContent = serializePage(wikiPage, pageBody);\n\n fs.mkdirSync(pageDir, { recursive: true });\n fs.writeFileSync(pagePath, pageContent, 'utf8');\n\n // Step 6: Append one log entry to wiki/log.md\n appendLogEntry(wikiRoot, { timestamp, action, id, relRawPath });\n\n // Step 7: Update wiki/index.md (atomic write-temp-then-rename)\n updateIndex(wikiRoot, { id, type, status: wikiPage.status, relRawPath, rename });\n\n // Step 8: Recompile affected synthesis pages (all four — M3 over-recompiles)\n recompileSynthesis(wikiRoot, cwd, templateDir);\n\n // Step 9: Phase 4 — Contradiction Check (advisory, never exits non-zero)\n const phase4Result = preparePhase4({\n absRawPath,\n relRawPath,\n wikiRoot,\n id,\n fm,\n body,\n currentSha,\n gitRunner,\n });\n\n if (!phase4Result.skip) {\n const phase4SeamStub = opts.phase4SubagentStub;\n if (phase4SeamStub) {\n // Test seam: synchronously call the stub instead of emitting a signal\n const findings = phase4SeamStub(phase4Result.draftWikiPath, phase4Result.neighborhood);\n commitPhase4Findings({\n absRawPath,\n wikiRoot,\n findings,\n ingestSha: phase4Result.ingestSha,\n truncated: phase4Result.truncated,\n draftId: phase4Result.draftId,\n now,\n });\n } else {\n // Production path: emit a signal line for the calling agent to act on.\n // The agent (cleargate-wiki-ingest) reads this JSON, spawns cleargate-wiki-contradict\n // via Task, and calls `cleargate wiki contradict-commit <rawPath> < findings.json`.\n stdout(`phase4: ${JSON.stringify({\n draftId: phase4Result.draftId,\n draftWikiPath: phase4Result.draftWikiPath,\n neighborhood: phase4Result.neighborhood,\n truncated: phase4Result.truncated,\n ingestSha: phase4Result.ingestSha,\n prompt: phase4Result.prompt,\n })}\\n`);\n }\n }\n\n // Step 10: Print result\n stdout(`wiki ingest: ${action} ${bucket}/${id}.md\\n`);\n}\n\nfunction checkContentUnchanged(\n absRawPath: string,\n sha: string,\n relRawPath: string,\n gitRunner?: GitRunner,\n): boolean {\n try {\n const run = gitRunner ?? defaultGitRunner;\n // git show <sha>:<relRawPath> returns file content at that commit\n const gitContent = run('git', ['show', `${sha}:${relRawPath}`]);\n // Non-zero exit is handled by runner returning empty string (defaultRunner returns stdout)\n // If empty string returned from show when sha is valid, treat as changed\n if (!gitContent && gitContent !== '') return false;\n const currentContent = fs.readFileSync(absRawPath, 'utf8');\n return gitContent === currentContent;\n } catch {\n return false;\n }\n}\n\nfunction defaultGitRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n if (result.status !== 0) return '\\0__NONZERO__'; // sentinel for non-zero exit\n return result.stdout ?? '';\n}\n\nfunction buildParentRef(fm: Record<string, unknown>): string {\n const raw = fm['parent_epic_ref'] ?? fm['parent'] ?? '';\n const s = String(raw);\n if (!s) return '';\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n}\n\nfunction buildChildrenRefs(fm: Record<string, unknown>): string[] {\n const raw = fm['children'];\n if (!raw) return [];\n const arr = Array.isArray(raw) ? raw : [raw];\n return arr.map((c) => {\n const s = String(c);\n if (s.startsWith('[[') && s.endsWith(']]')) return s;\n return `[[${s}]]`;\n });\n}\n\nfunction buildPageBody(item: { id: string; fm: Record<string, unknown>; body: string }): string {\n const title = String(item.fm['title'] ?? item.id);\n const summary = String(\n item.fm['description'] ?? item.body.split('\\n')[0] ?? 'No summary available.',\n ).slice(0, 200);\n\n const parent = buildParentRef(item.fm);\n const children = buildChildrenRefs(item.fm);\n const blastParts: string[] = [];\n if (parent) blastParts.push(parent);\n for (const child of children) blastParts.push(child);\n const blastLine = blastParts.length > 0 ? blastParts.join(', ') : 'None.';\n\n return [\n `# ${item.id}: ${title}`,\n '',\n summary,\n '',\n '## Blast radius',\n `Affects: ${blastLine}`,\n '',\n '## Open questions',\n 'None.',\n '',\n ].join('\\n');\n}\n\nfunction appendLogEntry(\n wikiRoot: string,\n entry: { timestamp: string; action: string; id: string; relRawPath: string },\n): void {\n const logPath = path.join(wikiRoot, 'log.md');\n const logEntry = [\n `- timestamp: \"${entry.timestamp}\"`,\n ` actor: \"cleargate wiki ingest\"`,\n ` action: \"${entry.action}\"`,\n ` target: \"${entry.id}\"`,\n ` path: \"${entry.relRawPath}\"`,\n ].join('\\n');\n\n if (fs.existsSync(logPath)) {\n const existing = fs.readFileSync(logPath, 'utf8');\n // Append to existing log\n const newContent = existing.trimEnd() + '\\n' + logEntry + '\\n';\n fs.writeFileSync(logPath, newContent, 'utf8');\n } else {\n fs.mkdirSync(wikiRoot, { recursive: true });\n fs.writeFileSync(logPath, `# Wiki Event Log\\n\\n${logEntry}\\n`, 'utf8');\n }\n}\n\nfunction updateIndex(\n wikiRoot: string,\n opts: {\n id: string;\n type: string;\n status: string;\n relRawPath: string;\n rename: (src: string, dst: string) => void;\n },\n): void {\n const indexPath = path.join(wikiRoot, 'index.md');\n const tmpPath = `${indexPath}.tmp`;\n\n const newRow = `| [[${opts.id}]] | ${opts.type} | ${opts.status} | ${opts.relRawPath} |`;\n\n let content: string;\n if (fs.existsSync(indexPath)) {\n content = fs.readFileSync(indexPath, 'utf8');\n // Check if a row with this id already exists; if so, replace it\n const idPattern = `[[${opts.id}]]`;\n const lines = content.split('\\n');\n let replaced = false;\n const newLines = lines.map((line) => {\n if (line.includes(idPattern) && line.startsWith('|')) {\n replaced = true;\n return newRow;\n }\n return line;\n });\n\n if (replaced) {\n content = newLines.join('\\n');\n } else {\n // Insert into the correct bucket section\n content = insertIntoSection(content, opts.id, newRow);\n }\n } else {\n // Build a minimal index.md with the item\n content = buildMinimalIndex(opts.id, opts.type, opts.status, opts.relRawPath);\n }\n\n fs.writeFileSync(tmpPath, content, 'utf8');\n opts.rename(tmpPath, indexPath);\n}\n\nfunction insertIntoSection(content: string, id: string, newRow: string): string {\n // Determine which bucket section to insert into\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n const sectionHeader = `## ${label}`;\n\n const lines = content.split('\\n');\n let sectionStart = -1;\n let nextSectionStart = -1;\n\n for (let i = 0; i < lines.length; i++) {\n if (lines[i] === sectionHeader) {\n sectionStart = i;\n // Find next section\n for (let j = i + 1; j < lines.length; j++) {\n if (lines[j].startsWith('## ')) {\n nextSectionStart = j;\n break;\n }\n }\n break;\n }\n }\n\n if (sectionStart === -1) {\n // Section doesn't exist — append new section at end\n const sectionContent = [\n '',\n sectionHeader,\n '',\n newRow,\n '',\n ].join('\\n');\n return content.trimEnd() + sectionContent;\n }\n\n // Find insertion point: after any existing rows in this section, sorted by id\n const sectionEnd = nextSectionStart === -1 ? lines.length : nextSectionStart;\n const sectionLines = lines.slice(sectionStart + 1, sectionEnd);\n\n // Find existing row entries and insert in sorted order\n let insertAt = -1;\n for (let i = 0; i < sectionLines.length; i++) {\n const line = sectionLines[i];\n if (line.startsWith('|') && !line.startsWith('|---|')) {\n // Extract the id from the row: | [[ID]] | ...\n const match = /\\|\\s*\\[\\[([^\\]]+)\\]\\]/.exec(line);\n if (match) {\n const rowId = match[1];\n if (id.localeCompare(rowId) <= 0) {\n insertAt = sectionStart + 1 + i;\n break;\n }\n }\n }\n }\n\n if (insertAt === -1) {\n // Append before the next section or at end of section\n // Find last row in section\n let lastRowIdx = sectionStart + 1;\n for (let i = sectionStart + 1; i < sectionEnd; i++) {\n if (lines[i].startsWith('|')) {\n lastRowIdx = i + 1;\n }\n }\n lines.splice(lastRowIdx, 0, newRow);\n } else {\n lines.splice(insertAt, 0, newRow);\n }\n\n return lines.join('\\n');\n}\n\nfunction buildMinimalIndex(id: string, type: string, status: string, relRawPath: string): string {\n const bucket = getBucketFromId(id);\n const label = BUCKET_LABELS[bucket] ?? bucket;\n\n const lines: string[] = [\n '# Wiki Index',\n '',\n '> Auto-generated by `cleargate wiki build`. Do not edit manually.',\n '',\n '| ID | Type | Status | Raw Path |',\n '|---|---|---|---|',\n ];\n\n for (const b of BUCKET_ORDER) {\n if (b === 'topics') continue;\n lines.push('', `## ${BUCKET_LABELS[b]}`, '');\n if (b === bucket) {\n lines.push(`| [[${id}]] | ${type} | ${status} | ${relRawPath} |`);\n } else {\n lines.push('_No items._');\n }\n }\n lines.push('');\n\n void label; // suppress\n return lines.join('\\n');\n}\n\nfunction recompileSynthesis(wikiRoot: string, cwd: string, templateDir?: string): void {\n // Recompile all four synthesis pages\n // Gather current state from wiki pages to pass to recipes\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n let items: RawItem[] = [];\n if (fs.existsSync(deliveryRoot)) {\n try {\n items = scanRawItems(deliveryRoot, cwd);\n } catch {\n // If scan fails, pass empty state — synthesis pages will reflect empty\n }\n }\n\n fs.writeFileSync(path.join(wikiRoot, 'active-sprint.md'), compileActiveSprint(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'open-gates.md'), compileOpenGates(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'product-state.md'), compileProductState(items, templateDir), 'utf8');\n fs.writeFileSync(path.join(wikiRoot, 'roadmap.md'), compileRoadmap(items, templateDir), 'utf8');\n}\n","/**\n * STORY-020-03: Shared Phase 4 — Contradiction Check helpers.\n *\n * Extracted from cleargate-cli/src/commands/wiki-ingest.ts so that both:\n * - wikiIngestHandler (STORY-020-02) — calls preparePhase4 + commitPhase4Findings\n * - wikiContradictHandler (STORY-020-03) — calls the same helpers for on-demand check\n * share a single implementation. No duplicate Phase 4 logic (DoD §4.2).\n *\n * The `runPhase4` function is the primary public surface used by the CLI handler.\n * `preparePhase4` and `commitPhase4Findings` remain exported for unit tests and for\n * the ingest handler which calls them in two separate steps.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from '../../wiki/parse-frontmatter.js';\nimport { parsePage } from '../../wiki/page-schema.js';\nimport type { GitRunner } from '../../wiki/git-sha.js';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\n/** Statuses for which Phase 4 (contradiction check) should run. */\nexport const PHASE4_TRIGGER_STATUSES = new Set(['Draft', 'In Review']);\n\n/** A single contradiction finding from the contradict subagent. */\nexport interface ContradictFinding {\n draft: string;\n neighbor: string;\n claim: string;\n}\n\n/** Returned by preparePhase4 when Phase 4 should be skipped. */\nexport interface Phase4Skip {\n skip: true;\n reason: string;\n}\n\n/** Returned by preparePhase4 when Phase 4 should proceed. */\nexport interface Phase4Ready {\n skip: false;\n /** ID of the draft item being ingested. */\n draftId: string;\n /** Absolute path to the just-written wiki page for the draft. */\n draftWikiPath: string;\n /** Neighborhood page paths (wiki pages, relative to wiki root). */\n neighborhood: string[];\n /** Whether neighborhood was truncated to 12. */\n truncated: boolean;\n /** Git SHA of the raw file at this moment. */\n ingestSha: string;\n /** Prompt string to pass to the contradict subagent. */\n prompt: string;\n}\n\nexport type Phase4Result = Phase4Skip | Phase4Ready;\n\n// ─── Phase 4: preparePhase4 ──────────────────────────────────────────────────\n\nexport interface PreparePhase4Opts {\n absRawPath: string;\n relRawPath: string;\n wikiRoot: string;\n id: string;\n fm: Record<string, unknown>;\n body: string;\n currentSha: string;\n gitRunner?: GitRunner;\n}\n\n/**\n * Deterministic Phase 4 prep (no LLM).\n *\n * Returns `{ skip: true, reason }` when Phase 4 should not run (status filter\n * or SHA idempotency). Returns `{ skip: false, ... }` with the neighborhood\n * and prompt when Phase 4 should proceed.\n *\n * Exported for unit tests and for the `cleargate wiki contradict` subcommand (STORY-020-03).\n */\nexport function preparePhase4(opts: PreparePhase4Opts): Phase4Result {\n const { relRawPath, wikiRoot, id, fm, body, currentSha } = opts;\n\n // 1. Status filter — only Draft / In Review\n const rawStatus = String(fm['status'] ?? '');\n if (!PHASE4_TRIGGER_STATUSES.has(rawStatus)) {\n return { skip: true, reason: `status=${rawStatus}` };\n }\n\n // 2. SHA idempotency — check existing wiki page's last_contradict_sha\n const bucket = getBucketFromId(id);\n const pagePath = path.join(wikiRoot, bucket, `${id}.md`);\n if (fs.existsSync(pagePath)) {\n try {\n const existingContent = fs.readFileSync(pagePath, 'utf8');\n const existingPage = parsePage(existingContent);\n if (existingPage.last_contradict_sha && existingPage.last_contradict_sha === currentSha) {\n return { skip: true, reason: `sha-idempotent sha=${currentSha.slice(0, 8)}` };\n }\n } catch {\n // If parse fails, proceed with Phase 4\n }\n }\n\n // 3. Build neighborhood (deterministic)\n const neighborhood = collectNeighborhood({ fm, body, wikiRoot });\n const truncated = neighborhood.length > 12;\n const trimmedNeighborhood = truncated ? neighborhood.slice(0, 12) : neighborhood;\n\n // 4. Build prompt for the contradict subagent\n const draftWikiPath = pagePath;\n const prompt = buildContradictPrompt({ id, draftWikiPath, neighborhood: trimmedNeighborhood, relRawPath });\n\n return {\n skip: false,\n draftId: id,\n draftWikiPath,\n neighborhood: trimmedNeighborhood,\n truncated,\n ingestSha: currentSha,\n prompt,\n };\n}\n\n// ─── Phase 4: commitPhase4Findings ───────────────────────────────────────────\n\nexport interface CommitPhase4Opts {\n /** Absolute path to the raw delivery file (for raw frontmatter stamp). */\n absRawPath: string;\n /** Absolute path to wiki root. */\n wikiRoot: string;\n /** Draft item ID (used to locate wiki page for idempotency stamp). */\n draftId: string;\n /** Findings from the contradict subagent. */\n findings: ContradictFinding[];\n /** Git SHA used for idempotency stamping. */\n ingestSha: string;\n /** Whether neighborhood was truncated. */\n truncated: boolean;\n /** Frozen timestamp generator. */\n now: () => string;\n}\n\n/**\n * Commit Phase 4 findings: append to contradictions.md and stamp last_contradict_sha.\n *\n * Stamps `last_contradict_sha` on BOTH:\n * 1. The wiki page (`.cleargate/wiki/<bucket>/<id>.md`) — used by `preparePhase4` for\n * idempotency on the next ingest run.\n * 2. The raw delivery file — informational, mirrors the wiki page stamp.\n *\n * Always exits cleanly — advisory only.\n *\n * Exported for unit tests and for the `cleargate wiki contradict` subcommand (STORY-020-03).\n */\nexport function commitPhase4Findings(opts: CommitPhase4Opts): void {\n const { absRawPath, wikiRoot, findings, ingestSha, truncated, draftId, now } = opts;\n\n const contradictionsPath = path.join(wikiRoot, 'contradictions.md');\n\n // Append findings to contradictions.md\n if (findings.length > 0) {\n const entries = findings.map((f) =>\n [\n `- draft: \"[[${f.draft || draftId}]]\"`,\n ` neighbor: \"[[${f.neighbor}]]\"`,\n ` claim: ${JSON.stringify(f.claim)}`,\n ` ingest_sha: \"${ingestSha}\"`,\n ` truncated: ${truncated}`,\n ` label: null`,\n ].join('\\n'),\n );\n\n if (fs.existsSync(contradictionsPath)) {\n const existing = fs.readFileSync(contradictionsPath, 'utf8');\n // Append entries after existing findings: block (trimEnd + newline + entries)\n const newContent = existing.trimEnd() + '\\n' + entries.join('\\n') + '\\n';\n fs.writeFileSync(contradictionsPath, newContent, 'utf8');\n } else {\n // Create skeleton + entries\n fs.mkdirSync(wikiRoot, { recursive: true });\n const skeleton = buildContradictionsSkeletonWithFindings(now(), entries);\n fs.writeFileSync(contradictionsPath, skeleton, 'utf8');\n }\n } else {\n // No findings — ensure contradictions.md exists (create skeleton if not)\n if (!fs.existsSync(contradictionsPath)) {\n fs.mkdirSync(wikiRoot, { recursive: true });\n fs.writeFileSync(contradictionsPath, buildContradictionsSkeleton(now()), 'utf8');\n }\n }\n\n // Stamp last_contradict_sha on the WIKI PAGE (primary — used by preparePhase4 idempotency)\n const wikiPageBucket = getBucketFromId(draftId);\n const wikiPagePath = path.join(wikiRoot, wikiPageBucket, `${draftId}.md`);\n stampContradictSha(wikiPagePath, ingestSha);\n\n // Also stamp on the raw file (informational mirror)\n stampContradictSha(absRawPath, ingestSha);\n}\n\n// ─── runPhase4: convenience wrapper for the CLI subcommand ───────────────────\n\nexport interface RunPhase4Opts {\n /** Absolute path to the raw delivery file. */\n absRawPath: string;\n /** Relative path from repo root (used in prompt and stamp). */\n relRawPath: string;\n /** Absolute path to wiki root. */\n wikiRoot: string;\n /** Item ID (derived from filename). */\n id: string;\n /** Frontmatter fields parsed from the raw file. */\n fm: Record<string, unknown>;\n /** Body text from the raw file (used for [[ID]] extraction). */\n body: string;\n /** Git SHA of the raw file at this moment. */\n currentSha: string;\n /** Whether to skip log append and SHA stamp (dry-run mode). */\n dryRun: boolean;\n /** Frozen timestamp generator. */\n now: () => string;\n /** Optional git runner test seam. */\n gitRunner?: GitRunner;\n /**\n * Test seam: stub for Phase 4 subagent invocation.\n * In production, runPhase4 emits a `phase4:` JSON line and returns without findings.\n * When provided, the stub is called synchronously and its findings are committed.\n */\n phase4SubagentStub?: (draftWikiPath: string, neighborhood: string[]) => ContradictFinding[];\n}\n\nexport interface RunPhase4Result {\n /** Whether Phase 4 was skipped. */\n skipped: boolean;\n /** Skip reason (status or sha-idempotent). Present when skipped=true. */\n reason?: string;\n /**\n * Findings committed (only populated when phase4SubagentStub is provided).\n * In production (no stub), findings come from the agent workflow.\n */\n findings: ContradictFinding[];\n /**\n * JSON signal emitted to stdout in production mode (no stub).\n * The calling agent reads this and invokes cleargate-wiki-contradict via Task.\n */\n phase4Signal?: string;\n}\n\n/**\n * Convenience wrapper that combines preparePhase4 + commitPhase4Findings.\n *\n * Used by wikiContradictHandler. wikiIngestHandler calls the two steps separately\n * so it can interleave its own stdout between them.\n *\n * Production path (no stub): returns the phase4Signal JSON string for the\n * calling agent to act on (Mode A — the agent spawns Task, not Node code).\n * Dry-run path: returns findings (or empty) without mutating any state.\n */\nexport function runPhase4(opts: RunPhase4Opts): RunPhase4Result {\n const result = preparePhase4({\n absRawPath: opts.absRawPath,\n relRawPath: opts.relRawPath,\n wikiRoot: opts.wikiRoot,\n id: opts.id,\n fm: opts.fm,\n body: opts.body,\n currentSha: opts.currentSha,\n gitRunner: opts.gitRunner,\n });\n\n if (result.skip) {\n return { skipped: true, reason: result.reason, findings: [] };\n }\n\n const stub = opts.phase4SubagentStub;\n\n if (stub) {\n // Test seam: synchronously call the stub and (optionally) commit findings\n const findings = stub(result.draftWikiPath, result.neighborhood);\n\n if (!opts.dryRun) {\n commitPhase4Findings({\n absRawPath: opts.absRawPath,\n wikiRoot: opts.wikiRoot,\n findings,\n ingestSha: result.ingestSha,\n truncated: result.truncated,\n draftId: result.draftId,\n now: opts.now,\n });\n }\n\n return { skipped: false, findings };\n }\n\n // Production path: emit a signal line for the calling agent (Mode A).\n // The agent (cleargate-wiki-ingest or orchestrator) reads this JSON, spawns\n // cleargate-wiki-contradict via Task, then calls `cleargate wiki contradict commit`.\n // Node code cannot invoke the Task tool directly — no Node-side Task API.\n const phase4Signal = JSON.stringify({\n draftId: result.draftId,\n draftWikiPath: result.draftWikiPath,\n neighborhood: result.neighborhood,\n truncated: result.truncated,\n ingestSha: result.ingestSha,\n prompt: result.prompt,\n });\n\n return { skipped: false, findings: [], phase4Signal };\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/** Resolve which wiki bucket an ID belongs to. */\nexport function getBucketFromId(id: string): string {\n if (id.startsWith('EPIC-')) return 'epics';\n if (id.startsWith('STORY-')) return 'stories';\n if (id.startsWith('SPRINT-')) return 'sprints';\n if (id.startsWith('PROPOSAL-')) return 'proposals';\n if (id.startsWith('CR-')) return 'crs';\n if (id.startsWith('BUG-')) return 'bugs';\n return 'topics';\n}\n\n/** Collect the contradiction-check neighborhood for a draft (§1.2 STORY-020-02). */\nfunction collectNeighborhood(opts: {\n fm: Record<string, unknown>;\n body: string;\n wikiRoot: string;\n}): string[] {\n const { fm, body, wikiRoot } = opts;\n const seen = new Set<string>();\n const result: string[] = [];\n\n function addPath(wikiRelPath: string): void {\n const abs = path.join(wikiRoot, wikiRelPath);\n if (!seen.has(wikiRelPath) && fs.existsSync(abs)) {\n seen.add(wikiRelPath);\n result.push(wikiRelPath);\n }\n }\n\n // Step a: [[ID]] mentions in raw body\n const idMentions = extractIdMentions(body);\n for (const mentionedId of idMentions) {\n const bucket = getBucketFromId(mentionedId);\n addPath(`${bucket}/${mentionedId}.md`);\n }\n\n // Step b: parent page (from frontmatter parent_epic_ref or parent)\n const parentRaw = String(fm['parent_epic_ref'] ?? fm['parent'] ?? '');\n const parentId = parentRaw.replace(/^\\[\\[|\\]\\]$/g, '').trim();\n if (parentId) {\n const parentBucket = getBucketFromId(parentId);\n addPath(`${parentBucket}/${parentId}.md`);\n\n // Step c: siblings (other children of the parent epic)\n const parentPagePath = path.join(wikiRoot, parentBucket, `${parentId}.md`);\n if (fs.existsSync(parentPagePath)) {\n try {\n const parentContent = fs.readFileSync(parentPagePath, 'utf8');\n const parentPage = parsePage(parentContent);\n for (const childRef of parentPage.children) {\n const childId = childRef.replace(/^\\[\\[|\\]\\]$/g, '').trim();\n if (childId) {\n const childBucket = getBucketFromId(childId);\n addPath(`${childBucket}/${childId}.md`);\n }\n }\n } catch {\n // ignore parse errors\n }\n }\n\n // Step d: topic pages that cite this parent\n const topicsDir = path.join(wikiRoot, 'topics');\n if (fs.existsSync(topicsDir)) {\n try {\n const topicFiles = fs.readdirSync(topicsDir).filter((f) => f.endsWith('.md'));\n for (const tf of topicFiles) {\n const topicPath = path.join(topicsDir, tf);\n try {\n const topicContent = fs.readFileSync(topicPath, 'utf8');\n const { fm: topicFm } = parseFrontmatter(topicContent);\n const cites = topicFm['cites'];\n const citesArr = Array.isArray(cites) ? cites : cites ? [cites] : [];\n const citesParent = citesArr.some((c) => {\n const s = String(c).replace(/^\\[\\[|\\]\\]$/g, '').trim();\n return s === parentId;\n });\n if (citesParent) {\n addPath(`topics/${tf}`);\n }\n } catch {\n // ignore\n }\n }\n } catch {\n // ignore\n }\n }\n }\n\n return result;\n}\n\n/** Extract [[ID]] references from raw markdown body text. */\nfunction extractIdMentions(body: string): string[] {\n const re = /\\[\\[([A-Z][A-Z0-9-]+)\\]\\]/g;\n const seen = new Set<string>();\n const results: string[] = [];\n let m: RegExpExecArray | null;\n while ((m = re.exec(body)) !== null) {\n const id = m[1];\n if (!seen.has(id)) {\n seen.add(id);\n results.push(id);\n }\n }\n return results;\n}\n\n/** Build the prompt string for the contradict subagent. */\nfunction buildContradictPrompt(opts: {\n id: string;\n draftWikiPath: string;\n neighborhood: string[];\n relRawPath: string;\n}): string {\n const { id, draftWikiPath, neighborhood, relRawPath } = opts;\n return [\n `Check draft ${id} for contradictions against its neighborhood.`,\n `Draft wiki page: ${draftWikiPath}`,\n `Raw source: ${relRawPath}`,\n `Neighborhood pages (${neighborhood.length}): ${neighborhood.join(', ')}`,\n `For each contradiction found, emit one line: contradiction: ${id} vs <neighbor-id> · <claim-summary (≤80 chars)>`,\n `Follow each finding with one reasoning paragraph. Always exit 0.`,\n ].join('\\n');\n}\n\n/** Build the initial contradictions.md skeleton with findings. */\nfunction buildContradictionsSkeletonWithFindings(timestamp: string, entries: string[]): string {\n return [\n '---',\n `type: \"synthesis\"`,\n `id: \"contradictions\"`,\n `generated_at: \"${timestamp}\"`,\n '---',\n '',\n '# Wiki Contradictions — Advisory Log',\n '',\n '(Append-only. Each entry is one YAML record. Human applies label: true-positive | false-positive | nitpick.)',\n '',\n 'findings:',\n ...entries,\n '',\n ].join('\\n');\n}\n\n/** Build the initial contradictions.md skeleton without findings. */\nfunction buildContradictionsSkeleton(timestamp: string): string {\n return [\n '---',\n `type: \"synthesis\"`,\n `id: \"contradictions\"`,\n `generated_at: \"${timestamp}\"`,\n '---',\n '',\n '# Wiki Contradictions — Advisory Log',\n '',\n '(Append-only. Each entry is one YAML record. Human applies label: true-positive | false-positive | nitpick.)',\n '',\n 'findings: []',\n '',\n ].join('\\n');\n}\n\n/** Stamp last_contradict_sha into a file's frontmatter (wiki page or raw file). */\nfunction stampContradictSha(absFilePath: string, sha: string): void {\n try {\n const raw = fs.readFileSync(absFilePath, 'utf8');\n const stamped = upsertFrontmatterField(raw, 'last_contradict_sha', sha);\n fs.writeFileSync(absFilePath, stamped, 'utf8');\n } catch {\n // Advisory — never throws\n }\n}\n\n/**\n * Upsert a single string field in the first frontmatter block.\n * Reads raw bytes, regex-replaces target line in first --- block.\n * Per FLASHCARD #frontmatter #write-back: do NOT round-trip via parseFrontmatter+re-serialize.\n */\nfunction upsertFrontmatterField(raw: string, key: string, value: string): string {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return raw;\n\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return raw;\n\n // Check if field already exists in frontmatter block\n const keyPrefix = `${key}:`;\n let found = false;\n for (let i = 1; i < closeIdx; i++) {\n if (lines[i].startsWith(keyPrefix)) {\n lines[i] = `${key}: \"${value}\"`;\n found = true;\n break;\n }\n }\n\n if (!found) {\n // Insert before closing ---\n lines.splice(closeIdx, 0, `${key}: \"${value}\"`);\n }\n\n return lines.join('\\n');\n}\n","/**\n * STORY-002-08: cleargate wiki lint\n *\n * Scans .cleargate/wiki/ pages against their raw source files.\n * Enforcement mode (default): exits non-zero on any drift finding.\n * Suggest mode (--suggest): exits 0, prefixes flags with [advisory].\n *\n * Output format matches cleargate-wiki-lint subagent def lines 220-227:\n * <category>: <primary-path> -> <secondary-path-or-detail> (<optional context>)\n * Summary line always last:\n * lint: <OK|FAIL> (N pages checked, M findings)\n */\n\nimport * as path from 'node:path';\nimport { loadWikiPages } from '../wiki/load-wiki.js';\nimport type { GitRunner } from '../wiki/git-sha.js';\nimport {\n checkOrphan,\n checkRepoMismatch,\n checkStaleCommit,\n checkMissingIngest,\n checkBrokenBacklinks,\n checkInvalidatedCitations,\n checkExcludedPathIngested,\n checkPaginationNeeded,\n checkGateFailure,\n checkGateStaleness,\n checkIndexBudget,\n discoverPlainTextMentions,\n type LintFinding,\n} from '../wiki/lint-checks.js';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\n\nexport interface WikiLintOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to git-sha calls */\n gitRunner?: GitRunner;\n /** Lint mode: 'enforce' (default, exits 1 on findings) or 'suggest' (always exits 0) */\n mode?: 'enforce' | 'suggest';\n}\n\nexport async function wikiLintHandler(opts: WikiLintOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n // stderr seam is wired but lint outputs everything to stdout per subagent def\n opts.stderr;\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const mode = opts.mode ?? 'enforce';\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const repoRoot = cwd;\n\n // Load wiki config early — used for pagination ceiling and index-budget ceiling\n const wikiConfig = loadWikiConfig(cwd);\n\n // Step 1: load all wiki pages (single discovery pass)\n let pages = loadWikiPages(wikiRoot);\n\n const findings: LintFinding[] = [];\n\n // Meta-check: pagination-needed (fires on bucket > configured ceiling, default 50)\n const paginationFindings = checkPaginationNeeded(pages, wikiConfig.wiki.bucket_pagination_ceiling);\n findings.push(...paginationFindings);\n\n // Step 2: per-page checks (O(n))\n for (const page of pages) {\n // (a) orphan\n const orphan = checkOrphan(page, repoRoot);\n if (orphan) findings.push(orphan);\n\n // (b) repo-mismatch\n const repoMismatch = checkRepoMismatch(page, repoRoot);\n if (repoMismatch) findings.push(repoMismatch);\n\n // (c) stale-commit\n const staleCommit = checkStaleCommit(page, repoRoot, gitRunner);\n if (staleCommit) findings.push(staleCommit);\n\n // (d) missing-ingest\n const missingIngest = checkMissingIngest(page, repoRoot);\n if (missingIngest) findings.push(missingIngest);\n\n // (e) excluded-path-ingested\n const excludedPath = checkExcludedPathIngested(page, repoRoot);\n if (excludedPath) findings.push(excludedPath);\n\n // (f) gate-failure — enforcing for Epic/Story/CR/Bug\n const gateFail = checkGateFailure(page, repoRoot);\n if (gateFail) findings.push(gateFail);\n\n // (g) gate-stale — applies to ALL types\n const gateStale = checkGateStaleness(page, repoRoot);\n if (gateStale) findings.push(gateStale);\n }\n\n // Step 3: single index cross-check pass — broken backlinks\n const backlinkFindings = checkBrokenBacklinks(pages, repoRoot);\n findings.push(...backlinkFindings);\n\n // Step 4: topic-page invalidated-citation check\n const citationFindings = checkInvalidatedCitations(pages, repoRoot);\n findings.push(...citationFindings);\n\n // Step 4.5: index token-budget check (STORY-015-03)\n const indexBudget = checkIndexBudget(repoRoot, wikiConfig.wiki.index_token_ceiling);\n\n // In suggest mode, always emit the usage line unconditionally (before advisory loop)\n if (mode === 'suggest' && indexBudget.tokens !== undefined) {\n const tokens = indexBudget.tokens;\n const ceiling = indexBudget.ceiling!;\n const pct = Math.round((tokens / ceiling) * 100);\n stdout(`index token usage: ${tokens} / ${ceiling} (${pct}%)\\n`);\n }\n\n // In enforce mode, push finding into findings array (will be emitted below)\n if (mode === 'enforce' && indexBudget.finding !== null) {\n findings.push(indexBudget.finding);\n }\n\n const pageCount = pages.length;\n const findingCount = findings.length;\n\n // Step 5: emit results\n if (mode === 'suggest') {\n // Advisory mode: prefix flags with [advisory] + do Karpathy discovery pass\n for (const finding of findings) {\n stdout(`[advisory] ${finding.line}\\n`);\n }\n\n // Karpathy discovery pass\n const suggestions = discoverPlainTextMentions(pages, repoRoot);\n for (const suggestion of suggestions) {\n stdout(`${suggestion}\\n`);\n }\n\n stdout(`lint: OK (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(0);\n return;\n }\n\n // Enforce mode\n for (const finding of findings) {\n stdout(`${finding.line}\\n`);\n }\n\n if (findingCount > 0) {\n stdout(`lint: FAIL (${pageCount} pages checked, ${findingCount} findings)\\n`);\n exit(1);\n } else {\n stdout(`lint: OK (${pageCount} pages checked, 0 findings)\\n`);\n exit(0);\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport type { WikiPage } from './page-schema.js';\nimport type { WikiPageType, RepoTag } from './page-schema.js';\n\nexport interface LoadedWikiPage {\n absPath: string;\n page: WikiPage;\n body: string;\n}\n\nconst BUCKET_DIRS = ['epics', 'stories', 'sprints', 'proposals', 'crs', 'bugs', 'topics'];\n\n/**\n * Glob+Read every wiki page from wikiRoot once.\n * Shared by wiki-lint and wiki-query (STORY-002-07 and STORY-002-08).\n */\nexport function loadWikiPages(wikiRoot: string): LoadedWikiPage[] {\n const results: LoadedWikiPage[] = [];\n\n for (const bucket of BUCKET_DIRS) {\n const dir = path.join(wikiRoot, bucket);\n if (!fs.existsSync(dir)) continue;\n\n const entries = fs.readdirSync(dir, { encoding: 'utf8' }) as string[];\n for (const filename of entries) {\n if (!filename.endsWith('.md')) continue;\n const absPath = path.join(dir, filename);\n const stat = fs.statSync(absPath);\n if (!stat.isFile()) continue;\n\n const raw = fs.readFileSync(absPath, 'utf8');\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(raw);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n continue;\n }\n\n const page: WikiPage = {\n type: (fm['type'] as WikiPageType) ?? 'epic',\n id: String(fm['id'] ?? ''),\n parent: String(fm['parent'] ?? ''),\n children: Array.isArray(fm['children'])\n ? (fm['children'] as unknown[]).map(String)\n : [],\n status: String(fm['status'] ?? ''),\n remote_id: String(fm['remote_id'] ?? ''),\n raw_path: String(fm['raw_path'] ?? ''),\n last_ingest: String(fm['last_ingest'] ?? ''),\n last_ingest_commit: String(fm['last_ingest_commit'] ?? ''),\n repo: (fm['repo'] as RepoTag) ?? 'planning',\n };\n\n results.push({ absPath, page, body });\n }\n }\n\n return results;\n}\n","/**\n * Pure-function lint check implementations.\n * One exported function per category so they can be unit-tested in isolation.\n * Categories must use these exact strings (subagent + CLI agree):\n * orphan, repo-mismatch, stale-commit, missing-ingest, broken-backlink,\n * invalidated-citation, excluded-path-ingested, pagination-needed, index-budget\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport type { GitRunner } from './git-sha.js';\nimport type { LoadedWikiPage } from './load-wiki.js';\nimport { deriveRepo } from './derive-repo.js';\nimport { parseFrontmatter } from './parse-frontmatter.js';\nimport { detectWorkItemTypeFromFm } from '../lib/work-item-type.js';\n\nexport interface LintFinding {\n category: string;\n line: string;\n}\n\n/** §10.3 excluded directories — wiki pages must not exist for raw files under these. */\nconst EXCLUDED_DIRS = [\n '.cleargate/knowledge/',\n '.cleargate/templates/',\n '.cleargate/sprint-runs/',\n '.cleargate/hook-log/',\n '.cleargate/wiki/',\n];\n\n/** Default maximum entries per bucket before pagination-needed fires (overridable via wiki config). */\nconst DEFAULT_MAX_BUCKET_ENTRIES = 50;\n\n/**\n * Check (a): Orphan — wiki page's raw_path doesn't exist on disk.\n * Skips pages whose raw_path is under an excluded directory (caught by check 7).\n */\nexport function checkOrphan(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n // Don't flag orphan for excluded paths — that's caught by excluded-path-ingested\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'orphan',\n line: `orphan: ${relPage} -> missing ${rawPath} (raw missing)`,\n };\n }\n return null;\n}\n\n/**\n * Check (b): repo-mismatch — stored repo field doesn't match raw_path prefix.\n */\nexport function checkRepoMismatch(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n let derivedRepo: string;\n try {\n derivedRepo = deriveRepo(rawPath);\n } catch {\n return null; // Can't derive — not our responsibility to flag here\n }\n\n if (page.page.repo !== derivedRepo) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'repo-mismatch',\n line: `repo-mismatch: ${relPage} declares repo:${page.page.repo} but raw_path implies repo:${derivedRepo}`,\n };\n }\n return null;\n}\n\n/**\n * Check (c): stale-commit — stored last_ingest_commit differs from current git HEAD SHA.\n * Empty stored SHA is tolerated (untracked file).\n */\nexport function checkStaleCommit(\n page: LoadedWikiPage,\n repoRoot: string,\n gitRunner?: GitRunner,\n): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const storedSha = page.page.last_ingest_commit;\n // If stored SHA is empty, file was untracked at ingest time — tolerate\n if (!storedSha) return null;\n\n // Run git log -1\n let currentSha: string;\n if (gitRunner) {\n currentSha = gitRunner('git', ['log', '-1', '--format=%H', '--', rawPath]).trim();\n } else {\n const result = spawnSync('git', ['log', '-1', '--format=%H', '--', rawPath], {\n encoding: 'utf8',\n cwd: repoRoot,\n });\n currentSha = (result.stdout ?? '').trim();\n }\n\n if (!currentSha) return null; // untracked now — don't flag\n if (storedSha !== currentSha) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'stale-commit',\n line: `stale-commit: ${relPage} at ${storedSha}, current ${currentSha}`,\n };\n }\n return null;\n}\n\n/**\n * Check (d): missing-ingest — raw file mtime newer than wiki page mtime.\n */\nexport function checkMissingIngest(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null; // orphan check handles this\n\n const rawStat = fs.statSync(absRaw);\n const pageStat = fs.statSync(page.absPath);\n\n // Use > 2s gap to avoid HFS+ mtime resolution flakiness (per blueprint gotcha)\n const rawMtimeMs = rawStat.mtimeMs;\n const pageMtimeMs = pageStat.mtimeMs;\n\n if (rawMtimeMs - pageMtimeMs > 2000) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n const rawMtime = rawStat.mtime.toISOString();\n const pageMtime = pageStat.mtime.toISOString();\n return {\n category: 'missing-ingest',\n line: `missing-ingest: ${rawPath} newer than ${relPage} (raw mtime: ${rawMtime}, page mtime: ${pageMtime})`,\n };\n }\n return null;\n}\n\n/**\n * Check (backlink): broken backlink — child's parent exists but parent doesn't list the child.\n * O(n) linear scan over collected parent declarations.\n */\nexport function checkBrokenBacklinks(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n // Build a map of id -> page for O(1) lookup\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n for (const childPage of pages) {\n const parentRef = childPage.page.parent;\n if (!parentRef) continue;\n\n // Extract parent ID from \"[[PARENT-ID]]\" form\n const match = parentRef.match(/\\[\\[(.+?)\\]\\]/);\n if (!match) continue;\n const parentId = match[1];\n\n const parentPage = byId.get(parentId);\n if (!parentPage) {\n // Parent page missing — flag\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n continue;\n }\n\n // Check that parent's children list contains [[childId]]\n const childId = childPage.page.id;\n const childRef = `[[${childId}]]`;\n const parentHasChild = parentPage.page.children.some(\n (c) => c === childRef || c === childId,\n );\n\n if (!parentHasChild) {\n const relChild = path.relative(wikiRoot, childPage.absPath).replace(/\\\\/g, '/');\n findings.push({\n category: 'broken-backlink',\n line: `broken-backlink: ${relChild} -> ${parentId} (parent missing child entry)`,\n });\n }\n }\n\n return findings;\n}\n\n/**\n * Check: invalidated-citation — topic page cites a cancelled or missing item.\n */\nexport function checkInvalidatedCitations(pages: LoadedWikiPage[], repoRoot: string): LintFinding[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, LoadedWikiPage>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, p);\n }\n\n const findings: LintFinding[] = [];\n\n const topicPages = pages.filter((p) => p.page.type === 'topic');\n\n for (const topicPage of topicPages) {\n // WikiPage doesn't have a cites field — re-parse raw frontmatter to get it.\n const relTopic = path.relative(wikiRoot, topicPage.absPath).replace(/\\\\/g, '/');\n\n let citesList: string[] = [];\n try {\n const raw = fs.readFileSync(topicPage.absPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n const rawCites = fm['cites'];\n if (Array.isArray(rawCites)) {\n citesList = (rawCites as unknown[]).map(String);\n }\n } catch {\n continue;\n }\n\n for (const cite of citesList) {\n const match = cite.match(/\\[\\[(.+?)\\]\\]/);\n const id = match ? match[1] : cite;\n\n const citedPage = byId.get(id);\n if (!citedPage) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (missing)`,\n });\n continue;\n }\n\n const status = citedPage.page.status;\n if (status === 'cancelled' || status.toLowerCase().includes('cancelled')) {\n findings.push({\n category: 'invalidated-citation',\n line: `invalidated-citation: ${relTopic} cites [[${id}]] (cancelled)`,\n });\n }\n }\n }\n\n return findings;\n}\n\n/**\n * Check: excluded-path-ingested — wiki page exists for a raw file under an excluded directory.\n */\nexport function checkExcludedPathIngested(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const isExcluded = EXCLUDED_DIRS.some((excl) => rawPath.startsWith(excl));\n if (isExcluded) {\n const relPage = path.relative(path.join(repoRoot, '.cleargate', 'wiki'), page.absPath).replace(/\\\\/g, '/');\n return {\n category: 'excluded-path-ingested',\n line: `excluded-path-ingested: ${relPage} (raw_path ${rawPath} is under an excluded directory)`,\n };\n }\n return null;\n}\n\n/**\n * Meta-check: pagination-needed — fires if any bucket has more than `ceiling` entries.\n * @param ceiling Maximum entries per bucket; defaults to DEFAULT_MAX_BUCKET_ENTRIES=50.\n * Configure via `.cleargate/config.yml` → `wiki.bucket_pagination_ceiling`.\n */\nexport function checkPaginationNeeded(pages: LoadedWikiPage[], ceiling: number = DEFAULT_MAX_BUCKET_ENTRIES): LintFinding[] {\n // Count by bucket (derived from absPath directory name)\n const bucketCounts = new Map<string, number>();\n for (const p of pages) {\n const bucket = path.basename(path.dirname(p.absPath));\n bucketCounts.set(bucket, (bucketCounts.get(bucket) ?? 0) + 1);\n }\n\n const findings: LintFinding[] = [];\n for (const [bucket, count] of bucketCounts) {\n if (count > ceiling) {\n findings.push({\n category: 'pagination-needed',\n line: `pagination-needed: ${bucket} (${count} entries, max ${ceiling} per bucket)`,\n });\n }\n }\n return findings;\n}\n\n/** Work-item types that trigger enforcing gate-failure lint (not advisory). */\nconst ENFORCING_TYPES = new Set(['epic', 'story', 'cr', 'bug']);\n\n/** Status values considered \"ready\" (🟢-candidate). */\nconst READY_STATUSES = new Set(['Ready', 'Active']);\n\n/**\n * Parse the cached_gate_result from a raw frontmatter record.\n * parseFrontmatter stores nested YAML objects as opaque strings starting with '{'.\n * This helper resolves either form into a plain object or null.\n */\nfunction parseCachedGateResult(\n raw: unknown,\n): { pass: unknown; failing_criteria: unknown; last_gate_check: unknown } | null {\n if (!raw || raw === null) return null;\n\n // Opaque string form — inline flow YAML written by writeCachedGate\n if (typeof raw === 'string') {\n if (!raw.startsWith('{')) return null;\n try {\n const parsed = yaml.load(raw);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n } catch {\n return null;\n }\n }\n\n // Already-parsed object form (future-proofing)\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n const p = raw as Record<string, unknown>;\n return { pass: p['pass'], failing_criteria: p['failing_criteria'], last_gate_check: p['last_gate_check'] };\n }\n\n return null;\n}\n\n/**\n * Check: gate-failure — 🟢-candidate Epic/Story/CR/Bug with cached_gate_result.pass === false.\n * Reads the raw work-item file (not the wiki page).\n * Proposal / Sprint / Initiative are advisory only → returns null (no enforcing block).\n */\nexport function checkGateFailure(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr || cgr.pass !== false) return null;\n\n // Check if the work-item type is enforcing\n const wiType = detectWorkItemTypeFromFm(rawFm);\n if (!wiType || !ENFORCING_TYPES.has(wiType)) return null;\n\n // Check if this is a 🟢-candidate (status Ready/Active or ambiguity 🟢 Low)\n const status = String(rawFm['status'] ?? '');\n const ambiguity = String(rawFm['ambiguity'] ?? '');\n const isReadyCandidate = READY_STATUSES.has(status) || ambiguity === '🟢 Low';\n if (!isReadyCandidate) return null;\n\n // Collect failing criteria IDs\n const failingCriteria = cgr.failing_criteria;\n const criteriaIds: string[] = [];\n if (Array.isArray(failingCriteria)) {\n for (const criterion of failingCriteria as unknown[]) {\n if (criterion && typeof criterion === 'object' && 'id' in (criterion as object)) {\n criteriaIds.push(String((criterion as Record<string, unknown>)['id']));\n } else if (typeof criterion === 'string') {\n criteriaIds.push(criterion);\n }\n }\n }\n\n const criteriaStr = criteriaIds.length > 0 ? criteriaIds.join(', ') : 'unknown';\n return {\n category: 'gate-failure',\n line: `gate-failure: ${rawPath} failed criteria: ${criteriaStr}`,\n };\n}\n\n/**\n * Check: gate-stale — cached_gate_result.last_gate_check < updated_at (ISO-8601 lexical compare).\n * Applies to ALL work-item types (including Proposal/Sprint/Initiative).\n * Reads the raw work-item file (not the wiki page).\n */\nexport function checkGateStaleness(page: LoadedWikiPage, repoRoot: string): LintFinding | null {\n const rawPath = page.page.raw_path;\n if (!rawPath) return null;\n\n const absRaw = path.join(repoRoot, rawPath);\n if (!fs.existsSync(absRaw)) return null;\n\n let rawFm: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(absRaw, 'utf8');\n const { fm } = parseFrontmatter(raw);\n rawFm = fm;\n } catch {\n return null;\n }\n\n const cgr = parseCachedGateResult(rawFm['cached_gate_result']);\n if (!cgr) return null;\n\n const lastGateCheck = cgr.last_gate_check;\n if (!lastGateCheck || lastGateCheck === null) return null;\n\n const updatedAt = rawFm['updated_at'];\n if (!updatedAt) return null;\n\n const lastCheckStr = String(lastGateCheck);\n const updatedAtStr = String(updatedAt);\n\n // ISO-8601 lexical compare: if last_gate_check < updated_at → stale\n if (lastCheckStr < updatedAtStr) {\n return {\n category: 'gate-stale',\n line: `gate-stale: ${rawPath} last_gate_check=${lastCheckStr} < updated_at=${updatedAtStr}`,\n };\n }\n return null;\n}\n\n/**\n * Karpathy discovery pass: scan page bodies for plain-text ID mentions\n * (not wrapped in [[]]). Emit suggest lines.\n */\nexport function discoverPlainTextMentions(pages: LoadedWikiPage[], repoRoot: string): string[] {\n const wikiRoot = path.join(repoRoot, '.cleargate', 'wiki');\n const byId = new Map<string, boolean>();\n for (const p of pages) {\n if (p.page.id) byId.set(p.page.id, true);\n }\n\n const suggestions: string[] = [];\n const ID_PATTERN = /\\b((?:EPIC|STORY|SPRINT|PROPOSAL|CR|BUG)-[\\w-]+)\\b/g;\n const LINK_PATTERN = /\\[\\[[\\w-]+\\]\\]/g;\n\n for (const page of pages) {\n const relPage = path.relative(wikiRoot, page.absPath).replace(/\\\\/g, '/');\n // Find all [[...]] wrapped references to exclude\n const wrappedRefs = new Set<string>();\n for (const m of page.body.matchAll(LINK_PATTERN)) {\n const inner = m[0].slice(2, -2);\n wrappedRefs.add(inner);\n }\n\n // Find plain-text ID mentions\n for (const m of page.body.matchAll(ID_PATTERN)) {\n const mentionedId = m[1];\n if (!byId.has(mentionedId)) continue;\n if (wrappedRefs.has(mentionedId)) continue;\n if (mentionedId === page.page.id) continue; // self-reference\n suggestions.push(`suggest: ${relPage} mentions ${mentionedId} in plain text, consider [[${mentionedId}]] wrap`);\n }\n }\n\n return suggestions;\n}\n\n/**\n * Check: index-budget — wiki/index.md approximate token count exceeds configured ceiling.\n * Token heuristic: Math.round(bytes / 4). Returns null when index.md is absent.\n * This check is a structural check, not a per-page check, so it takes repoRoot directly.\n */\nexport interface IndexBudgetResult {\n /** Present when tokens > ceiling. Push this into findings array. */\n finding: LintFinding | null;\n /** Always populated when index.md exists; undefined when file absent. */\n tokens?: number;\n ceiling?: number;\n}\n\nexport function checkIndexBudget(repoRoot: string, indexTokenCeiling: number): IndexBudgetResult {\n const indexPath = path.join(repoRoot, '.cleargate', 'wiki', 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n return { finding: null };\n }\n\n const bytes = fs.statSync(indexPath).size;\n const tokens = Math.round(bytes / 4);\n const ceiling = indexTokenCeiling;\n\n if (tokens > ceiling) {\n return {\n finding: {\n category: 'index-budget',\n line: `index-budget: wiki/index.md exceeds token ceiling: ${tokens} > ${ceiling}. Shard or prune (see EPIC-015).`,\n },\n tokens,\n ceiling,\n };\n }\n\n return { finding: null, tokens, ceiling };\n}\n","/**\n * work-item-type.ts — Shared work-item type detection utility.\n *\n * STORY-008-03: extracted here for STORY-008-05 to import without duplication.\n * Maps frontmatter ID keys and filename patterns to canonical work-item types.\n */\n\nexport type WorkItemType = 'story' | 'epic' | 'proposal' | 'cr' | 'bug';\n\n/**\n * Frontmatter key → work-item type mapping.\n * Keys are checked in order; first match wins.\n */\nconst FM_KEY_MAP: Array<{ key: string; type: WorkItemType }> = [\n { key: 'story_id', type: 'story' },\n { key: 'epic_id', type: 'epic' },\n { key: 'proposal_id', type: 'proposal' },\n { key: 'cr_id', type: 'cr' },\n { key: 'bug_id', type: 'bug' },\n];\n\n/**\n * Filename / ID prefix → work-item type mapping.\n */\nconst PREFIX_MAP: Array<{ prefix: string; type: WorkItemType }> = [\n { prefix: 'STORY-', type: 'story' },\n { prefix: 'EPIC-', type: 'epic' },\n { prefix: 'PROPOSAL-', type: 'proposal' },\n { prefix: 'CR-', type: 'cr' },\n { prefix: 'BUG-', type: 'bug' },\n];\n\n/**\n * Detect the work-item type from a parsed frontmatter record.\n * Returns null if no recognized ID key is found.\n */\nexport function detectWorkItemTypeFromFm(\n fm: Record<string, unknown>,\n): WorkItemType | null {\n for (const { key, type } of FM_KEY_MAP) {\n if (fm[key] !== undefined && fm[key] !== null && fm[key] !== '') {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Detect the work-item type from an ID string or file path.\n * Matches against the uppercase prefix (STORY-, EPIC-, etc.).\n * Returns null if no prefix matches.\n */\nexport function detectWorkItemType(idOrPath: string): WorkItemType | null {\n const upper = idOrPath.toUpperCase();\n // Strip leading directory path components\n const basename = upper.split('/').pop() ?? upper;\n for (const { prefix, type } of PREFIX_MAP) {\n if (basename.includes(prefix)) {\n return type;\n }\n }\n return null;\n}\n\n/**\n * Canonical transitions per work-item type.\n * Epic has 2; all others have 1.\n */\nexport const WORK_ITEM_TRANSITIONS: Record<WorkItemType, string[]> = {\n proposal: ['ready-for-decomposition'],\n epic: ['ready-for-decomposition', 'ready-for-coding'],\n story: ['ready-for-execution'],\n cr: ['ready-to-apply'],\n bug: ['ready-for-fix'],\n};\n","/**\n * STORY-015-03: Per-repo wiki configuration loader.\n * STORY-018-03: Extended to include `gates` map.\n * CR-002: Extended to include `bucket_pagination_ceiling` for configurable pagination threshold.\n *\n * Reads `.cleargate/config.yml` from the repo root.\n * Single responsibility: surface `wiki.index_token_ceiling`, `wiki.bucket_pagination_ceiling`,\n * and `gates` map. Missing file → defaults. Malformed YAML → throws with file path in message.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport yaml from 'js-yaml';\n\nexport interface GatesConfig {\n precommit?: string;\n test?: string;\n typecheck?: string;\n lint?: string;\n}\n\nexport interface WikiConfig {\n wiki: {\n index_token_ceiling: number;\n /** Maximum entries per bucket before pagination-needed fires. Default: 50. */\n bucket_pagination_ceiling: number;\n };\n gates: GatesConfig;\n}\n\nconst DEFAULT_INDEX_TOKEN_CEILING = 8000;\nconst DEFAULT_BUCKET_PAGINATION_CEILING = 50;\n\n/**\n * Load per-repo wiki config from `<repoRoot>/.cleargate/config.yml`.\n * Returns defaults when file is absent.\n * Throws a descriptive error on malformed YAML.\n */\nexport function loadWikiConfig(repoRoot: string): WikiConfig {\n const configPath = path.join(repoRoot, '.cleargate', 'config.yml');\n\n if (!fs.existsSync(configPath)) {\n return {\n wiki: {\n index_token_ceiling: DEFAULT_INDEX_TOKEN_CEILING,\n bucket_pagination_ceiling: DEFAULT_BUCKET_PAGINATION_CEILING,\n },\n gates: {},\n };\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch (err) {\n throw new Error(`Failed to read ${configPath}: ${String(err)}`);\n }\n\n let parsed: unknown;\n try {\n parsed = yaml.load(raw, { schema: yaml.CORE_SCHEMA });\n } catch (err) {\n throw new Error(`Malformed YAML in ${configPath}: ${String(err)}`);\n }\n\n const ceiling = extractCeiling(parsed);\n const paginationCeiling = extractPaginationCeiling(parsed);\n const gates = extractGates(parsed);\n\n return {\n wiki: {\n index_token_ceiling: ceiling,\n bucket_pagination_ceiling: paginationCeiling,\n },\n gates,\n };\n}\n\nfunction extractCeiling(parsed: unknown): number {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const root = parsed as Record<string, unknown>;\n const wiki = root['wiki'];\n\n if (wiki === null || wiki === undefined || typeof wiki !== 'object' || Array.isArray(wiki)) {\n return DEFAULT_INDEX_TOKEN_CEILING;\n }\n\n const wikiObj = wiki as Record<string, unknown>;\n const ceiling = wikiObj['index_token_ceiling'];\n\n if (typeof ceiling === 'number' && Number.isFinite(ceiling) && ceiling > 0) {\n return ceiling;\n }\n\n return DEFAULT_INDEX_TOKEN_CEILING;\n}\n\nfunction extractPaginationCeiling(parsed: unknown): number {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return DEFAULT_BUCKET_PAGINATION_CEILING;\n }\n\n const root = parsed as Record<string, unknown>;\n const wiki = root['wiki'];\n\n if (wiki === null || wiki === undefined || typeof wiki !== 'object' || Array.isArray(wiki)) {\n return DEFAULT_BUCKET_PAGINATION_CEILING;\n }\n\n const wikiObj = wiki as Record<string, unknown>;\n const ceiling = wikiObj['bucket_pagination_ceiling'];\n\n if (typeof ceiling === 'number' && Number.isFinite(ceiling) && ceiling > 0) {\n return ceiling;\n }\n\n return DEFAULT_BUCKET_PAGINATION_CEILING;\n}\n\nfunction extractGates(parsed: unknown): GatesConfig {\n if (parsed === null || parsed === undefined || typeof parsed !== 'object' || Array.isArray(parsed)) {\n return {};\n }\n\n const root = parsed as Record<string, unknown>;\n const gates = root['gates'];\n\n if (gates === null || gates === undefined || typeof gates !== 'object' || Array.isArray(gates)) {\n return {};\n }\n\n const gatesObj = gates as Record<string, unknown>;\n const result: GatesConfig = {};\n\n if (typeof gatesObj['precommit'] === 'string') result.precommit = gatesObj['precommit'];\n if (typeof gatesObj['test'] === 'string') result.test = gatesObj['test'];\n if (typeof gatesObj['typecheck'] === 'string') result.typecheck = gatesObj['typecheck'];\n if (typeof gatesObj['lint'] === 'string') result.lint = gatesObj['lint'];\n\n return result;\n}\n","/**\n * STORY-002-08: cleargate wiki query [--persist]\n *\n * Read-only by default: grep .cleargate/wiki/index.md for query terms,\n * return matching [[ID]] list with one-line excerpts to stdout. Exit 0.\n *\n * --persist: compute slug, write wiki/topics/<slug>.md with frontmatter\n * type: topic, id, created_by, created_at, cites. Append to wiki/index.md\n * ## Topics section.\n *\n * NOTE: CLI synthesis is grep-and-list. For NL synthesis with the\n * cleargate-wiki-query subagent, invoke from a Claude Code session.\n * This diverges from PROPOSAL-002 §2.2 intentionally for testability and\n * offline/scripted use.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface WikiQueryOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: frozen ISO timestamp (defaults to new Date().toISOString()) */\n now?: () => string;\n /** The query string */\n query: string;\n /** If true, write result as a topic page under wiki/topics/ */\n persist?: boolean;\n}\n\n/**\n * Compute a slug from a query string.\n * Lowercase, replace spaces and punctuation with hyphens,\n * strip consecutive hyphens, truncate to ≤40 chars.\n */\nexport function computeSlug(query: string): string {\n return query\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-') // non-alphanumeric → hyphen\n .replace(/^-+|-+$/g, '') // strip leading/trailing hyphens\n .replace(/-{2,}/g, '-') // collapse consecutive hyphens\n .slice(0, 40)\n .replace(/-+$/, ''); // strip trailing hyphens after truncation\n}\n\n/**\n * Parse index.md and extract matching IDs for the given query terms.\n * Returns array of { id, line } matching lines.\n */\nfunction searchIndex(indexContent: string, query: string): Array<{ id: string; excerpt: string }> {\n const terms = query\n .toLowerCase()\n .split(/\\s+/)\n .filter((t) => t.length > 0);\n\n const results: Array<{ id: string; excerpt: string }> = [];\n const seenIds = new Set<string>();\n\n for (const line of indexContent.split('\\n')) {\n const lower = line.toLowerCase();\n const matchesAll = terms.every((term) => lower.includes(term));\n if (!matchesAll) continue;\n\n // Extract [[ID]] from line\n const match = line.match(/\\[\\[([^\\]]+)\\]\\]/);\n if (!match) continue;\n const id = match[1];\n if (seenIds.has(id)) continue;\n seenIds.add(id);\n\n results.push({ id, excerpt: line.trim() });\n }\n\n return results;\n}\n\nexport async function wikiQueryHandler(opts: WikiQueryOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const now = opts.now ?? (() => new Date().toISOString());\n const query = opts.query;\n const persist = opts.persist ?? false;\n\n void stderr; // suppress unused warning\n\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const indexPath = path.join(wikiRoot, 'index.md');\n\n if (!fs.existsSync(indexPath)) {\n stdout(`wiki query: no index.md found at ${indexPath}\\n`);\n stdout(`Run \\`cleargate wiki build\\` first.\\n`);\n exit(1);\n return;\n }\n\n const indexContent = fs.readFileSync(indexPath, 'utf8');\n const matches = searchIndex(indexContent, query);\n\n if (matches.length === 0) {\n stdout(`wiki query: no matches for \"${query}\"\\n`);\n exit(0);\n return;\n }\n\n // Build output body\n const bodyLines: string[] = [\n `# Query: ${query}`,\n '',\n `Found ${matches.length} match(es):`,\n '',\n ];\n\n for (const { id, excerpt } of matches) {\n bodyLines.push(`- [[${id}]] — ${excerpt}`);\n }\n bodyLines.push('');\n\n const body = bodyLines.join('\\n');\n\n // Output to stdout (read-only mode: always output to stdout)\n stdout(body);\n\n if (!persist) {\n exit(0);\n return;\n }\n\n // Persist mode: write topic page\n const slug = computeSlug(query);\n const topicsDir = path.join(wikiRoot, 'topics');\n fs.mkdirSync(topicsDir, { recursive: true });\n\n const citesArray = matches.map(({ id }) => `\"[[${id}]]\"`);\n const createdAt = now();\n\n // Build topic page frontmatter\n const frontmatter = [\n '---',\n `type: topic`,\n `id: \"${slug}\"`,\n `created_by: \"cleargate-wiki-query\"`,\n `created_at: \"${createdAt}\"`,\n `cites: [${citesArray.join(', ')}]`,\n '---',\n ].join('\\n');\n\n const topicContent = `${frontmatter}\\n\\n${body}`;\n const topicPath = path.join(topicsDir, `${slug}.md`);\n\n // Overwrite if exists (slug collision → overwrite per subagent def line 136)\n fs.writeFileSync(topicPath, topicContent, 'utf8');\n\n // Update wiki/index.md Topics section\n updateIndexTopicsSection(indexPath, slug, query, createdAt);\n\n exit(0);\n}\n\n/**\n * Append one row to the ## Topics section of wiki/index.md.\n * Creates the section header if absent.\n */\nfunction updateIndexTopicsSection(\n indexPath: string,\n slug: string,\n query: string,\n createdAt: string,\n): void {\n let content = fs.readFileSync(indexPath, 'utf8');\n\n const row = `| ${slug} | ${query} | ${createdAt} |`;\n\n if (content.includes('## Topics')) {\n // Append after the last line of the Topics section (end of file or before next ##)\n // Find the Topics section and append the row at the end\n const topicsIdx = content.indexOf('## Topics');\n const afterTopics = content.slice(topicsIdx);\n\n // Find the next ## section or end of file\n const nextSectionMatch = afterTopics.slice('## Topics'.length).match(/\\n## /);\n if (nextSectionMatch && nextSectionMatch.index !== undefined) {\n const insertPos = topicsIdx + '## Topics'.length + nextSectionMatch.index;\n content = content.slice(0, insertPos) + `\\n${row}` + content.slice(insertPos);\n } else {\n // Topics is the last section — append at end\n content = content.trimEnd() + `\\n${row}\\n`;\n }\n } else {\n // Create Topics section at end of file\n content = content.trimEnd() + `\\n\\n## Topics\\n\\n${row}\\n`;\n }\n\n fs.writeFileSync(indexPath, content, 'utf8');\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline';\nimport { scanRawItems } from '../wiki/scan.js';\n\nexport interface WikiAuditStatusOptions {\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp (not used for output; reserved for future use) */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Apply safe status corrections to frontmatter */\n fix?: boolean;\n /** Required together with --fix to confirm writes in non-interactive mode */\n yes?: boolean;\n /** Suppress diff output */\n quiet?: boolean;\n /** Test seam: override isTTY detection */\n isTTY?: boolean;\n /** Test seam: override readline for confirmation prompt */\n promptReader?: () => Promise<string>;\n}\n\n// Terminal statuses: items whose lifecycle is conclusively closed (no further work expected).\n// 'Verified' covers QA-verified bugs; 'Approved' covers CRs/proposals formally accepted with no separate Completed step.\nconst TERMINAL = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved', 'Verified', 'Approved']);\n\ninterface DriftItem {\n id: string;\n rawPath: string;\n absPath: string;\n bucket: string;\n currentStatus: string;\n rule: 'A' | 'B' | 'C';\n /** Suggested new status (undefined for Rule B — file move needed) */\n suggestedStatus?: string;\n /** Human-readable description for the report */\n description: string;\n}\n\nexport async function wikiAuditStatusHandler(opts: WikiAuditStatusOptions = {}): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => { process.stdout.write(s); });\n const stderr = opts.stderr ?? ((s: string) => { process.stderr.write(s); });\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const isTTY = opts.isTTY ?? Boolean(process.stdout.isTTY);\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n\n if (!fs.existsSync(deliveryRoot)) {\n stderr(`audit-status: .cleargate/delivery/ not found at ${deliveryRoot}\\n`);\n exit(1);\n return;\n }\n\n const items = scanRawItems(deliveryRoot, cwd);\n\n // Build a lookup of epic id → child stories (with terminal status check)\n const storiesByEpic = new Map<string, typeof items>();\n for (const item of items) {\n if (item.bucket !== 'stories') continue;\n const epicRef = String(item.fm['parent_epic_ref'] ?? '').replace(/^\\[\\[|\\]\\]$/g, '');\n if (!epicRef) continue;\n if (!storiesByEpic.has(epicRef)) storiesByEpic.set(epicRef, []);\n storiesByEpic.get(epicRef)!.push(item);\n }\n\n const driftItems: DriftItem[] = [];\n\n for (const item of items) {\n const currentStatus = String(item.fm['status'] ?? '');\n const isTerminal = TERMINAL.has(currentStatus);\n const inArchive = item.rawPath.includes('/archive/');\n const inPendingSync = item.rawPath.includes('/pending-sync/');\n\n // Rule A: in archive/ but status is non-terminal\n if (inArchive && !isTerminal) {\n // Determine suggested fix: Completed if all child stories terminal (epics/sprints), else Abandoned\n let suggestedStatus = 'Abandoned';\n if (item.bucket === 'epics' || item.bucket === 'sprints') {\n const childStories = storiesByEpic.get(item.id) ?? [];\n if (childStories.length > 0 && childStories.every((s) => TERMINAL.has(String(s.fm['status'] ?? '')))) {\n suggestedStatus = 'Completed';\n }\n }\n\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'A',\n suggestedStatus,\n description: `Rule A — archived with non-terminal status '${currentStatus}'`,\n });\n }\n\n // Rule B: in pending-sync/ but status is terminal\n if (inPendingSync && isTerminal) {\n const archivePath = item.rawPath.replace('/pending-sync/', '/archive/');\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'B',\n // No suggestedStatus — Rule B requires file move, not status change\n description: `Rule B — pending-sync with terminal status '${currentStatus}'; run: git mv ${item.rawPath} ${archivePath.replace(/\\/[^/]+$/, '/')}`,\n });\n }\n\n // Rule C: sprint file, non-terminal status, all children of its epics are terminal\n if (item.bucket === 'sprints' && !isTerminal) {\n const epicRefs = item.fm['epics'];\n if (!epicRefs) continue; // No epics key → Rule C does not fire\n const epicsArr = Array.isArray(epicRefs) ? epicRefs : [epicRefs];\n if (epicsArr.length === 0) continue;\n\n let totalChildren = 0;\n let terminalChildren = 0;\n\n for (const epicRef of epicsArr) {\n const epicId = String(epicRef).replace(/^\\[\\[|\\]\\]$/g, '');\n const children = storiesByEpic.get(epicId) ?? [];\n totalChildren += children.length;\n terminalChildren += children.filter((s) => TERMINAL.has(String(s.fm['status'] ?? ''))).length;\n }\n\n if (totalChildren > 0 && totalChildren === terminalChildren) {\n driftItems.push({\n id: item.id,\n rawPath: item.rawPath,\n absPath: item.absPath,\n bucket: item.bucket,\n currentStatus,\n rule: 'C',\n suggestedStatus: 'Completed',\n description: `Rule C — ${terminalChildren}/${totalChildren} child stories terminal; suggest Completed`,\n });\n }\n }\n }\n\n // Print report\n if (driftItems.length === 0) {\n stdout('audit-status: clean (0 drift)\\n');\n exit(0);\n return;\n }\n\n for (const d of driftItems) {\n if (d.rule === 'B') {\n // Rule B: emit git mv hint\n const archivePath = d.rawPath.replace('/pending-sync/', '/archive/');\n const archiveDir = archivePath.replace(/\\/[^/]+$/, '/');\n stdout(`${d.id}: ${d.description}\\n`);\n stdout(` git mv ${d.rawPath} ${archiveDir}\\n`);\n } else {\n stdout(`${d.id}: ${d.description}\\n`);\n }\n }\n\n if (!opts.fix) {\n exit(1);\n return;\n }\n\n // --fix mode\n const fixable = driftItems.filter((d) => d.rule !== 'B' && d.suggestedStatus !== undefined);\n const ruleB = driftItems.filter((d) => d.rule === 'B');\n\n if (ruleB.length > 0) {\n for (const d of ruleB) {\n stdout(` (skipping ${d.id}: Rule B requires manual file move, not a status change)\\n`);\n }\n }\n\n if (fixable.length === 0) {\n stdout('audit-status: no auto-fixable items (Rule B items require manual git mv)\\n');\n exit(0);\n return;\n }\n\n // Confirmation\n if (!opts.yes) {\n if (!isTTY) {\n stderr('audit-status: --fix requires --yes in non-interactive mode\\n');\n exit(2);\n return;\n }\n\n // TTY: prompt\n let answer: string;\n if (opts.promptReader) {\n answer = await opts.promptReader();\n } else {\n answer = await new Promise<string>((resolve) => {\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n rl.question(`apply ${fixable.length} changes? [y/N] `, (ans) => {\n rl.close();\n resolve(ans);\n });\n });\n }\n\n if (!answer.trim().toLowerCase().startsWith('y')) {\n stdout('aborted\\n');\n exit(2);\n return;\n }\n }\n\n // Apply fixes via line-surgery regex (do NOT round-trip through parseFrontmatter)\n for (const d of fixable) {\n const rawText = fs.readFileSync(d.absPath, 'utf8');\n const updated = applyStatusFix(rawText, d.suggestedStatus!);\n\n if (!opts.quiet) {\n stdout(`--- ${d.rawPath}\\n`);\n stdout(`+++ ${d.rawPath}\\n`);\n stdout(`@@ status change @@\\n`);\n // Show the old and new status line\n const oldLine = rawText.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n const newLine = updated.split('\\n').find((l) => /^status:/.test(l)) ?? '';\n stdout(`-${oldLine}\\n`);\n stdout(`+${newLine}\\n`);\n }\n\n fs.writeFileSync(d.absPath, updated, 'utf8');\n }\n\n stdout(`audit-status: applied ${fixable.length} fix(es)\\n`);\n exit(0);\n}\n\n/**\n * Replace the first `status:` line inside the first `---` YAML front-matter block.\n * Everything else is byte-identical (no round-trip through parseFrontmatter).\n */\nfunction applyStatusFix(rawText: string, newStatus: string): string {\n // Find the closing --- of the frontmatter\n const lines = rawText.split('\\n');\n if (lines[0] !== '---') return rawText; // no frontmatter — leave untouched\n\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return rawText; // malformed — leave untouched\n\n // Replace only the first `status:` line within the frontmatter block\n let replaced = false;\n for (let i = 1; i < closeIdx; i++) {\n if (!replaced && /^status:/.test(lines[i])) {\n lines[i] = `status: \"${newStatus}\"`;\n replaced = true;\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * STORY-020-03: `cleargate wiki contradict <file>` handler.\n *\n * Thin CLI wrapper over the shared Phase 4 helpers in lib/wiki/contradict.ts.\n * Both this handler and wikiIngestHandler call the same runPhase4 / preparePhase4\n * / commitPhase4Findings — no duplicate logic (DoD §4.2).\n *\n * Flow (Mode A — orchestrator decision §B=A1):\n * 1. Resolve file → derive bucket/id/fm/body/currentSha\n * 2. Call runPhase4 which calls preparePhase4 internally\n * 3a. Skipped → print \"skipped: <reason>\", exit 0\n * 3b. Stub provided (tests) → findings committed (or not if --dry-run)\n * 3c. Production → emit phase4 JSON signal on stdout for the calling agent\n * (agent spawns cleargate-wiki-contradict via Task); exit 0\n *\n * `--dry-run` suppresses log append and SHA stamp; only prints findings.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { deriveBucket } from '../wiki/derive-bucket.js';\nimport { getGitSha, type GitRunner } from '../wiki/git-sha.js';\nimport { runPhase4, type ContradictFinding } from '../lib/wiki/contradict.js';\n\nexport interface WikiContradictOptions {\n /** Absolute or repo-relative path to a wiki page or raw delivery file. */\n filePath: string;\n /** When true, print findings without mutating state. */\n dryRun?: boolean;\n /** Test seam: working directory (defaults to process.cwd()) */\n cwd?: string;\n /** Test seam: frozen ISO timestamp */\n now?: () => string;\n /** Test seam: replaces process.stdout.write */\n stdout?: (s: string) => void;\n /** Test seam: replaces process.stderr.write */\n stderr?: (s: string) => void;\n /** Test seam: replaces process.exit */\n exit?: (code: number) => never;\n /** Test seam: forwarded to getGitSha */\n gitRunner?: GitRunner;\n /**\n * Test seam: stub for Phase 4 subagent invocation.\n * When provided, replaces the stdout signal with a synchronous call.\n */\n phase4SubagentStub?: (draftWikiPath: string, neighborhood: string[]) => ContradictFinding[];\n}\n\nexport async function wikiContradictHandler(opts: WikiContradictOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const now = opts.now ?? (() => new Date().toISOString());\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const gitRunner = opts.gitRunner;\n const dryRun = opts.dryRun ?? false;\n\n const filePath = opts.filePath;\n\n // Resolve filePath: if relative, resolve against cwd\n const absFilePath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);\n const relFilePath = path.relative(cwd, absFilePath).replace(/\\\\/g, '/');\n\n // Validate the file exists\n if (!fs.existsSync(absFilePath)) {\n stderr(`wiki contradict: file not found: ${filePath}\\n`);\n exit(1);\n return;\n }\n\n // Derive bucket + id from filename\n const filename = path.basename(absFilePath);\n let bucketInfo: { type: string; id: string; bucket: string };\n try {\n bucketInfo = deriveBucket(filename);\n } catch (e) {\n stderr(`wiki contradict: cannot determine bucket for ${filePath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n const { id } = bucketInfo;\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n\n // Read and parse the raw file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absFilePath, 'utf8');\n } catch (e) {\n stderr(`wiki contradict: cannot read ${filePath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch (e) {\n stderr(`wiki contradict: malformed frontmatter in ${filePath}: ${(e as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // Get current git SHA for idempotency check\n const currentSha = getGitSha(absFilePath, gitRunner ?? defaultGitRunner) ?? '';\n\n // Run Phase 4\n const result = runPhase4({\n absRawPath: absFilePath,\n relRawPath: relFilePath,\n wikiRoot,\n id,\n fm,\n body,\n currentSha,\n dryRun,\n now,\n gitRunner,\n phase4SubagentStub: opts.phase4SubagentStub,\n });\n\n if (result.skipped) {\n stdout(`skipped: ${result.reason ?? 'unknown reason'}\\n`);\n exit(0);\n return;\n }\n\n if (opts.phase4SubagentStub) {\n // Test seam path: findings were committed (or dry-run skipped commit)\n for (const f of result.findings) {\n stdout(`contradiction: ${f.draft} vs ${f.neighbor} · ${f.claim}\\n`);\n }\n if (result.findings.length === 0) {\n stdout(`wiki contradict: no contradictions found for ${id}\\n`);\n }\n } else {\n // Production path: emit the phase4 signal for the calling agent\n // The agent reads this JSON, spawns cleargate-wiki-contradict via Task.\n // (Node code cannot invoke the Task tool directly — no Node-side Task API.)\n if (result.phase4Signal) {\n stdout(`phase4: ${result.phase4Signal}\\n`);\n }\n }\n\n // Always exit 0 — contradiction check is advisory\n exit(0);\n}\n\nfunction defaultGitRunner(cmd: string, args: string[]): string {\n const result = spawnSync(cmd, args, { encoding: 'utf8' });\n if (result.status !== 0) return '\\0__NONZERO__';\n return result.stdout ?? '';\n}\n","/**\n * doctor.ts — STORY-009-04 + STORY-008-06\n *\n * `cleargate doctor` base command + `--check-scaffold` / `--session-start` / `--pricing` modes.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { computeUsd, type DraftTokensInput } from '../lib/pricing.js';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n readDriftState,\n type DriftMap,\n type DriftMapEntry,\n type DriftState,\n} from '../lib/manifest.js';\nimport { shortHash } from '../lib/sha256.js';\nimport { getMembershipState } from '../lib/membership.js';\nimport {\n checkLatestVersion as defaultCheckLatestVersion,\n compareSemver,\n type CheckResult,\n} from '../lib/registry-check.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface DoctorCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override the package root for loadPackageManifest (test seam). */\n packageRoot?: string;\n /** CR-011: override ~/.cleargate home path for getMembershipState (test seam). */\n cleargateHome?: string;\n /** CR-011: profile to pass to getMembershipState. */\n profile?: string;\n /** STORY-016-02: test seam — override checkLatestVersion for deterministic notifier tests. */\n checkLatestVersion?: () => Promise<CheckResult>;\n /** STORY-016-02: test seam — override the installed CLI version string for notifier tests. */\n installedVersion?: string;\n}\n\n/**\n * STORY-014-01: Accumulator passed by reference through all mode handlers.\n * Top-level doctorHandler reads at the end and exits per §3.2 pseudocode:\n * configError → exit(2), blocker → exit(1), else → exit(0).\n */\nexport interface DoctorOutcome {\n configError: boolean;\n blocker: boolean;\n}\n\n/**\n * Flags for `cleargate doctor`.\n *\n * Reserved keys for 008-06 (M3): sessionStart, pricing.\n * All flags are mutually exclusive — selectMode throws when >1 is set.\n */\nexport interface DoctorFlags {\n checkScaffold?: boolean;\n /** Hidden flag: used by the M3 session-start hook; enables daily throttle. */\n sessionStartMode?: boolean;\n verbose?: boolean;\n /** --session-start: emit blocked pending-sync items summary */\n sessionStart?: boolean;\n /** --pricing: compute USD estimate for a work item */\n pricing?: boolean;\n /** File path passed to --pricing <file> */\n pricingFile?: string;\n /** CR-008: --can-edit <file>: exits 0 if allowed, 1 if would-block */\n canEdit?: boolean;\n /** CR-008: the file path argument for --can-edit */\n canEditFile?: string;\n}\n\nexport type DoctorMode = 'check-scaffold' | 'session-start' | 'pricing' | 'hook-health' | 'can-edit';\n\n// ─── Mode dispatcher ──────────────────────────────────────────────────────────\n\n/**\n * Determine which doctor mode to run based on flags.\n *\n * Throws when multiple mutually-exclusive mode flags are set.\n * Returns 'hook-health' when no mode flag is set (default).\n *\n * Exported so STORY-008-06 can add cases without re-editing the switch.\n */\nexport function selectMode(flags: DoctorFlags): DoctorMode {\n const modes: DoctorMode[] = [];\n if (flags.checkScaffold) modes.push('check-scaffold');\n if (flags.sessionStart) modes.push('session-start');\n if (flags.pricing) modes.push('pricing');\n if (flags.canEdit) modes.push('can-edit');\n\n if (modes.length > 1) {\n throw new Error(\n `cleargate doctor: mutually exclusive flags set: ${modes.join(', ')}. Use only one mode flag at a time.`\n );\n }\n\n if (modes.length === 1) {\n return modes[0]!;\n }\n\n return 'hook-health';\n}\n\n// ─── Hook-health default mode ─────────────────────────────────────────────────\n\nconst HOOK_LOG_24H_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Parse a single gate-check.log line.\n * Format: [ISO_TS] stamp=N gate=N ingest=N file=<path>\n * Returns null if the line does not match.\n */\nexport interface HookLogEntry {\n ts: string;\n stamp: number;\n gate: number;\n ingest: number;\n file: string;\n}\n\nexport function parseHookLogLine(line: string): HookLogEntry | null {\n // [2026-04-19T12:00:00Z] stamp=0 gate=1 ingest=0 file=/some/path\n const m = line.match(\n /^\\[([^\\]]+)\\]\\s+stamp=(\\d+)\\s+gate=(\\d+)\\s+ingest=(\\d+)\\s+file=(.+)$/\n );\n if (!m) return null;\n return {\n ts: m[1]!,\n stamp: parseInt(m[2]!, 10),\n gate: parseInt(m[3]!, 10),\n ingest: parseInt(m[4]!, 10),\n file: m[5]!.trim(),\n };\n}\n\nfunction runHookHealth(\n stdout: (s: string) => void,\n cwd: string,\n now?: Date,\n outcome?: DoctorOutcome\n): void {\n // STORY-014-01: config-error — missing .cleargate/ directory\n const cleargateDir = path.join(cwd, '.cleargate');\n if (!fs.existsSync(cleargateDir)) {\n stdout('cleargate misconfigured: no .cleargate/ found. Run: cleargate init');\n if (outcome) outcome.configError = true;\n return;\n }\n\n // STORY-014-01: config-error — missing cleargate-planning/MANIFEST.json\n const manifestPath = path.join(cwd, 'cleargate-planning', 'MANIFEST.json');\n if (!fs.existsSync(manifestPath)) {\n stdout(`cleargate misconfigured: cleargate-planning/MANIFEST.json not found. Run: cleargate init`);\n if (outcome) outcome.configError = true;\n // Do not return — continue with remaining checks\n }\n\n // Minimal hook-config report: check that .claude/settings.json has the\n // SubagentStop hook wired (if the .claude directory exists).\n const settingsPath = path.join(cwd, '.claude', 'settings.json');\n if (!fs.existsSync(settingsPath)) {\n stdout('[doctor] No .claude/settings.json found — hook config unavailable.');\n return;\n }\n\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as unknown;\n const hasHooks =\n typeof settings === 'object' &&\n settings !== null &&\n 'hooks' in settings;\n if (hasHooks) {\n stdout('[doctor] Hook config present in .claude/settings.json.');\n } else {\n stdout('[doctor] .claude/settings.json found but no hooks key — SubagentStop hook not wired.');\n }\n } catch {\n stdout('[doctor] .claude/settings.json is not valid JSON — cannot verify hook config.');\n }\n\n // Scan gate-check.log for recent failures\n const logPath = path.join(cwd, '.cleargate', 'hook-log', 'gate-check.log');\n if (!fs.existsSync(logPath)) {\n return;\n }\n\n let logContent: string;\n try {\n logContent = fs.readFileSync(logPath, 'utf-8');\n } catch {\n return;\n }\n\n const nowMs = (now ?? new Date()).getTime();\n const lines = logContent.split('\\n').filter((l) => l.trim().length > 0);\n\n for (const line of lines) {\n const entry = parseHookLogLine(line);\n if (!entry) continue;\n\n const entryMs = new Date(entry.ts).getTime();\n if (isNaN(entryMs)) continue;\n\n // Only consider entries within the last 24h\n if (nowMs - entryMs > HOOK_LOG_24H_MS) continue;\n\n // A failure means ANY step exit code is non-zero\n const isFailing = entry.stamp !== 0 || entry.gate !== 0 || entry.ingest !== 0;\n if (!isFailing) continue;\n\n stdout(\n `\\u26a0 hook failure at ${entry.ts}: stamp=${entry.stamp} gate=${entry.gate} ingest=${entry.ingest} file=${entry.file}`\n );\n }\n}\n\n// ─── Check-scaffold mode ──────────────────────────────────────────────────────\n\nconst TWENTY_FOUR_HOURS_MS = 24 * 60 * 60 * 1000;\n\n/**\n * Throttle decision: returns true when the cache is fresh enough to skip\n * re-computation.\n *\n * Fresh = `now - lastRefreshed < 24h`.\n * Throttle only applies when `sessionStartMode` is set (non-interactive invocation).\n * Exported for unit tests.\n */\nexport function shouldUseCache(\n lastRefreshed: string,\n now: Date,\n sessionStartMode: boolean\n): boolean {\n if (!sessionStartMode) {\n // Interactive mode always recomputes.\n return false;\n }\n const age = now.getTime() - new Date(lastRefreshed).getTime();\n return age < TWENTY_FOUR_HOURS_MS;\n}\n\n/**\n * Format a single non-clean file line for verbose output.\n * `<path> <state> (<installed>→<current> vs <package>)`\n * with 6-char short hashes.\n */\nexport function formatVerboseLine(\n filePath: string,\n entry: DriftMapEntry\n): string {\n const inst = entry.install_sha ? shortHash(entry.install_sha).slice(0, 6) : 'null';\n const curr = entry.current_sha ? shortHash(entry.current_sha).slice(0, 6) : 'null';\n const pkg = entry.package_sha ? shortHash(entry.package_sha).slice(0, 6) : 'null';\n return ` ${filePath} ${entry.state} (${inst}→${curr} vs ${pkg})`;\n}\n\ntype CountsByState = Record<Exclude<DriftState, 'untracked'>, number> & { untracked: number };\n\nfunction zeroCounts(): CountsByState {\n return {\n 'clean': 0,\n 'user-modified': 0,\n 'upstream-changed': 0,\n 'both-changed': 0,\n 'untracked': 0,\n };\n}\n\nasync function runCheckScaffold(\n flags: DoctorFlags,\n cli: DoctorCliOptions,\n cwd: string,\n now: Date,\n stdout: (s: string) => void,\n _stderr: (s: string) => void\n): Promise<void> {\n // 1. Check daily throttle when in session-start mode\n const sessionStartMode = flags.sessionStartMode ?? false;\n const existingState = await readDriftState(cwd);\n\n if (existingState && shouldUseCache(existingState.last_refreshed, now, sessionStartMode)) {\n // Reuse cached result — emit summary from cached data\n emitSummary(existingState.drift, flags.verbose ?? false, stdout);\n return;\n }\n\n // 2. Load manifests\n const pkgManifest = loadPackageManifest({ packageRoot: cli.packageRoot });\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // 3. Compute SHAs + classify\n const driftMap: DriftMap = {};\n\n await Promise.all(\n pkgManifest.files.map(async (entry) => {\n // Silently skip user-artifact tier (EPIC-009 §6 Q8)\n if (entry.tier === 'user-artifact') {\n return;\n }\n\n // BUG-023: pass pinVersion so pin-aware hook files are reverse-substituted before hashing.\n const currentSha = await computeCurrentSha(entry, cwd, { pinVersion: installSnapshot?.pin_version });\n const installSha =\n installSnapshot?.files.find((f) => f.path === entry.path)?.sha256 ?? null;\n const pkgSha = entry.sha256;\n const state = classify(pkgSha, installSha, currentSha, entry.tier);\n\n driftMap[entry.path] = {\n state,\n entry,\n install_sha: installSha,\n current_sha: currentSha,\n package_sha: pkgSha,\n };\n })\n );\n\n // 4. Write drift state atomically\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n // 5. Emit summary\n emitSummary(driftMap, flags.verbose ?? false, stdout);\n}\n\nfunction emitSummary(\n driftMap: DriftMap,\n verbose: boolean,\n stdout: (s: string) => void\n): void {\n const counts = zeroCounts();\n for (const entry of Object.values(driftMap)) {\n counts[entry.state]++;\n }\n\n stdout(\n `Scaffold drift: ${counts['user-modified']} user-modified, ` +\n `${counts['upstream-changed']} upstream-changed, ` +\n `${counts['both-changed']} both-changed, ` +\n `${counts['clean']} clean`\n );\n\n if (counts['upstream-changed'] > 0 || counts['both-changed'] > 0) {\n stdout('Run cleargate upgrade to review.');\n }\n\n if (verbose) {\n for (const [filePath, entry] of Object.entries(driftMap)) {\n if (entry.state !== 'clean' && entry.state !== 'untracked') {\n stdout(formatVerboseLine(filePath, entry));\n }\n }\n }\n}\n\n// ─── Session-start mode ───────────────────────────────────────────────────────\n\nconst SESSION_START_MAX_ITEMS = 10;\nconst SESSION_START_MAX_CHARS = 400;\n\ninterface BlockedItem {\n id: string;\n firstCriterionId: string;\n}\n\n/**\n * Coerce `cached_gate_result` into a typed shape.\n * Accepts both the current native-object form (parseFrontmatter via js-yaml)\n * and the legacy JSON-in-a-string form (pre-BUG-001 files).\n */\nfunction parseCachedGateResult(\n raw: unknown\n): { pass: boolean | null; failing_criteria: Array<{ id: string }> } | null {\n if (raw == null) return null;\n\n let parsed: { pass?: boolean | null; failing_criteria?: Array<{ id: string }> } | null = null;\n\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n parsed = raw as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } else if (typeof raw === 'string') {\n try {\n parsed = JSON.parse(raw) as { pass?: boolean | null; failing_criteria?: Array<{ id: string }> };\n } catch {\n return null;\n }\n } else {\n return null;\n }\n\n return {\n pass: parsed.pass ?? null,\n failing_criteria: parsed.failing_criteria ?? [],\n };\n}\n\n/**\n * CR-009: Probe the three-branch resolver chain at runtime and emit one\n * `cleargate CLI: <branch> — <one-line>` status line to stdout.\n * This line is emitted ALWAYS (success or failure) so Claude sees which resolver\n * the hooks will use before any hook fires.\n */\nexport function emitResolverStatusLine(\n cwd: string,\n stdout: (s: string) => void\n): void {\n const distCliPath = path.join(cwd, 'cleargate-cli', 'dist', 'cli.js');\n\n if (fs.existsSync(distCliPath)) {\n stdout(`cleargate CLI: local dist — ${distCliPath}`);\n return;\n }\n\n // Check PATH\n const whichResult = spawnSync('command', ['-v', 'cleargate'], {\n shell: true,\n encoding: 'utf8',\n timeout: 3000,\n });\n if (whichResult.status === 0) {\n stdout('cleargate CLI: PATH (global install) — cleargate');\n return;\n }\n\n // Try to read the pinned version from the live hook script\n let pinVersion = 'unknown';\n const hookPath = path.join(cwd, '.claude', 'hooks', 'stamp-and-gate.sh');\n if (fs.existsSync(hookPath)) {\n try {\n const hookContent = fs.readFileSync(hookPath, 'utf-8');\n // Pattern: # cleargate-pin: 0.5.0\n const pinMatch = hookContent.match(/^#\\s*cleargate-pin:\\s*(\\S+)\\s*$/m);\n if (pinMatch?.[1]) {\n pinVersion = pinMatch[1];\n } else {\n // Fallback: look for npx -y \"@cleargate/cli@X.Y.Z\"\n const npxMatch = hookContent.match(/@cleargate\\/cli@([^\\s\"']+)/);\n if (npxMatch?.[1]) pinVersion = npxMatch[1];\n }\n } catch {\n // ignore\n }\n }\n\n if (pinVersion === 'unknown') {\n stdout('cleargate CLI: \\u{1F534} not resolvable — hooks will no-op. Fix: npm i -g cleargate or npx cleargate doctor');\n } else {\n stdout(`cleargate CLI: npx @cleargate/cli@${pinVersion} (cold-start ~600ms first call)`);\n }\n}\n\n/**\n * CR-008: planning-first reminder block text.\n * Emitted when pending-sync has zero approved stories AND no sprint-active sentinel.\n */\nexport const PLANNING_FIRST_REMINDER = `Triage first, draft second:\nBefore any Edit/Write that creates user-facing code, you must:\n (1) classify the request (Epic / Story / CR / Bug),\n (2) draft a work item under .cleargate/delivery/pending-sync/ from .cleargate/templates/,\n (3) halt at Gate 1 (Proposal approval) for human sign-off.\nBypass this only if the user has explicitly waived planning in this conversation.`;\n\nexport async function runSessionStart(\n cwd: string,\n stdout: (s: string) => void,\n outcome?: DoctorOutcome,\n cli?: DoctorCliOptions\n): Promise<void> {\n // CR-011: emit membership state banner as the FIRST line of --session-start output.\n // Placed before the resolver-status line so it is the first thing the agent sees.\n const membershipState = getMembershipState({\n cleargateHome: cli?.cleargateHome,\n profile: cli?.profile,\n });\n if (membershipState.state === 'member') {\n stdout(\n `ClearGate state: member (project: ${membershipState.project_id}) — full surface enabled.`\n );\n } else {\n stdout(\n 'ClearGate state: pre-member — local planning enabled, sync requires join.'\n );\n }\n\n // CR-009: emit resolver-status line ALWAYS (before the blocked-items list).\n // STORY-014-01: if resolver is \"not resolvable\", set configError.\n const resolverLines: string[] = [];\n emitResolverStatusLine(cwd, (line) => {\n stdout(line);\n resolverLines.push(line);\n });\n // Check if resolver completely failed (🔴 not resolvable branch)\n if (outcome && resolverLines.some((l) => l.includes('\\u{1F534}'))) {\n outcome.configError = true;\n }\n\n // STORY-016-02: emit update notifier BEFORE the pending-sync read so fresh installs\n // (no pending-sync/ dir) see the notice. Silent no-op on any error; never touches outcome.\n await (async () => {\n try {\n const checkFn = cli?.checkLatestVersion ?? defaultCheckLatestVersion;\n const result = await checkFn();\n if (result.latest !== null && result.from !== 'opt-out') {\n // Resolve installed version: test seam takes priority; fall back to reading\n // package.json alongside the compiled file (dist/../package.json in production).\n let installed: string;\n if (cli?.installedVersion !== undefined) {\n installed = cli.installedVersion;\n } else {\n installed = '0.0.0';\n try {\n // Resolve package.json in both environments:\n // production (tsup flat dist/): dist/../package.json = cleargate-cli/package.json (1 level up)\n // vitest (source): src/commands/../../package.json = cleargate-cli/package.json (2 levels up)\n // Try 1 level up first (production); fall back to 2 levels up (vitest source).\n const thisDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.join(thisDir, '..', 'package.json'),\n path.join(thisDir, '..', '..', 'package.json'),\n ];\n for (const pkgJsonPath of candidates) {\n try {\n const raw = fs.readFileSync(pkgJsonPath, 'utf-8');\n const pkg = JSON.parse(raw) as { version?: unknown };\n if (typeof pkg.version === 'string' && pkg.version.length > 0) {\n installed = pkg.version;\n break;\n }\n } catch {\n // Try next candidate\n }\n }\n } catch {\n // Ignore — 0.0.0 fallback means any real release looks \"newer\" (safe default).\n }\n }\n if (compareSemver(result.latest, installed) > 0) {\n stdout(\n `cleargate ${result.latest} available (current: ${installed}) — run \\`cleargate upgrade\\` or see CHANGELOG`\n );\n }\n }\n } catch {\n // Silent no-op on any error (offline, timeout, etc.)\n }\n })();\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // Directory doesn't exist or unreadable — nothing to report\n return;\n }\n\n const blocked: BlockedItem[] = [];\n let hasApprovedStory = false;\n\n for (const filePath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n // CR-008: track approved stories for planning-first gate\n if (fm['approved'] === true) {\n hasApprovedStory = true;\n }\n\n const gate = parseCachedGateResult(fm['cached_gate_result']);\n if (!gate || gate.pass !== false) continue;\n\n // Determine item ID from frontmatter\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id', 'sprint_id'];\n let itemId = '';\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim()) {\n itemId = val.trim();\n break;\n }\n }\n if (!itemId) {\n // Fallback: use filename stem\n itemId = path.basename(filePath, '.md');\n }\n\n const firstCriterionId =\n gate.failing_criteria.length > 0 ? (gate.failing_criteria[0]?.id ?? '') : '';\n\n blocked.push({ id: itemId, firstCriterionId });\n }\n\n // CR-008: check sprint-active sentinel\n const activesentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n const sprintActive = fs.existsSync(activesentinel);\n\n // CR-008: emit planning-first reminder when no approved stories AND no active sprint\n const shouldRemind = !hasApprovedStory && !sprintActive;\n if (shouldRemind) {\n stdout(PLANNING_FIRST_REMINDER);\n if (blocked.length > 0) {\n // Separator before blocked items\n stdout('');\n }\n }\n\n if (blocked.length === 0) {\n return;\n }\n\n // STORY-014-01: blocked items present → set blocker flag\n if (outcome) outcome.blocker = true;\n\n const overflow = blocked.length > SESSION_START_MAX_ITEMS\n ? blocked.length - SESSION_START_MAX_ITEMS\n : 0;\n const visible = blocked.slice(0, SESSION_START_MAX_ITEMS);\n\n const lines: string[] = [`${blocked.length} items blocked:`];\n for (const item of visible) {\n const line = item.firstCriterionId\n ? ` ${item.id}: ${item.firstCriterionId}`\n : ` ${item.id}`;\n lines.push(line);\n }\n if (overflow > 0) {\n lines.push(`…and ${overflow} more — run cleargate doctor for full list`);\n }\n\n let output = lines.join('\\n');\n\n // Cap at SESSION_START_MAX_CHARS (100-token proxy)\n if (output.length > SESSION_START_MAX_CHARS) {\n output = output.slice(0, SESSION_START_MAX_CHARS - 3) + '...';\n }\n\n stdout(output);\n}\n\n// ─── Pricing mode ─────────────────────────────────────────────────────────────\n\nexport async function runPricing(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n if (!filePath) {\n // STORY-014-01: missing <file> argument is a config/input error → exit(2)\n stderr('cleargate doctor --pricing: missing <file> argument');\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);\n\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf-8');\n } catch {\n // STORY-014-01: cannot read file is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot read file: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n if (!raw.trimStart().startsWith('---')) {\n // STORY-014-01: file has no frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: file has no frontmatter: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n // STORY-014-01: cannot parse frontmatter is a config error → exit(2)\n stderr(`cleargate doctor --pricing: cannot parse frontmatter in: ${absPath}`);\n if (outcome) outcome.configError = true;\n exit(2);\n return;\n }\n\n const draftTokensRaw = fm['draft_tokens'];\n if (!draftTokensRaw) {\n // STORY-014-01: draft_tokens unpopulated is a blocker (content exists, state incomplete) → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let draftTokens: DraftTokensInput & { model: string | null };\n if (typeof draftTokensRaw === 'object' && !Array.isArray(draftTokensRaw)) {\n draftTokens = draftTokensRaw as DraftTokensInput & { model: string | null };\n } else if (typeof draftTokensRaw === 'string') {\n try {\n draftTokens = JSON.parse(draftTokensRaw) as DraftTokensInput & { model: string | null };\n } catch {\n // STORY-014-01: unparseable draft_tokens is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n } else {\n // STORY-014-01: unexpected type is a blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n // Check if tokens are actually populated\n if (\n draftTokens.input === null &&\n draftTokens.output === null &&\n draftTokens.cache_read === null &&\n draftTokens.cache_creation === null\n ) {\n // STORY-014-01: all null values → blocker → exit(1)\n stdout('draft_tokens unpopulated — run cleargate stamp-tokens first');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n const { usd, unknownModel } = computeUsd(draftTokens);\n const model = draftTokens.model ?? 'unknown';\n\n if (unknownModel) {\n stderr(`cleargate doctor --pricing: unknown model '${model}' — no pricing data available`);\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n const fileName = path.basename(absPath);\n\n stdout(\n `${fileName}: ${model} — input:${input} output:${output} cache_read:${cacheRead} cache_creation:${cacheCreation} ≈ $${usd.toFixed(4)}`\n );\n}\n\n// ─── Can-edit mode (CR-008 Phase B) ──────────────────────────────────────────\n\n/**\n * CR-008: reasons why an edit would be blocked.\n */\nexport type CanEditBlockReason = 'no_approved_stories' | 'file_not_in_implementation_files';\n\n/**\n * CR-008: result of the can-edit check.\n */\nexport interface CanEditResult {\n allowed: boolean;\n reason?: CanEditBlockReason;\n}\n\n/**\n * CR-008: simple glob-style match.\n * Supports `*` (any characters except `/`) and `**` (any characters including `/`).\n */\nexport function globMatch(pattern: string, filePath: string): boolean {\n // Normalise separators\n const normalPattern = pattern.replace(/\\\\/g, '/');\n const normalFile = filePath.replace(/\\\\/g, '/');\n\n // Escape regex specials except * and ?\n const regexStr = normalPattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '\u0000') // placeholder for **\n .replace(/\\*/g, '[^/]*')\n .replace(/\u0000/g, '.*');\n\n const re = new RegExp(`^${regexStr}$`);\n return re.test(normalFile);\n}\n\n/**\n * CR-008: check whether editing `filePath` is permitted.\n *\n * Logic:\n * 1. If sprint-active sentinel exists → always allowed.\n * 2. Read pending-sync/*.md; for each with approved: true:\n * a. If no implementation_files field → treat as \"any approved story → allow\".\n * b. If implementation_files present → glob-match filePath against each pattern.\n * 3. If zero approved stories → block with reason 'no_approved_stories'.\n * 4. If approved stories exist but filePath not covered → block with 'file_not_in_implementation_files'.\n */\nexport async function runCanEdit(\n filePath: string,\n cwd: string,\n stdout: (s: string) => void,\n exit: (code: number) => never,\n outcome?: DoctorOutcome\n): Promise<void> {\n // Sprint-active sentinel → always allow\n const activeSentinel = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n if (fs.existsSync(activeSentinel)) {\n stdout('allowed: sprint active');\n return;\n }\n\n const pendingSyncDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n\n let files: string[];\n try {\n files = fs\n .readdirSync(pendingSyncDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => path.join(pendingSyncDir, f));\n } catch {\n // No pending-sync dir → no approved stories → blocker → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n let hasApprovedStory = false;\n let coveredByStory = false;\n\n for (const storyPath of files) {\n let raw: string;\n try {\n raw = fs.readFileSync(storyPath, 'utf-8');\n } catch {\n continue;\n }\n\n if (!raw.trimStart().startsWith('---')) continue;\n\n let fm: Record<string, unknown>;\n try {\n fm = parseFrontmatter(raw).fm;\n } catch {\n continue;\n }\n\n if (fm['approved'] !== true) continue;\n\n hasApprovedStory = true;\n\n const implFilesRaw = fm['implementation_files'];\n if (implFilesRaw === undefined || implFilesRaw === null) {\n // No implementation_files field → any approved story covers any file\n coveredByStory = true;\n break;\n }\n\n if (Array.isArray(implFilesRaw)) {\n for (const pattern of implFilesRaw) {\n if (typeof pattern !== 'string') continue;\n if (globMatch(pattern, filePath)) {\n coveredByStory = true;\n break;\n }\n }\n }\n\n if (coveredByStory) break;\n }\n\n if (!hasApprovedStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: no_approved_stories');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n if (!coveredByStory) {\n // STORY-014-01: blocked items → exit(1)\n stdout('blocked: file_not_in_implementation_files');\n if (outcome) outcome.blocker = true;\n exit(1);\n return;\n }\n\n stdout('allowed');\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function doctorHandler(\n flags: DoctorFlags,\n cli?: DoctorCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n\n // STORY-014-01: outcome accumulator — each mode pushes booleans here.\n // At the end, we exit per §3.2 pseudocode:\n // configError → exit(2), blocker → exit(1), else → exit(0).\n const outcome: DoctorOutcome = { configError: false, blocker: false };\n\n // Track whether exit() was already called by a mode (e.g. runPricing early error)\n // so we don't double-exit from the final outcome block.\n let exitedEarly = false;\n const wrappedExit = (code: number): never => {\n exitedEarly = true;\n return exit(code);\n };\n\n let mode: DoctorMode;\n try {\n mode = selectMode(flags);\n } catch (err) {\n // STORY-014-01: mutually exclusive flags is a config error → exit(2)\n stderr((err as Error).message);\n exit(2);\n return;\n }\n\n switch (mode) {\n case 'check-scaffold':\n await runCheckScaffold(flags, cli ?? {}, cwd, now, stdout, stderr);\n break;\n\n case 'hook-health':\n runHookHealth(stdout, cwd, now, outcome);\n break;\n\n case 'session-start':\n await runSessionStart(cwd, stdout, outcome, cli);\n break;\n\n case 'pricing':\n await runPricing(flags.pricingFile ?? '', cwd, stdout, stderr, wrappedExit, outcome);\n break;\n\n case 'can-edit':\n await runCanEdit(flags.canEditFile ?? '', cwd, stdout, wrappedExit, outcome);\n break;\n\n default: {\n const exhaustiveCheck: never = mode;\n stderr(`cleargate doctor: unknown mode '${String(exhaustiveCheck)}'`);\n // STORY-014-01: unknown mode is a config error → exit(2)\n exit(2);\n return;\n }\n }\n\n // STORY-014-01: §3.2 pseudocode exit-code computation.\n // If a mode called exit() early (e.g. runPricing on bad input), don't double-exit.\n if (exitedEarly) return;\n\n if (outcome.configError) {\n exit(2);\n } else if (outcome.blocker) {\n exit(1);\n } else {\n exit(0);\n }\n}\n","/**\n * pricing.ts — STORY-008-06\n *\n * USD pricing table for Claude models (per 1M tokens).\n * Numbers from Anthropic public pricing as of 2026-04-19.\n * No network. No config file. Numbers live in source.\n */\n\nexport interface ModelPricing {\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n}\n\n/**\n * Pricing table: USD per 1,000,000 tokens.\n *\n * claude-opus-4-7: $15 input / $75 output / $1.50 cache_read / $18.75 cache_creation\n * claude-sonnet-4-5: $3 input / $15 output / $0.30 cache_read / $3.75 cache_creation\n * claude-haiku-4-5: $0.80 input / $4 output / $0.08 cache_read / $1 cache_creation\n */\nexport const PRICING_TABLE: Record<string, ModelPricing> = {\n 'claude-opus-4-7': {\n input: 15.0,\n output: 75.0,\n cache_read: 1.5,\n cache_creation: 18.75,\n },\n 'claude-sonnet-4-5': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-sonnet-4-6': {\n input: 3.0,\n output: 15.0,\n cache_read: 0.3,\n cache_creation: 3.75,\n },\n 'claude-haiku-4-5': {\n input: 0.8,\n output: 4.0,\n cache_read: 0.08,\n cache_creation: 1.0,\n },\n};\n\nexport interface DraftTokensInput {\n input: number | null;\n output: number | null;\n cache_read: number | null;\n cache_creation: number | null;\n model: string | null;\n}\n\nexport interface ComputeUsdResult {\n usd: number;\n unknownModel: boolean;\n}\n\n/**\n * Compute USD cost from draft_tokens and model.\n *\n * If modelOverride is provided, it takes precedence over draftTokens.model.\n * Unknown model → {usd: 0, unknownModel: true}.\n * All token counts default to 0 if null.\n */\nexport function computeUsd(\n draftTokens: DraftTokensInput,\n modelOverride?: string\n): ComputeUsdResult {\n const model = modelOverride ?? draftTokens.model ?? '';\n const pricing = PRICING_TABLE[model];\n\n if (!pricing) {\n return { usd: 0, unknownModel: true };\n }\n\n const input = draftTokens.input ?? 0;\n const output = draftTokens.output ?? 0;\n const cacheRead = draftTokens.cache_read ?? 0;\n const cacheCreation = draftTokens.cache_creation ?? 0;\n\n const usd =\n (input * pricing.input +\n output * pricing.output +\n cacheRead * pricing.cache_read +\n cacheCreation * pricing.cache_creation) /\n 1_000_000;\n\n return { usd, unknownModel: false };\n}\n","/**\n * registry-check.ts — STORY-016-01\n *\n * Reusable library: checks the npm registry for the latest `cleargate` version,\n * caches the result for 24 hours at ~/.cleargate/update-check.json, honours\n * the CLEARGATE_NO_UPDATE_CHECK=1 opt-out env var, and is fully offline-silent.\n *\n * API contract is LOCKED — consumed verbatim by STORY-016-02 (doctor notifier).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type CheckSource = 'cache' | 'network' | 'opt-out' | 'error';\n\nexport interface CheckResult {\n latest: string | null;\n from: CheckSource;\n}\n\n/**\n * All four keys are test-seam injection points.\n * - `fetcher`: replaces globalThis.fetch for unit tests (no real network).\n * - `now`: replaces `new Date()` for deterministic 24h throttle tests.\n * - `cleargateHome`: replaces `~/.cleargate` for tmpdir-isolated tests.\n * - `env`: replaces `process.env` (captured at call-time, not module-load time).\n */\nexport interface CheckOptions {\n fetcher?: typeof fetch; // test-seam injection\n now?: () => Date; // test-seam for 24h throttle\n cleargateHome?: string; // test-seam for ~/.cleargate (mirrors doctor.ts seam at line 38)\n env?: NodeJS.ProcessEnv; // test-seam for CLEARGATE_NO_UPDATE_CHECK\n}\n\n// ─── Internal types ───────────────────────────────────────────────────────────\n\ninterface CacheFile {\n checked_at: string;\n latest_version: string;\n}\n\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst REGISTRY_URL = 'https://registry.npmjs.org/cleargate';\nconst TTL_MS = 24 * 60 * 60 * 1000; // 24 hours in milliseconds\nconst CACHE_FILENAME = 'update-check.json';\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Compare two semver strings of the form `\\d+\\.\\d+\\.\\d+`.\n * Returns -1 when a < b, 0 when equal, 1 when a > b.\n * Exported so STORY-016-02 (and later STORY-016-04) can import without adding\n * the `semver` npm package as a dependency.\n */\nexport function compareSemver(a: string, b: string): -1 | 0 | 1 {\n const parseVersion = (v: string): [number, number, number] => {\n const parts = v.split('.').map(Number);\n return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];\n };\n\n const [aMajor, aMinor, aPatch] = parseVersion(a);\n const [bMajor, bMinor, bPatch] = parseVersion(b);\n\n if (aMajor !== bMajor) return aMajor > bMajor ? 1 : -1;\n if (aMinor !== bMinor) return aMinor > bMinor ? 1 : -1;\n if (aPatch !== bPatch) return aPatch > bPatch ? 1 : -1;\n return 0;\n}\n\n/**\n * Resolve the cache file path from the cleargateHome option or default ~/.cleargate.\n */\nfunction resolveCachePath(cleargateHome?: string): string {\n const home = cleargateHome ?? path.join(os.homedir(), '.cleargate');\n return path.join(home, CACHE_FILENAME);\n}\n\n/**\n * Try to read and parse the cache file. Returns null on any error (missing,\n * malformed JSON, missing fields) — callers treat null as \"no cache.\"\n */\nfunction readCache(cachePath: string): CacheFile | null {\n try {\n const raw = fs.readFileSync(cachePath, 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'checked_at' in parsed &&\n 'latest_version' in parsed &&\n typeof (parsed as CacheFile).checked_at === 'string' &&\n typeof (parsed as CacheFile).latest_version === 'string'\n ) {\n return parsed as CacheFile;\n }\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Write (or overwrite) the cache file. Creates the parent directory if absent.\n * Silent on write failure — never throws.\n */\nfunction writeCache(cachePath: string, version: string): void {\n try {\n fs.mkdirSync(path.dirname(cachePath), { recursive: true });\n const entry: CacheFile = {\n checked_at: new Date().toISOString(),\n latest_version: version,\n };\n fs.writeFileSync(cachePath, JSON.stringify(entry, null, 2), 'utf-8');\n } catch {\n // Silently ignore write failures (read-only fs, permission issue, etc.)\n }\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Returns the latest published `cleargate` version (or a cached value, or null\n * on failure / opt-out). Never throws; never writes to stderr.\n *\n * Execution order:\n * 1. Opt-out check — if CLEARGATE_NO_UPDATE_CHECK=1, return immediately.\n * 2. Cache read — if cache is fresh (<24h), return from cache.\n * 3. Network fetch — hit the npm registry.\n * 4. On fetch success: write cache, return with `from:'network'`.\n * 5. On fetch failure: return cached version (or null) with `from:'error'`.\n */\nexport async function checkLatestVersion(opts?: CheckOptions): Promise<CheckResult> {\n // 1. Capture env at call-time (not module-load time) so tests can mutate env\n // between cases without leaking state.\n const env = opts?.env ?? process.env;\n if (env['CLEARGATE_NO_UPDATE_CHECK'] === '1') {\n return { latest: null, from: 'opt-out' };\n }\n\n const cachePath = resolveCachePath(opts?.cleargateHome);\n const nowMs = (opts?.now ? opts.now() : new Date()).getTime();\n\n // 2. Cache read + freshness check\n const cached = readCache(cachePath);\n if (cached !== null) {\n const age = nowMs - Date.parse(cached.checked_at);\n if (age < TTL_MS) {\n return { latest: cached.latest_version, from: 'cache' };\n }\n }\n\n // 3. Network fetch\n const fetcher = opts?.fetcher ?? globalThis.fetch;\n try {\n const response = await fetcher(REGISTRY_URL, {\n signal: AbortSignal.timeout(2000),\n });\n\n if (!response.ok) {\n // Non-2xx response — treat as error, fall back to cache\n return { latest: cached?.latest_version ?? null, from: 'error' };\n }\n\n // Parse only the dist-tags.latest field; ignore everything else so\n // registry-format drift (new fields, renamed keys) doesn't break us.\n const body: unknown = await response.json();\n if (\n typeof body !== 'object' ||\n body === null ||\n !('dist-tags' in body) ||\n typeof (body as Record<string, unknown>)['dist-tags'] !== 'object'\n ) {\n return { latest: cached?.latest_version ?? null, from: 'error' };\n }\n\n const distTags = (body as Record<string, Record<string, string>>)['dist-tags'];\n const latest = distTags['latest'];\n if (typeof latest !== 'string' || latest.trim() === '') {\n return { latest: cached?.latest_version ?? null, from: 'error' };\n }\n\n // 4. Write cache then return network result\n writeCache(cachePath, latest);\n return { latest, from: 'network' };\n } catch {\n // 5. Any thrown error (AbortError on timeout, ECONNREFUSED, etc.)\n // → fall back to cache silently; never re-throw; never write to stderr.\n return { latest: cached?.latest_version ?? null, from: 'error' };\n }\n}\n","/**\n * gate.ts — `cleargate gate check|explain|qa|arch` command handlers.\n *\n * STORY-008-03: Wires readiness-predicates.evaluate() + frontmatter-cache into\n * two Commander subcommands (check + explain).\n *\n * STORY-013-08: Extends with gate qa|arch subcommands that shell out via\n * run_script.sh to pre_gate_runner.sh. Both are v1-inert.\n *\n * FLASHCARD #cli #commander #optional-key: opts.transition may be undefined — strip key.\n * FLASHCARD #cli #determinism #test-seam: thread `now`, `exit`, `stdout`, `stderr` seams.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * Output is agent-facing: only ❌ / ⚠ / ✅ emoji; no ANSI color codes.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { evaluate } from '../lib/readiness-predicates.js';\nimport type { ParsedDoc } from '../lib/readiness-predicates.js';\nimport { readCachedGate, writeCachedGate } from '../lib/frontmatter-cache.js';\nimport type { CachedGate } from '../lib/frontmatter-cache.js';\nimport {\n detectWorkItemTypeFromFm,\n WORK_ITEM_TRANSITIONS,\n} from '../lib/work-item-type.js';\nimport type { WorkItemType } from '../lib/work-item-type.js';\nimport { toIsoSecond } from '../lib/frontmatter-yaml.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to readiness-gates.md (test seam). */\n gatesDocPath?: string;\n /** Override path to wiki index (test seam). */\n wikiIndexPath?: string;\n}\n\n// ─── Internal gate-block shape ────────────────────────────────────────────────\n\ninterface GateCriterion {\n id: string;\n check: string;\n}\n\ninterface GateBlock {\n work_item_type: string;\n transition: string;\n severity: 'advisory' | 'enforcing';\n criteria: GateCriterion[];\n}\n\n// ─── Gate document loader ─────────────────────────────────────────────────────\n\n/**\n * Load and parse all fenced ```yaml blocks from readiness-gates.md.\n * Each block's yaml.load() returns an array — unwrap [0] per FLASHCARD.\n */\nfunction loadGateBlocks(gatesDocPath: string): GateBlock[] {\n const raw = fs.readFileSync(gatesDocPath, 'utf8');\n const blocks: GateBlock[] = [];\n\n // Match all fenced ```yaml ... ``` blocks\n const fenceRe = /^```yaml\\n([\\s\\S]*?)^```/gm;\n let match: RegExpExecArray | null;\n while ((match = fenceRe.exec(raw)) !== null) {\n const yamlContent = match[1]!;\n const parsed = yaml.load(yamlContent);\n // Per FLASHCARD: readiness-gates.md fenced yaml blocks are YAML lists; unwrap [0]\n const block = Array.isArray(parsed) ? parsed[0] : parsed;\n if (\n block &&\n typeof block === 'object' &&\n 'work_item_type' in block &&\n 'transition' in block &&\n 'severity' in block &&\n 'criteria' in block\n ) {\n blocks.push(block as GateBlock);\n }\n }\n return blocks;\n}\n\n/**\n * Find the gate block matching a work-item type + transition.\n */\nfunction findGate(\n blocks: GateBlock[],\n type: WorkItemType,\n transition: string,\n): GateBlock | null {\n return blocks.find(\n (b) => b.work_item_type === type && b.transition === transition,\n ) ?? null;\n}\n\n/**\n * Infer the default transition for a work-item type given the current cached gate state.\n * - If cached_gate_result is absent or failing → return first transition.\n * - If cached_gate_result.pass === true and there's a next transition → return next.\n * - Otherwise return first transition.\n */\nfunction inferTransition(\n type: WorkItemType,\n cachedGate: CachedGate | null,\n): string {\n const transitions = WORK_ITEM_TRANSITIONS[type];\n if (!cachedGate || !cachedGate.pass) {\n return transitions[0]!;\n }\n // Find next unpassed transition\n // We don't know which transition was last checked from cache alone;\n // for Epic: if cached pass=true, assume first is done → pick second.\n // For types with only one transition: always return that one.\n if (transitions.length === 1) {\n return transitions[0]!;\n }\n // Multi-transition (Epic): if cached gate passes, infer next\n return transitions[1]!;\n}\n\n// ─── gateCheckHandler ─────────────────────────────────────────────────────────\n\nexport async function gateCheckHandler(\n file: string,\n opts: { verbose?: boolean; transition?: string },\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n const nowFn = cli?.now ?? (() => new Date());\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Parse the document\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch (err) {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Detect work-item type from frontmatter\n const detectedType = detectWorkItemTypeFromFm(fm);\n if (!detectedType) {\n stderrFn(`[cleargate gate] error: unable to detect work-item type from frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n // Load gates document\n const projectRoot = cwd;\n const gatesDocPath = cli?.gatesDocPath\n ?? path.join(projectRoot, '.cleargate', 'knowledge', 'readiness-gates.md');\n\n if (!fs.existsSync(gatesDocPath)) {\n stderrFn(`[cleargate gate] error: readiness-gates.md not found at: ${gatesDocPath}`);\n return exitFn(1);\n }\n\n let gateBlocks: GateBlock[];\n try {\n gateBlocks = loadGateBlocks(gatesDocPath);\n } catch (err) {\n stderrFn(`[cleargate gate] error: failed to parse readiness-gates.md: ${String(err)}`);\n return exitFn(1);\n }\n\n // Read current cached gate for transition inference\n const cachedGate = await readCachedGate(absPath);\n\n // Determine transition\n const transition = opts.transition ?? inferTransition(detectedType, cachedGate);\n\n // Find the matching gate\n const gate = findGate(gateBlocks, detectedType, transition);\n if (!gate) {\n stderrFn(\n `[cleargate gate] error: no gate definition found for ${detectedType}.${transition}`,\n );\n return exitFn(1);\n }\n\n const wikiIndexPath = cli?.wikiIndexPath;\n const parsedDoc: ParsedDoc = { fm, body, absPath };\n const evalOpts = { projectRoot, ...(wikiIndexPath ? { wikiIndexPath } : {}) };\n\n // Evaluate each criterion\n const failingCriteria: { id: string; detail: string }[] = [];\n const allResults: Array<{ id: string; pass: boolean; detail: string }> = [];\n\n for (const criterion of gate.criteria) {\n let result: { pass: boolean; detail: string };\n try {\n result = evaluate(criterion.check, parsedDoc, evalOpts);\n } catch (err) {\n result = { pass: false, detail: `predicate error: ${String(err)}` };\n }\n allResults.push({ id: criterion.id, ...result });\n if (!result.pass) {\n failingCriteria.push({ id: criterion.id, detail: result.detail });\n }\n }\n\n const overallPass = failingCriteria.length === 0;\n const lastGateCheck = toIsoSecond(nowFn());\n\n // Write cached gate result\n const cacheResult: CachedGate = {\n pass: overallPass,\n failing_criteria: failingCriteria,\n last_gate_check: lastGateCheck,\n };\n await writeCachedGate(absPath, cacheResult, { now: nowFn });\n\n // Format and emit output\n const isAdvisory = gate.severity === 'advisory';\n const headerLine = `Gate: ${detectedType}.${transition} (${gate.severity})`;\n stdoutFn(headerLine);\n\n if (overallPass) {\n stdoutFn(`\\u2705 ${detectedType}.${transition} passed (${gate.criteria.length} criteria)`);\n } else {\n for (const r of allResults) {\n if (!r.pass) {\n if (isAdvisory) {\n stdoutFn(`\\u26A0 ${r.id}: ${r.detail} (advisory)`);\n } else {\n stdoutFn(`\\u274C ${r.id}: ${r.detail}`);\n }\n }\n if (opts.verbose) {\n // In verbose mode, emit full detail per criterion\n stdoutFn(` [${r.pass ? 'pass' : 'fail'}] ${r.id}: ${r.detail}`);\n }\n }\n }\n\n // Severity-based exit routing\n if (!overallPass && !isAdvisory) {\n return exitFn(1);\n }\n // advisory or pass → exit 0 (implicit return)\n}\n\n// ─── gateExplainHandler ───────────────────────────────────────────────────────\n\nexport async function gateExplainHandler(\n file: string,\n cli?: GateCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n if (!fs.existsSync(absPath)) {\n stderrFn(`[cleargate gate] error: file not found: ${absPath}`);\n return exitFn(1);\n }\n\n // Read cached gate result — read-only, no evaluate calls\n const cached = await readCachedGate(absPath);\n\n if (!cached) {\n stdoutFn('no gate check cached; run: cleargate gate check <file>');\n return;\n }\n\n // Parse frontmatter to get type info (read-only — no writes)\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch {\n stderrFn(`[cleargate gate] error: cannot read file: ${absPath}`);\n return exitFn(1);\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n stderrFn(`[cleargate gate] error: cannot parse frontmatter in: ${absPath}`);\n return exitFn(1);\n }\n\n const detectedType = detectWorkItemTypeFromFm(fm) ?? 'unknown';\n\n // Render ≤50-LLM-token summary\n const failingIds = cached.failing_criteria.map((c) => c.id).join(', ');\n const statusStr = cached.pass ? 'pass' : 'fail';\n const summary = failingIds\n ? `${detectedType}: ${statusStr} at ${cached.last_gate_check}; ${cached.failing_criteria.length} failing: ${failingIds}`\n : `${detectedType}: ${statusStr} at ${cached.last_gate_check}`;\n\n stdoutFn(summary);\n}\n\n// ─── v2 gate qa|arch handlers ─────────────────────────────────────────────────\n\n/**\n * Options for v2 gate subcommands (qa + arch).\n * Extends GateCliOptions with execution-mode seams.\n */\nexport interface GateV2CliOptions extends GateCliOptions, ExecutionModeOptions {\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Sprint ID for execution_mode discovery. */\n sprintId?: string;\n}\n\nfunction resolveRunScriptForGate(opts: GateV2CliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * `cleargate gate qa <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh qa <worktree> <branch>`\n */\nexport function gateQaHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'qa', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate qa] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n/**\n * `cleargate gate arch <worktree> <branch>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh pre_gate_runner.sh arch <worktree> <branch>`\n */\nexport function gateArchHandler(\n opts: { worktree: string; branch: string },\n cli?: GateV2CliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const sprintId = cli?.sprintId ?? 'SPRINT-UNKNOWN';\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n const runScript = resolveRunScriptForGate(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'pre_gate_runner.sh', 'arch', opts.worktree, opts.branch],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate gate arch] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * execution-mode.ts — reads `execution_mode` from a Sprint Plan frontmatter.\n *\n * STORY-013-08: provides a test seam via `sprintFilePath` override so tests\n * can inject synthetic SPRINT-99.md fixture without touching live state.\n *\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests — keep exitFn\n * only at handler top-level (not inside helper functions).\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nconst V1_INERT_MESSAGE =\n 'v1 mode active — command inert. Set execution_mode: v2 in sprint frontmatter to enable.';\n\nexport type ExecutionMode = 'v1' | 'v2';\n\nexport interface ExecutionModeOptions {\n /** Absolute path to the sprint file. Overrides auto-discovery. */\n sprintFilePath?: string;\n /** Working directory for relative-path resolution. Defaults to process.cwd(). */\n cwd?: string;\n /**\n * When true and sprintId is absent or 'SPRINT-UNKNOWN', read\n * `.cleargate/sprint-runs/.active` for the sprint ID before falling through\n * to v1-inert. Callers can also use `resolveSprintIdFromSentinel` directly.\n */\n sentinelFallback?: boolean;\n}\n\n/**\n * Parse just the YAML frontmatter from a markdown file.\n * Returns the raw frontmatter block as a plain object.\n * On any parse failure, returns an empty object.\n */\nfunction parseFrontmatterSimple(raw: string): Record<string, unknown> {\n const match = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(raw);\n if (!match) return {};\n const block = match[1]!;\n const result: Record<string, unknown> = {};\n for (const line of block.split('\\n')) {\n const kv = /^([^:]+):\\s*(.*)$/.exec(line.trim());\n if (!kv) continue;\n const key = kv[1]!.trim();\n const val = kv[2]!.trim().replace(/^[\"']|[\"']$/g, '');\n result[key] = val;\n }\n return result;\n}\n\n/**\n * Discover the sprint file for a given sprint ID.\n * Looks in `.cleargate/delivery/pending-sync/SPRINT-{id}_*.md`\n * and `.cleargate/delivery/archive/SPRINT-{id}_*.md`.\n *\n * Returns null if no matching file is found.\n */\nfunction discoverSprintFile(sprintId: string, cwd: string): string | null {\n const searchDirs = [\n path.join(cwd, '.cleargate', 'delivery', 'pending-sync'),\n path.join(cwd, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of searchDirs) {\n if (!fs.existsSync(dir)) continue;\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n continue;\n }\n const prefix = `${sprintId}_`;\n for (const entry of entries) {\n if (entry.startsWith(prefix) && entry.endsWith('.md')) {\n return path.join(dir, entry);\n }\n // Also allow exact match like SPRINT-99.md (test fixtures)\n if (entry === `${sprintId}.md`) {\n return path.join(dir, entry);\n }\n }\n }\n\n return null;\n}\n\n/**\n * Read the active sprint ID from `.cleargate/sprint-runs/.active`.\n * Returns null if the file does not exist or is empty after trim.\n *\n * This is the primary API for sentinel-based sprint discovery. Callers that\n * need a fallback chain use:\n * const sprintId = argSprintId ?? resolveSprintIdFromSentinel(cwd);\n * const mode = readSprintExecutionMode(sprintId ?? 'SPRINT-UNKNOWN', { cwd });\n */\nexport function resolveSprintIdFromSentinel(cwd?: string): string | null {\n const resolvedCwd = cwd ?? process.cwd();\n const sentinelPath = path.join(resolvedCwd, '.cleargate', 'sprint-runs', '.active');\n try {\n const content = fs.readFileSync(sentinelPath, 'utf8').trim();\n return content.length > 0 ? content : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Read the `execution_mode` field from a sprint file.\n *\n * Resolution order:\n * 1. If `opts.sprintFilePath` is set, use that directly.\n * 2. Otherwise, discover the file by sprintId in `.cleargate/delivery/`.\n * 3. If `opts.sentinelFallback` is true and sprintId is absent or\n * 'SPRINT-UNKNOWN', read `.cleargate/sprint-runs/.active` and substitute.\n * 4. If no file found, return \"v1\" (safe default per §19.5).\n */\nexport function readSprintExecutionMode(\n sprintId: string,\n opts: ExecutionModeOptions = {},\n): ExecutionMode {\n const cwd = opts.cwd ?? process.cwd();\n\n // Sentinel fallback: when sprintId is absent or unknown, try .active\n let resolvedSprintId = sprintId;\n if (opts.sentinelFallback && (!resolvedSprintId || resolvedSprintId === 'SPRINT-UNKNOWN')) {\n const sentinelId = resolveSprintIdFromSentinel(cwd);\n if (sentinelId) {\n resolvedSprintId = sentinelId;\n }\n }\n\n let filePath: string | null = opts.sprintFilePath ?? null;\n if (!filePath) {\n filePath = discoverSprintFile(resolvedSprintId, cwd);\n }\n\n if (!filePath || !fs.existsSync(filePath)) {\n // Default to v1 — safe, no behavioral change (§19.5)\n return 'v1';\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(filePath, 'utf8');\n } catch {\n return 'v1';\n }\n\n const fm = parseFrontmatterSimple(raw);\n const mode = fm['execution_mode'];\n\n if (mode === 'v2') return 'v2';\n return 'v1';\n}\n\n/**\n * Print the v1-inert message and exit 0.\n * The caller is responsible for calling this when execution_mode is v1.\n */\nexport function printInertAndExit(\n stdoutFn: (s: string) => void,\n exitFn: (code: number) => never,\n): never {\n stdoutFn(V1_INERT_MESSAGE);\n return exitFn(0);\n}\n\nexport { V1_INERT_MESSAGE };\n","/**\n * STORY-008-02: Predicate evaluator for ClearGate readiness gates.\n * Supports exactly 6 closed-set predicate shapes. Any other shape throws.\n * Sandboxed: no shell-out, no network, read-only FS limited to projectRoot.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type ParsedPredicate =\n | { kind: 'frontmatter'; ref: string; field: string; op: '==' | '!=' | '>=' | '<='; value: string | number | boolean }\n | { kind: 'body-contains'; needle: string; negated: boolean }\n | { kind: 'marker-absence'; marker: 'TBD' | 'TODO' | 'FIXME' }\n | { kind: 'section'; index: number; count: { op: '>=' | '==' | '>'; n: number }; itemType: 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item' }\n | { kind: 'file-exists'; path: string }\n | { kind: 'link-target-exists'; id: string }\n | { kind: 'status-of'; id: string; value: string };\n\nexport interface ParsedDoc {\n fm: Record<string, unknown>;\n body: string;\n absPath: string;\n}\n\nexport interface EvalOptions {\n projectRoot?: string;\n wikiIndexPath?: string;\n}\n\n// ─── Parser ───────────────────────────────────────────────────────────────────\n\n/**\n * Parse a predicate string into a typed ParsedPredicate.\n * Throws with \"unsupported predicate shape: <src>\" on any unrecognized input.\n * Target: ≤150 LoC, hand-rolled tokenizer + switch on first token.\n */\nexport function parsePredicate(src: string): ParsedPredicate {\n const s = src.trim();\n\n // 1. frontmatter(<ref>).<field> <op> <value>\n const fmMatch = s.match(\n /^frontmatter\\(([^)]*)\\)\\.(\\w+)\\s*(==|!=|>=|<=)\\s*(.+)$/\n );\n if (fmMatch) {\n const ref = fmMatch[1]!.trim();\n if (ref === '') throw new Error(`unsupported predicate shape: ${src}`);\n const field = fmMatch[2]!;\n const op = fmMatch[3] as '==' | '!=' | '>=' | '<=';\n const rawVal = fmMatch[4]!.trim();\n const value = parseValue(rawVal);\n return { kind: 'frontmatter', ref, field, op, value };\n }\n\n // 2a. body does not contain marker '<id>' — new marker-absence shape\n const markerNotMatch = s.match(/^body does not contain marker ['\"]([A-Z]+)['\"]$/);\n if (markerNotMatch) {\n const marker = markerNotMatch[1]!;\n if (marker !== 'TBD' && marker !== 'TODO' && marker !== 'FIXME') {\n throw new Error(`unsupported predicate shape: ${src}`);\n }\n return { kind: 'marker-absence', marker };\n }\n\n // 2b. body does not contain '<needle>'\n const bodyNotMatch = s.match(/^body does not contain ['\"](.+)['\"]$/);\n if (bodyNotMatch) {\n return { kind: 'body-contains', needle: bodyNotMatch[1]!, negated: true };\n }\n\n // 2c. body contains '<needle>'\n const bodyMatch = s.match(/^body contains ['\"](.+)['\"]$/);\n if (bodyMatch) {\n return { kind: 'body-contains', needle: bodyMatch[1]!, negated: false };\n }\n\n // 3. section(<N>) has <count> <item-type>\n const sectionMatch = s.match(\n /^section\\((\\d+)\\) has (≥|>=|==|>)(\\d+) (checked-checkbox|unchecked-checkbox|listed-item)$/\n );\n if (sectionMatch) {\n const index = parseInt(sectionMatch[1]!, 10);\n const opChar = sectionMatch[2]!;\n const n = parseInt(sectionMatch[3]!, 10);\n const itemType = sectionMatch[4] as 'checked-checkbox' | 'unchecked-checkbox' | 'listed-item';\n let countOp: '>=' | '==' | '>';\n if (opChar === '≥' || opChar === '>=') countOp = '>=';\n else if (opChar === '>') countOp = '>';\n else countOp = '==';\n return { kind: 'section', index, count: { op: countOp, n }, itemType };\n }\n\n // 4. file-exists(<path>)\n const fileExistsMatch = s.match(/^file-exists\\((.+)\\)$/);\n if (fileExistsMatch) {\n const filePath = fileExistsMatch[1]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'file-exists', path: filePath };\n }\n\n // 5. link-target-exists([[ID]])\n const linkMatch = s.match(/^link-target-exists\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)$/);\n if (linkMatch) {\n return { kind: 'link-target-exists', id: linkMatch[1]! };\n }\n\n // 6. status-of([[ID]]) == <value>\n const statusMatch = s.match(/^status-of\\(\\[\\[([A-Z0-9\\-]+)\\]\\]\\)\\s*==\\s*(.+)$/);\n if (statusMatch) {\n const id = statusMatch[1]!;\n const value = statusMatch[2]!.trim().replace(/^['\"]|['\"]$/g, '');\n return { kind: 'status-of', id, value };\n }\n\n throw new Error(`unsupported predicate shape: ${src}`);\n}\n\n/** Parse a YAML scalar value string to string | number | boolean. */\nfunction parseValue(raw: string): string | number | boolean {\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n if (raw === 'null') return 'null'; // treat null as string \"null\" for comparison\n const num = Number(raw);\n if (!isNaN(num) && raw !== '') return num;\n // Strip quotes\n return raw.replace(/^['\"]|['\"]$/g, '');\n}\n\n// ─── Evaluator ────────────────────────────────────────────────────────────────\n\n/**\n * Evaluate a predicate string against a document. Returns {pass, detail}.\n * Throws on malformed predicate (delegate to parsePredicate).\n */\nexport function evaluate(\n predicate: string,\n doc: ParsedDoc,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const parsed = parsePredicate(predicate);\n const projectRoot = opts?.projectRoot ?? process.cwd();\n\n switch (parsed.kind) {\n case 'frontmatter':\n return evalFrontmatter(parsed, doc, projectRoot);\n case 'body-contains':\n return evalBodyContains(parsed, doc);\n case 'marker-absence':\n return evalMarkerAbsence(parsed, doc);\n case 'section':\n return evalSection(parsed, doc);\n case 'file-exists':\n return evalFileExists(parsed, projectRoot);\n case 'link-target-exists':\n return evalLinkTargetExists(parsed, opts);\n case 'status-of':\n return evalStatusOf(parsed, opts, projectRoot);\n }\n}\n\n// ─── Frontmatter evaluator ────────────────────────────────────────────────────\n\nfunction evalFrontmatter(\n parsed: Extract<ParsedPredicate, { kind: 'frontmatter' }>,\n doc: ParsedDoc,\n projectRoot: string\n): { pass: boolean; detail: string } {\n let fm: Record<string, unknown>;\n\n if (parsed.ref === '.') {\n fm = doc.fm;\n } else {\n // ref is a frontmatter key whose value is a path to another document\n const refVal = doc.fm[parsed.ref];\n if (refVal === undefined || refVal === null) {\n return {\n pass: false,\n detail: `frontmatter key '${parsed.ref}' is missing or null in ${doc.absPath}`,\n };\n }\n\n // Sub-fix #1 (BUG-008): prose-vs-path heuristic.\n // If the value looks like prose (contains a space, em-dash, en-dash, colon, parens,\n // newline, or exceeds 200 chars), it is not a file path. In that case, pass the gate\n // only when the parent document declares an explicit proposal-gate waiver via any of:\n // - proposal_gate_waiver: <truthy> — explicit opt-in waiver field\n // - approved_by: <non-empty> AND approved_at: <non-empty> — existing approval fields\n // A plain path like \"PROPOSAL-999.md\" (no spaces, ≤200 chars, no special prose chars)\n // still falls through to the existing resolveLinkedPath logic — preserving the R-08\n // regression guarantee that broken file references still fail.\n const refStr = String(refVal);\n const looksLikeProse =\n refStr.length > 200 ||\n /[ —–:()\\n]/.test(refStr); // space, em-dash, en-dash, colon, parens, newline\n if (looksLikeProse) {\n // Signal 1: explicit proposal_gate_waiver field\n const waiver = doc.fm['proposal_gate_waiver'];\n const hasExplicitWaiver =\n waiver !== null && waiver !== undefined && waiver !== false &&\n String(waiver).trim() !== '' && String(waiver).trim() !== 'false';\n // Signal 2: approved_by + approved_at both set (existing approval fields)\n const approvedBy = doc.fm['approved_by'];\n const approvedAt = doc.fm['approved_at'];\n const hasApprovalFields =\n approvedBy !== null && approvedBy !== undefined && String(approvedBy).trim() !== '' &&\n approvedAt !== null && approvedAt !== undefined && String(approvedAt).trim() !== '';\n const hasWaiver = hasExplicitWaiver || hasApprovalFields;\n if (hasWaiver) {\n return {\n pass: true,\n detail: `context_source is prose; proposal-gate waiver per frontmatter approved_by/approved_at`,\n };\n }\n return {\n pass: false,\n detail: `context_source is prose but no proposal_gate_waiver (approved_by + approved_at) found in frontmatter`,\n };\n }\n\n // Resolve the path\n const linkedPath = resolveLinkedPath(String(refVal), doc.absPath, projectRoot);\n if (!linkedPath) {\n return {\n pass: false,\n detail: `linked file not found: ${refVal}`,\n };\n }\n fm = readFrontmatterFromFile(linkedPath);\n }\n\n const actual = fm[parsed.field];\n\n // Compare\n const pass = compareValues(actual, parsed.op, parsed.value);\n const detail = pass\n ? `frontmatter(${parsed.ref}).${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)} → actual: ${JSON.stringify(actual)}`\n : `expected ${parsed.field} ${parsed.op} ${JSON.stringify(parsed.value)}, got ${JSON.stringify(actual)}`;\n\n return { pass, detail };\n}\n\nfunction compareValues(\n actual: unknown,\n op: '==' | '!=' | '>=' | '<=',\n expected: string | number | boolean\n): boolean {\n // null check for != null\n if (expected === 'null') {\n const isNull = actual === null || actual === undefined || actual === '' || actual === 'null';\n return op === '==' ? isNull : !isNull;\n }\n // Normalize actual: strip quotes\n let a: unknown = actual;\n if (typeof a === 'string') {\n a = a.replace(/^[\"']|[\"']$/g, '');\n // Try to coerce to bool/number for comparison\n if (a === 'true') a = true;\n else if (a === 'false') a = false;\n else {\n const n = Number(a);\n if (!isNaN(n) && (a as string) !== '') a = n;\n }\n }\n\n switch (op) {\n case '==': return a === expected || String(a) === String(expected);\n case '!=': return a !== expected && String(a) !== String(expected);\n case '>=': return Number(a) >= Number(expected);\n case '<=': return Number(a) <= Number(expected);\n }\n}\n\n/** Resolve a path reference relative to the document or project root. */\nfunction resolveLinkedPath(\n ref: string,\n docAbsPath: string,\n projectRoot: string\n): string | null {\n // Try relative to doc first, then relative to projectRoot\n const candidates = [\n path.resolve(path.dirname(docAbsPath), ref),\n path.resolve(projectRoot, ref),\n ];\n for (const candidate of candidates) {\n // Sandbox check\n if (!candidate.startsWith(projectRoot)) continue;\n if (fs.existsSync(candidate)) return candidate;\n }\n return null;\n}\n\n/** Read frontmatter from a file as a plain Record. Does not throw on body. */\nfunction readFrontmatterFromFile(absPath: string): Record<string, unknown> {\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return {};\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return {};\n const fmLines = lines.slice(1, closeIdx);\n const fm: Record<string, unknown> = {};\n for (const line of fmLines) {\n if (line.trim() === '' || line.trim().startsWith('#')) continue;\n const colon = line.indexOf(':');\n if (colon === -1) continue;\n const key = line.slice(0, colon).trim();\n const val = line.slice(colon + 1).trim();\n if (val === '' || val === '[]') { fm[key] = []; continue; }\n if (val.startsWith('{')) { fm[key] = val; continue; }\n if (val.startsWith('[') && val.endsWith(']')) {\n const inner = val.slice(1, -1).trim();\n fm[key] = inner === '' ? [] : inner.split(',').map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''));\n continue;\n }\n fm[key] = val.replace(/^[\"']|[\"']$/g, '');\n }\n return fm;\n } catch {\n return {};\n }\n}\n\n// ─── Body-contains evaluator ──────────────────────────────────────────────────\n\nfunction evalBodyContains(\n parsed: Extract<ParsedPredicate, { kind: 'body-contains' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const body = doc.body;\n const needle = parsed.needle;\n\n // Count occurrences and find section context\n let count = 0;\n let pos = 0;\n const sections: number[] = []; // 1-indexed section numbers for each occurrence\n const bodySections = body.split(/^## /m);\n\n // Simple occurrence count\n while ((pos = body.indexOf(needle, pos)) !== -1) {\n count++;\n // Find which section this occurrence is in\n const before = body.slice(0, pos);\n const sectionCount = (before.match(/^## /gm) || []).length;\n sections.push(sectionCount + 1); // 1-indexed\n pos += needle.length;\n }\n\n const present = count > 0;\n void bodySections; // suppress unused warning\n\n if (parsed.negated) {\n // \"body does not contain\" → pass when absent\n if (present) {\n const sectionList = [...new Set(sections)].map((s) => `§${s}`).join(', ');\n return {\n pass: false,\n detail: `${count} occurrence${count === 1 ? '' : 's'} at ${sectionList}`,\n };\n }\n return { pass: true, detail: `'${needle}' not found in body` };\n } else {\n // \"body contains\" → pass when present\n if (present) {\n return { pass: true, detail: `'${needle}' found ${count} time${count === 1 ? '' : 's'}` };\n }\n return { pass: false, detail: `'${needle}' not found in body` };\n }\n}\n\n// ─── Marker-absence evaluator ─────────────────────────────────────────────────\n\n/**\n * Sub-fix #2 (BUG-008): Evaluates \"body does not contain marker '<id>'\".\n * A marker is counted only when it appears in a syntactic role:\n * 1. Followed immediately by a colon: TBD: ...\n * 2. Wrapped in parens: (TBD) or square brackets: [TBD]\n * 3. The entire trimmed line equals the marker: bare TBD on its own line\n * 4. Preceded by a code-comment prefix: // TBD or # TBD\n *\n * NOT counted:\n * - TBD as part of another word (TBDs, TBDish, TBD's)\n * - TBD inside quotes in prose (\"TBD resolution\")\n * - Template self-reference lines: \"- [x] 0 \"TBDs\" exist\" / \"- [ ] 0 \"TBDs\" exist\"\n */\nfunction evalMarkerAbsence(\n parsed: Extract<ParsedPredicate, { kind: 'marker-absence' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n const { marker } = parsed;\n const lines = doc.body.split('\\n');\n\n // Template self-reference lines to exclude (BUG-008 spec: \"- [x] 0 \"TBDs\" exist\")\n const templateSelfRefRe = /^\\s*-\\s*\\[[x ]\\]\\s*0\\s*\"TBDs?\"\\s*exist/i;\n\n // Regex: marker in a syntactic role.\n // Matches: (MARKER) | [MARKER] | MARKER: | // MARKER | # MARKER | bare MARKER line\n // The (?<!\\w) and (?!\\w) prevent matching inside longer words.\n const markerRe = new RegExp(\n `(?:^|(?<=\\\\())${marker}(?=:)|` + // MARKER: (colon follows)\n `\\\\(${marker}\\\\)|` + // (MARKER) parens\n `\\\\[${marker}\\\\]|` + // [MARKER] square brackets\n `(?<=//\\\\s*)${marker}(?!\\\\w)|` + // // MARKER (comment)\n `(?<=#\\\\s*)${marker}(?!\\\\w)`, // # MARKER (comment)\n 'g'\n );\n\n // Also check bare-line: entire trimmed line is just the marker\n const bareLineRe = new RegExp(`^${marker}$`);\n\n const violations: number[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!;\n\n // Skip template self-reference boilerplate\n if (templateSelfRefRe.test(line)) continue;\n\n const trimmed = line.trim();\n\n // Check bare line\n if (bareLineRe.test(trimmed)) {\n violations.push(i + 1);\n continue;\n }\n\n // Check syntactic marker roles via regex\n markerRe.lastIndex = 0;\n if (markerRe.test(line)) {\n violations.push(i + 1);\n }\n }\n\n if (violations.length > 0) {\n return {\n pass: false,\n detail: `${violations.length} marker occurrence${violations.length === 1 ? '' : 's'} of '${marker}' at line${violations.length === 1 ? '' : 's'} ${violations.join(', ')}`,\n };\n }\n return { pass: true, detail: `no '${marker}' markers found in body` };\n}\n\n// ─── Section evaluator ────────────────────────────────────────────────────────\n\nfunction evalSection(\n parsed: Extract<ParsedPredicate, { kind: 'section' }>,\n doc: ParsedDoc\n): { pass: boolean; detail: string } {\n // Split body on ## headings (1-indexed).\n // Use lookahead so each part starts with \"## \" (or is preamble if body doesn't start with ##).\n const body = doc.body;\n\n const rawParts = body.split(/^(?=## )/m);\n // If body starts with \"## \", rawParts[0] = \"## Section 1\\n...\", rawParts[1] = \"## Section 2\\n...\", etc.\n // Section N is rawParts[N-1] (0-based array, 1-indexed sections).\n // If body has preamble before first ##, rawParts[0] = preamble (section 0), rawParts[1] = section 1, etc.\n\n // Detect if there is a preamble (content before first ##)\n const hasPreamble = rawParts.length > 0 && !rawParts[0]!.startsWith('## ');\n // Section N → rawParts index: with preamble, index = N; without, index = N - 1\n const arrayIndex = hasPreamble ? parsed.index : parsed.index - 1;\n const sectionContent = rawParts[arrayIndex];\n const totalSections = hasPreamble ? rawParts.length - 1 : rawParts.length;\n\n if (!sectionContent) {\n return {\n pass: false,\n detail: `section ${parsed.index} not found (body has ${totalSections} sections)`,\n };\n }\n\n let actualCount: number;\n switch (parsed.itemType) {\n case 'checked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[x\\]/gim) || []).length;\n break;\n case 'unchecked-checkbox':\n actualCount = (sectionContent.match(/^\\s*- \\[ \\]/gim) || []).length;\n break;\n case 'listed-item':\n actualCount = (sectionContent.match(/^\\s*- /gm) || []).length;\n break;\n }\n\n const pass = applyCountOp(actualCount, parsed.count.op, parsed.count.n);\n const opStr = parsed.count.op === '>=' ? '≥' : parsed.count.op;\n const detail = pass\n ? `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`\n : `section ${parsed.index} has ${actualCount} ${parsed.itemType} (${opStr}${parsed.count.n} required)`;\n\n return { pass, detail };\n}\n\nfunction applyCountOp(actual: number, op: '>=' | '==' | '>', n: number): boolean {\n switch (op) {\n case '>=': return actual >= n;\n case '==': return actual === n;\n case '>': return actual > n;\n }\n}\n\n// ─── File-exists evaluator ────────────────────────────────────────────────────\n\nfunction evalFileExists(\n parsed: Extract<ParsedPredicate, { kind: 'file-exists' }>,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const resolved = path.resolve(projectRoot, parsed.path);\n\n // Sandbox check: must be inside projectRoot\n if (!resolved.startsWith(projectRoot + path.sep) && resolved !== projectRoot) {\n return {\n pass: false,\n detail: `path '${parsed.path}' resolves outside project root (sandbox violation)`,\n };\n }\n\n const exists = fs.existsSync(resolved);\n return {\n pass: exists,\n detail: exists ? `${parsed.path} exists` : `${parsed.path} not found`,\n };\n}\n\n// ─── Link-target-exists evaluator ────────────────────────────────────────────\n\nfunction evalLinkTargetExists(\n parsed: Extract<ParsedPredicate, { kind: 'link-target-exists' }>,\n opts?: EvalOptions\n): { pass: boolean; detail: string } {\n const projectRoot = opts?.projectRoot ?? process.cwd();\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n const found = indexContent.includes(`[[${parsed.id}]]`);\n return {\n pass: found,\n detail: found\n ? `[[${parsed.id}]] found in wiki index`\n : `[[${parsed.id}]] not found in wiki index`,\n };\n}\n\n// ─── Status-of evaluator ─────────────────────────────────────────────────────\n\nfunction evalStatusOf(\n parsed: Extract<ParsedPredicate, { kind: 'status-of' }>,\n opts: EvalOptions | undefined,\n projectRoot: string\n): { pass: boolean; detail: string } {\n const wikiIndexPath =\n opts?.wikiIndexPath ?? path.join(projectRoot, '.cleargate', 'wiki', 'index.md');\n\n // Sandbox check\n if (!wikiIndexPath.startsWith(projectRoot)) {\n return { pass: false, detail: 'wikiIndexPath resolves outside project root' };\n }\n\n let indexContent: string;\n try {\n indexContent = fs.readFileSync(wikiIndexPath, 'utf8');\n } catch {\n return { pass: false, detail: `wiki index not found at ${wikiIndexPath}` };\n }\n\n // Find the raw path for this ID in the wiki index\n // Wiki index format: | [[STORY-003-13]] | story | Draft | .cleargate/delivery/... |\n const rowMatch = indexContent.match(\n new RegExp(`\\\\[\\\\[${parsed.id}\\\\]\\\\]\\\\s*\\\\|[^|]+\\\\|[^|]+\\\\|\\\\s*([^|\\\\n]+)`)\n );\n if (!rowMatch) {\n return { pass: false, detail: `[[${parsed.id}]] not found in wiki index` };\n }\n\n const rawPath = rowMatch[1]!.trim();\n const fullPath = path.resolve(projectRoot, rawPath);\n\n // Sandbox check\n if (!fullPath.startsWith(projectRoot)) {\n return { pass: false, detail: `wiki path for ${parsed.id} resolves outside project root` };\n }\n\n const linkedFm = readFrontmatterFromFile(fullPath);\n const status = linkedFm['status'];\n if (status === undefined) {\n return { pass: false, detail: `[[${parsed.id}]] has no status field` };\n }\n\n const pass = String(status).replace(/^[\"']|[\"']$/g, '') === parsed.value;\n return {\n pass,\n detail: pass\n ? `status-of([[${parsed.id}]]) == ${parsed.value}`\n : `status-of([[${parsed.id}]]) is '${status}', expected '${parsed.value}'`,\n };\n}\n","/**\n * STORY-008-02: Idempotent cached_gate_result frontmatter writer.\n * Reuses the shared frontmatter serializer from frontmatter-yaml.ts.\n *\n * writeCachedGate is byte-identical on re-run with identical inputs (same now + same result).\n *\n * Post-BUG-001: cached_gate_result is stored as a native YAML mapping\n * (parseFrontmatter returns it as an object). Legacy flow-style strings are\n * still accepted on read for backwards-compat with old files.\n */\n\nimport * as fs from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from './frontmatter-yaml.js';\n\nexport interface CachedGate {\n pass: boolean;\n failing_criteria: { id: string; detail: string }[];\n last_gate_check: string;\n}\n\n/**\n * Read the cached_gate_result from a file's frontmatter.\n * Returns null if the key is absent or the file has no valid frontmatter.\n */\nexport async function readCachedGate(absPath: string): Promise<CachedGate | null> {\n let raw: string;\n try {\n raw = await fs.readFile(absPath, 'utf8');\n } catch {\n return null;\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n return null;\n }\n\n return coerceCachedGate(fm['cached_gate_result']);\n}\n\n/**\n * Write (or update) cached_gate_result in a file's frontmatter.\n * Idempotent: if the result deep-equals the existing cached value AND the same\n * now() is supplied, the file bytes are left untouched.\n *\n * @param absPath Absolute path to the markdown file.\n * @param result The gate result to cache.\n * @param opts Optional: inject `now` for test determinism.\n */\nexport async function writeCachedGate(\n absPath: string,\n result: CachedGate,\n opts?: { now?: () => Date }\n): Promise<void> {\n const nowFn = opts?.now ?? (() => new Date());\n const lastGateCheck = result.last_gate_check || toIsoSecond(nowFn());\n\n const newResult: CachedGate = {\n pass: result.pass,\n failing_criteria: result.failing_criteria,\n last_gate_check: lastGateCheck,\n };\n\n const raw = await fs.readFile(absPath, 'utf8');\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(raw));\n } catch {\n throw new Error(`writeCachedGate: failed to parse frontmatter in ${absPath}`);\n }\n\n // Idempotency check: compare existing cached_gate_result\n const existing = coerceCachedGate(fm['cached_gate_result']);\n if (existing && JSON.stringify(existing) === JSON.stringify(newResult)) {\n return;\n }\n\n // Build new frontmatter: preserve all existing keys, inject/update cached_gate_result\n const newFm: Record<string, unknown> = {};\n let inserted = false;\n for (const [k, v] of Object.entries(fm)) {\n if (k === 'cached_gate_result') {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n inserted = true;\n } else {\n newFm[k] = v;\n }\n }\n if (!inserted) {\n newFm['cached_gate_result'] = newResult as unknown as Record<string, unknown>;\n }\n\n const fmBlock = serializeFrontmatter(newFm);\n const newContent = body.length > 0 ? `${fmBlock}\\n\\n${body}` : `${fmBlock}\\n`;\n\n await fs.writeFile(absPath, newContent, 'utf8');\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Coerce a frontmatter value (native object or legacy flow-style string) into\n * a CachedGate. Returns null if absent or unrecognizable.\n */\nfunction coerceCachedGate(val: unknown): CachedGate | null {\n if (val === undefined || val === null) return null;\n\n // Native object (current format)\n if (typeof val === 'object' && !Array.isArray(val)) {\n const c = val as Record<string, unknown>;\n return {\n pass: Boolean(c['pass']),\n failing_criteria: Array.isArray(c['failing_criteria'])\n ? (c['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(c['last_gate_check'] ?? ''),\n };\n }\n\n // Legacy flow-style string \"{pass: true, ...}\" — parse via js-yaml\n if (typeof val === 'string' && val.startsWith('{')) {\n try {\n const parsed = yaml.load(val, { schema: yaml.CORE_SCHEMA });\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const p = parsed as Record<string, unknown>;\n return {\n pass: Boolean(p['pass']),\n failing_criteria: Array.isArray(p['failing_criteria'])\n ? (p['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(p['last_gate_check'] ?? ''),\n };\n } catch {\n return null;\n }\n }\n\n return null;\n}\n","/**\n * gate-run.ts — `cleargate gate <name>` command handler.\n *\n * STORY-018-03: Config-driven gates. Runs a shell command configured in\n * `.cleargate/config.yml` under `gates.<name>`. Known gate names:\n * precommit, test, typecheck, lint.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exit seam throws in tests; extract logic\n * into value-returning internal fn, call exitFn only at handler top-level.\n * FLASHCARD #cli #commander #optional-key: opts.strict undefined must be\n * checked with === true, not truthy check.\n */\n\nimport { spawnSync } from 'node:child_process';\nimport { loadWikiConfig } from '../lib/wiki-config.js';\nimport type { WikiConfig } from '../lib/wiki-config.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface GateRunCliOptions {\n cwd?: string;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n spawnFn?: typeof spawnSync;\n configLoader?: (repoRoot: string) => WikiConfig;\n}\n\nconst KNOWN_GATES = ['precommit', 'test', 'typecheck', 'lint'] as const;\ntype KnownGate = typeof KNOWN_GATES[number];\n\n// ─── gateRunHandler ───────────────────────────────────────────────────────────\n\n/**\n * Handle `cleargate gate <name>`.\n *\n * - Validates name is in KNOWN_GATES (exit 2 if not).\n * - Loads config via configLoader (defaults to loadWikiConfig from cwd).\n * - If gate not configured: friendly message + exit 0 (default) or exit 1 (--strict).\n * - If configured: spawnSync with shell:true, propagate exit code.\n */\nexport function gateRunHandler(\n name: string,\n opts: { strict?: boolean },\n cli?: GateRunCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never =\n cli?.exit ?? ((code: number) => process.exit(code) as never);\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n const configLoaderFn = cli?.configLoader ?? loadWikiConfig;\n\n // Validate gate name\n if (!(KNOWN_GATES as readonly string[]).includes(name)) {\n stderrFn(\n `unknown gate name '${name}' — must be one of: precommit, test, typecheck, lint`,\n );\n return exitFn(2);\n }\n\n // Load config\n const config = configLoaderFn(cwd);\n const cmd = config.gates[name as KnownGate];\n\n if (cmd == null) {\n const msg = `gate \"${name}\" not configured — add gates.${name} to .cleargate/config.yml (see cleargate-planning/.cleargate/config.example.yml)`;\n if (opts.strict === true) {\n stderrFn(msg);\n return exitFn(1);\n } else {\n stdoutFn(msg);\n return exitFn(0);\n }\n }\n\n // Run the configured command\n const result = spawnFn(cmd, { shell: true, stdio: 'inherit', cwd });\n\n if (result.error) {\n stderrFn(`[cleargate gate ${name}] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n return exitFn(result.status ?? 0);\n}\n","/**\n * sprint.ts — `cleargate sprint init|close|archive` command handlers.\n *\n * STORY-013-08: CLI wrappers for sprint lifecycle scripts.\n * STORY-014-08: sprintArchiveHandler — final sprint close-out.\n * STORY-015-04: stampSprintClose + rollback on wiki build/lint failure.\n * CR-017: lifecycle reconciliation gate + decomposition gate at sprint init.\n *\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh\n * or orchestrate filesystem + git operations directly.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly —\n * always route through `run_script.sh`.\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync, execSync } from 'node:child_process';\nimport yaml from 'js-yaml';\nimport {\n readSprintExecutionMode,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\nimport { wikiBuildHandler } from './wiki-build.js';\nimport { wikiLintHandler } from './wiki-lint.js';\nimport {\n reconcileLifecycle,\n reconcileDecomposition,\n checkVerbMismatch,\n} from '../lib/lifecycle-reconcile.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\n\n// Terminal statuses — re-declared locally to avoid cross-module runtime import.\n// Keep in sync with TERMINAL_STATUSES in wiki-build.ts.\nconst TERMINAL_STATUSES = new Set(['Completed', 'Done', 'Abandoned', 'Closed', 'Resolved']);\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface SprintCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /** Test seam: override wiki build invocation. Defaults to wikiBuildHandler. */\n wikiBuildFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n /** Test seam: override wiki lint invocation. Defaults to wikiLintHandler. */\n wikiLintFn?: (cwd: string, stdout: (s: string) => void) => Promise<void>;\n /**\n * CR-017: --allow-drift flag.\n * When true at sprint init, lifecycle drift is warned but does not block.\n * Does NOT waive the decomposition gate (decomposition is always block-by-default).\n */\n allowDrift?: boolean;\n /**\n * CR-017 test seam: override git runner for reconcileLifecycle.\n * Used in tests to inject fake git log output.\n */\n gitRunner?: (cmd: string, args: string[]) => string;\n}\n\n// ─── Shared run_script.sh resolution ─────────────────────────────────────────\n\nfunction resolveRunScript(opts: SprintCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\n// ─── sprintInitHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint init <sprint-id> --stories <csv>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh init_sprint.mjs <sprint-id> --stories <csv>`\n *\n * CR-017: before shelling out, run two gates (v2 only):\n * 1. reconcileLifecycle — warn-only in v1-mode sprint; block when lifecycle_init_mode === \"block\"\n * 2. reconcileDecomposition — block-by-default; no --allow-drift waiver\n */\nexport function sprintInitHandler(\n opts: { sprintId: string; stories: string; allowDrift?: boolean },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n const allowDrift = opts.allowDrift ?? cli?.allowDrift ?? false;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── CR-017 Gate 1: Lifecycle Reconciliation ───────────────────────────────\n // Runs BEFORE init_sprint.mjs to prevent state.json mutation on gate failure.\n // warn-only in v1-like sprint (lifecycle_init_mode === \"warn\"), block in \"block\" mode.\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n\n // Find the sprint plan file to read lifecycle_init_mode\n let lifecycleInitMode: 'warn' | 'block' = 'warn'; // default: warn-only\n let sprintPlanPath: string | null = null;\n const pendingDir = path.join(deliveryRoot, 'pending-sync');\n\n try {\n const entries = fs.readdirSync(pendingDir);\n const sprintFile = entries.find(\n (e) => (e.startsWith(`${opts.sprintId}_`) || e === `${opts.sprintId}.md`) && e.endsWith('.md'),\n );\n if (sprintFile) {\n sprintPlanPath = path.join(pendingDir, sprintFile);\n const raw = fs.readFileSync(sprintPlanPath, 'utf8');\n const { fm } = parseFileFrontmatter(raw);\n if (fm['lifecycle_init_mode'] === 'block') {\n lifecycleInitMode = 'block';\n }\n }\n } catch {\n // If we can't read the sprint file, default to warn-only\n }\n\n // Run lifecycle reconciliation: look back 30 days as a reasonable prior-sprint window\n const since = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);\n const until = new Date();\n\n try {\n const lifecycleResult = reconcileLifecycle({\n since,\n until,\n deliveryRoot,\n repoRoot: cwd,\n gitRunner: cli?.gitRunner,\n });\n\n if (lifecycleResult.drift.length > 0) {\n // Emit punch list\n stderrFn('[cleargate sprint init] lifecycle reconciliation: unreconciled artifacts found:');\n for (const item of lifecycleResult.drift) {\n const warnTag = checkVerbMismatch(\n item.commit_shas.length > 0 ? '' : '',\n item.type,\n );\n stderrFn(\n ` DRIFT: ${item.id} status=${item.actual_status ?? 'missing'} in ${item.in_archive ? 'archive' : 'pending-sync'}, expected ${item.expected_status}` +\n ` (commit ${item.commit_shas[0] ?? 'unknown'})`,\n );\n if (warnTag) stderrFn(` WARN: ${warnTag}`);\n }\n\n if (allowDrift) {\n // --allow-drift passed: record waiver in context_source (best-effort)\n stderrFn('[cleargate sprint init] lifecycle drift waived via --allow-drift flag');\n if (sprintPlanPath) {\n try {\n const rawSprint = fs.readFileSync(sprintPlanPath, 'utf8');\n const { fm, body } = parseFileFrontmatter(rawSprint);\n const waiverLine = `lifecycle waiver: ${new Date().toISOString().split('T')[0]} for ${lifecycleResult.drift.map((d) => d.id).join(', ')}`;\n const currentContextSource = typeof fm['context_source'] === 'string'\n ? fm['context_source']\n : '';\n fm['context_source'] = currentContextSource\n ? `${currentContextSource}\\n${waiverLine}`\n : waiverLine;\n atomicWriteStr(sprintPlanPath, serializeFileContent(fm, body));\n } catch {\n // best-effort; don't block on write failure\n }\n }\n } else if (lifecycleInitMode === 'block') {\n stderrFn(\n '[cleargate sprint init] lifecycle drift blocks sprint activation (lifecycle_init_mode: block)',\n );\n stderrFn(' To waive: pass --allow-drift flag. To fix: archive artifacts and set status to terminal.');\n return exitFn(1);\n } else {\n // warn-only mode: print warning but proceed\n stderrFn('[cleargate sprint init] WARNING: lifecycle drift detected (warn-only mode — proceeding)');\n stderrFn(' Set lifecycle_init_mode: block in sprint frontmatter for SPRINT-16+ to enforce blocking.');\n }\n }\n } catch {\n // Lifecycle gate failure should not block sprint init — log and proceed\n stderrFn('[cleargate sprint init] lifecycle reconciliation unavailable (proceeding without gate)');\n }\n\n // ── CR-017 Gate 2: Decomposition Gate ────────────────────────────────────\n // Block-by-default; no --allow-drift waiver.\n if (sprintPlanPath) {\n try {\n const decompResult = reconcileDecomposition({\n sprintPlanPath,\n deliveryRoot,\n });\n\n if (decompResult.missing.length > 0) {\n if (allowDrift) {\n // --allow-drift does NOT waive decomposition gate\n stderrFn('decomposition gate cannot be waived; complete the decomposition or push start_date.');\n } else {\n stderrFn('[cleargate sprint init] decomposition gate: missing decompositions:');\n }\n for (const item of decompResult.missing) {\n stderrFn(` MISSING: ${item.id} (${item.type}) — ${item.reason}`);\n for (const f of item.expected_files) {\n stderrFn(` expected: ${f}`);\n }\n }\n return exitFn(1);\n }\n } catch {\n // If we can't run decomposition check (e.g., no sprint plan), skip gate\n stderrFn('[cleargate sprint init] decomposition gate unavailable (proceeding without check)');\n }\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['init_sprint.mjs', opts.sprintId, '--stories', opts.stories];\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint init] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n if (code === 0) { stdoutFn('→ Load skill: sprint-execution'); }\n return exitFn(code);\n}\n\n// ─── sprintCloseHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate sprint close <sprint-id> [--assume-ack]`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh close_sprint.mjs <sprint-id> [--assume-ack]`\n */\nexport function sprintCloseHandler(\n opts: { sprintId: string; assumeAck?: boolean },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const args = ['close_sprint.mjs', opts.sprintId];\n // FLASHCARD #cli #commander #optional-key: omit the key when undefined; only\n // append --assume-ack when the flag was explicitly set (opts.assumeAck === true).\n if (opts.assumeAck === true) {\n args.push('--assume-ack');\n }\n\n const result = spawnFn('bash', [runScript, ...args], { stdio: 'inherit' });\n\n if (result.error) {\n stderrFn(`[cleargate sprint close] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── reconcileLifecycleCliHandler ─────────────────────────────────────────────\n\n/**\n * CR-017: `cleargate sprint reconcile-lifecycle <sprint-id>`\n *\n * Pure wrapper around reconcileLifecycle. Used by close_sprint.mjs (Step 2.6)\n * to check lifecycle status at sprint close.\n *\n * Exits 0 if clean; exits 1 with punch list if drift found.\n * The sprint ID is used to derive the git date range from sprint frontmatter.\n * Falls back to last 90 days if frontmatter cannot be read.\n */\nexport function reconcileLifecycleCliHandler(\n opts: { sprintId: string; since?: string; until?: string },\n cli?: SprintCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const cwd = cli?.cwd ?? process.cwd();\n\n const deliveryRoot = path.join(cwd, '.cleargate', 'delivery');\n\n // Determine date range: from CLI flags or from sprint frontmatter\n let since: Date;\n let until: Date;\n\n if (opts.since) {\n since = new Date(opts.since);\n } else {\n // Try to read sprint start_date from frontmatter\n try {\n const pendingDir = path.join(deliveryRoot, 'pending-sync');\n const entries = fs.readdirSync(pendingDir);\n const sprintFile = entries.find(\n (e) => (e.startsWith(`${opts.sprintId}_`) || e === `${opts.sprintId}.md`) && e.endsWith('.md'),\n );\n if (sprintFile) {\n const raw = fs.readFileSync(path.join(pendingDir, sprintFile), 'utf8');\n const { fm } = parseFileFrontmatter(raw);\n const startDate = fm['start_date'];\n since = typeof startDate === 'string' ? new Date(startDate) : new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);\n } else {\n since = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);\n }\n } catch {\n since = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);\n }\n }\n\n until = opts.until ? new Date(opts.until) : new Date();\n\n try {\n const result = reconcileLifecycle({\n since,\n until,\n deliveryRoot,\n repoRoot: cwd,\n gitRunner: cli?.gitRunner,\n });\n\n if (result.drift.length === 0) {\n stdoutFn(`lifecycle: clean (${result.clean} artifacts reconciled)`);\n return exitFn(0);\n }\n\n stderrFn(`lifecycle: DRIFT detected (${result.drift.length} unreconciled artifacts):`);\n for (const item of result.drift) {\n stderrFn(\n ` DRIFT: ${item.id} status=${item.actual_status ?? 'missing'} in ${item.in_archive ? 'archive' : 'pending-sync'}, expected ${item.expected_status}` +\n ` (commit ${item.commit_shas[0] ?? 'unknown'})`,\n );\n stderrFn(\n ` Remediation: git mv .cleargate/delivery/pending-sync/${item.file_path?.replace('pending-sync/', '') ?? item.id + '_*.md'} .cleargate/delivery/archive/ && update status: ${item.expected_status}`,\n );\n }\n return exitFn(1);\n } catch (err) {\n stderrFn(`lifecycle reconciliation error: ${err instanceof Error ? err.message : String(err)}`);\n return exitFn(1);\n }\n}\n\n// ─── sprintArchiveHandler ─────────────────────────────────────────────────────\n\n/**\n * Parse just the frontmatter from a markdown file using js-yaml CORE_SCHEMA.\n * Returns { fm, body } where body is the content after the closing `---`.\n * FLASHCARD #cli #frontmatter #parse: body may start with blank line; we strip\n * one leading blank to match parseFrontmatter.ts convention.\n */\nfunction parseFileFrontmatter(raw: string): { fm: Record<string, unknown>; body: string } {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return { fm: {}, body: raw };\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return { fm: {}, body: raw };\n const yamlText = lines.slice(1, closeIdx).join('\\n');\n const bodyLines = lines.slice(closeIdx + 1);\n if (bodyLines[0] === '') bodyLines.shift();\n const body = bodyLines.join('\\n');\n if (yamlText.trim() === '') return { fm: {}, body };\n let parsed: unknown;\n try {\n parsed = yaml.load(yamlText, { schema: yaml.CORE_SCHEMA });\n } catch {\n return { fm: {}, body };\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return { fm: {}, body };\n return { fm: parsed as Record<string, unknown>, body };\n}\n\n/**\n * Serialize frontmatter + body back to a markdown string.\n * Always adds blank separator between `---` and body.\n */\nfunction serializeFileContent(fm: Record<string, unknown>, body: string): string {\n const yamlBody = yaml.dump(fm, {\n schema: yaml.CORE_SCHEMA,\n lineWidth: -1,\n noRefs: true,\n noCompatMode: true,\n quotingType: '\"',\n forceQuotes: false,\n });\n return `---\\n${yamlBody.replace(/\\n+$/, '')}\\n---\\n\\n${body}`;\n}\n\n/**\n * Atomic write via tmp + rename (same pattern as story.ts / close_sprint.mjs).\n */\nfunction atomicWriteStr(filePath: string, content: string): void {\n const tmp = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmp, content, 'utf8');\n fs.renameSync(tmp, filePath);\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranchForArchive(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Stamp a file's frontmatter with status + completed_at.\n * Returns the stamped content string (does NOT write to disk).\n */\nfunction stampFile(raw: string, status: string, completedAt: string): string {\n const { fm, body } = parseFileFrontmatter(raw);\n fm['status'] = status;\n fm['completed_at'] = completedAt;\n return serializeFileContent(fm, body);\n}\n\n/**\n * STORY-015-04: Stamp the sprint file's frontmatter with status=\"Completed\"\n * (if not already terminal) and completed_at (if absent). Writes atomically.\n *\n * Returns:\n * previousContent — original file bytes for rollback\n * stampedContent — the content written to disk\n * didChange — false if file was already terminal + completed_at set\n */\nexport function stampSprintClose(\n sprintPath: string,\n now: () => string,\n): { previousContent: string; stampedContent: string; didChange: boolean } {\n const previousContent = fs.readFileSync(sprintPath, 'utf8');\n const { fm, body } = parseFileFrontmatter(previousContent);\n\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const alreadyTerminal = TERMINAL_STATUSES.has(currentStatus);\n const hasCompletedAt = typeof fm['completed_at'] === 'string' && fm['completed_at'].length > 0;\n\n if (alreadyTerminal && hasCompletedAt) {\n return { previousContent, stampedContent: previousContent, didChange: false };\n }\n\n if (!alreadyTerminal) {\n fm['status'] = 'Completed';\n }\n if (!hasCompletedAt) {\n fm['completed_at'] = now();\n }\n\n const stampedContent = serializeFileContent(fm, body);\n atomicWriteStr(sprintPath, stampedContent);\n return { previousContent, stampedContent, didChange: true };\n}\n\n/**\n * STORY-015-04: Atomically restore a sprint file from a previously-snapshotted\n * string (rollback after wiki build/lint failure).\n */\nexport function restoreSprintFile(sprintPath: string, original: string): void {\n atomicWriteStr(sprintPath, original);\n}\n\n/** Return state.json story keys that belong to a given epic. */\nfunction storyKeysForEpic(\n stateStories: Record<string, unknown>,\n epicId: string,\n): string[] {\n const epicNum = epicId.replace('EPIC-', '');\n return Object.keys(stateStories).filter((k) => k.startsWith(`STORY-${epicNum}-`));\n}\n\n/**\n * `cleargate sprint archive <sprint-id> [--dry-run]`\n *\n * v1: print inert message, exit 0.\n * v2:\n * 1. Read state.json — refuse if sprint_status !== 'Completed'.\n * 2. Resolve file set from sprint frontmatter + state.json + orphan scan.\n * 3. --dry-run: print plan; no writes.\n * 4. Live: stamp sprint file, wiki build+lint (if wiki initialised), then\n * move files, clear .active, git checkout main, merge, branch -d.\n */\nexport async function sprintArchiveHandler(\n opts: { sprintId: string; dryRun?: boolean; allowWikiLintDebt?: boolean },\n cli?: SprintCliOptions,\n): Promise<void> {\n try {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Wiki build/lint seam wrappers.\n // wikiBuildHandler on success returns (no exit call); on failure calls exit(1) which throws.\n // wikiLintHandler on success calls exit(0) which throws; on findings calls exit(1) which throws.\n const wikiBuildFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiBuildFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-build-exit:${code}`); };\n try {\n await wikiBuildHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-build-exit:0')) return;\n throw err;\n }\n });\n const wikiLintFn: (wCwd: string, wStdout: (s: string) => void) => Promise<void> =\n cli?.wikiLintFn ??\n (async (wCwd: string, wStdout: (s: string) => void) => {\n const fakeExit = (code: number): never => { throw new Error(`wiki-lint-exit:${code}`); };\n try {\n await wikiLintHandler({ cwd: wCwd, stdout: wStdout, exit: fakeExit as never });\n } catch (err) {\n const msg = err instanceof Error ? err.message : '';\n if (msg.startsWith('wiki-lint-exit:0')) return;\n throw err;\n }\n });\n\n // Step 1: v1-inert check\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // Step 2: Read state.json — refuse if not Completed\n const stateFile = path.join(cwd, '.cleargate', 'sprint-runs', opts.sprintId, 'state.json');\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate sprint archive] state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n sprint_status?: string;\n stories?: Record<string, unknown>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate sprint archive] failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n if (state.sprint_status !== 'Completed') {\n stderrFn(\n `sprint not closed — run \\`cleargate sprint close ${opts.sprintId} --assume-ack\\` first`,\n );\n return exitFn(1);\n }\n\n const stateStories: Record<string, unknown> = state.stories ?? {};\n\n // Step 3: Resolve file set\n const pendingDir = path.join(cwd, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(cwd, '.cleargate', 'delivery', 'archive');\n\n // Sprint file\n let sprintFile: string | null = null;\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${opts.sprintId}_`) || entry === `${opts.sprintId}.md`) && entry.endsWith('.md')) {\n sprintFile = path.join(pendingDir, entry);\n break;\n }\n }\n\n // Sprint frontmatter → epics list\n let epicIds: string[] = [];\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { fm } = parseFileFrontmatter(fs.readFileSync(sprintFile, 'utf8'));\n const epics = fm['epics'];\n if (Array.isArray(epics)) {\n epicIds = epics.map(String);\n }\n }\n\n // Plan entries: { src, destName, status }\n interface FilePlan {\n src: string;\n destName: string;\n status: string;\n }\n const plan: FilePlan[] = [];\n\n if (sprintFile) {\n plan.push({\n src: sprintFile,\n destName: path.basename(sprintFile),\n status: 'Completed',\n });\n }\n\n for (const epicId of epicIds) {\n // Epic file\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${epicId}_`) || entry === `${epicId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Approved',\n });\n break;\n }\n }\n\n // Story files (authoritative: state.json keys for this epic)\n const storyKeys = storyKeysForEpic(stateStories, epicId);\n for (const storyId of storyKeys) {\n for (const entry of fs.readdirSync(pendingDir)) {\n if ((entry.startsWith(`${storyId}_`) || entry === `${storyId}.md`) && entry.endsWith('.md')) {\n plan.push({\n src: path.join(pendingDir, entry),\n destName: entry,\n status: 'Done',\n });\n break;\n }\n }\n }\n }\n\n // Orphan scan: any STORY-*.md in pending-sync with parent_epic_ref matching\n // one of our epics but NOT in state.json keys.\n const storyIdsInState = new Set(Object.keys(stateStories));\n const planSrcs = new Set(plan.map((p) => p.src));\n const orphans: string[] = [];\n for (const entry of fs.readdirSync(pendingDir)) {\n if (!entry.startsWith('STORY-') || !entry.endsWith('.md')) continue;\n const candidate = path.join(pendingDir, entry);\n if (planSrcs.has(candidate)) continue;\n let raw: string;\n try {\n raw = fs.readFileSync(candidate, 'utf8');\n } catch { continue; }\n const { fm } = parseFileFrontmatter(raw);\n const parentRef = String(fm['parent_epic_ref'] ?? '');\n if (epicIds.includes(parentRef)) {\n // Derive story ID from filename (STORY-014-01_Something.md → STORY-014-01)\n const storyId = entry.replace(/\\.md$/, '').replace(/_.*$/, '');\n if (!storyIdsInState.has(storyId)) {\n orphans.push(candidate);\n stderrFn(`WARN: orphan story ${entry} matches epic ${parentRef} but is not in state.json — archiving anyway`);\n plan.push({ src: candidate, destName: entry, status: 'Done' });\n }\n }\n }\n\n // Step 4: --dry-run: print plan + exit 0\n const completedAt = new Date().toISOString();\n const sprintBranch = deriveSprintBranchForArchive(opts.sprintId);\n const activePath = path.join(cwd, '.cleargate', 'sprint-runs', '.active');\n\n if (opts.dryRun) {\n stdoutFn(`[dry-run] Sprint archive plan for ${opts.sprintId}:`);\n stdoutFn(` Sprint branch: ${sprintBranch}`);\n stdoutFn(` Files to archive (${plan.length}):`);\n for (const entry of plan) {\n stdoutFn(\n ` ${path.basename(entry.src)} → archive/${entry.destName} [stamp: status=${entry.status}, completed_at=<now>]`,\n );\n }\n if (orphans.length > 0) {\n stdoutFn(` Orphan files (${orphans.length}): ${orphans.map((o) => path.basename(o)).join(', ')}`);\n }\n stdoutFn(` .active → \"\" (truncate)`);\n stdoutFn(` git checkout main`);\n stdoutFn(` git merge --no-ff -m \"merge: ${sprintBranch} → main\" ${sprintBranch}`);\n stdoutFn(` git branch -d ${sprintBranch}`);\n return exitFn(0);\n }\n\n // Step 5a: Stamp the sprint file's frontmatter (status + completed_at)\n let sprintFileSnapshot: string | null = null;\n const wikiRoot = path.join(cwd, '.cleargate', 'wiki');\n const wikiInitialised = fs.existsSync(wikiRoot);\n\n if (sprintFile && fs.existsSync(sprintFile)) {\n const { previousContent } = stampSprintClose(sprintFile, () => completedAt);\n sprintFileSnapshot = previousContent;\n\n // Step 5b: wiki build + lint — only if wiki has been initialised.\n // Skip gracefully when .cleargate/wiki/ doesn't exist yet (wiki not built).\n if (wikiInitialised) {\n for (const [stepName, stepFn] of [\n ['wiki build', () => wikiBuildFn(cwd, stdoutFn)] as const,\n ['wiki lint', () => wikiLintFn(cwd, stdoutFn)] as const,\n ]) {\n try {\n await stepFn();\n } catch (err) {\n // CR-022 M5: --allow-wiki-lint-debt waives lint failure but NOT build failure.\n if (stepName === 'wiki lint' && opts.allowWikiLintDebt === true) {\n stderrFn('[cleargate sprint archive] wiki-lint debt waived via --allow-wiki-lint-debt flag');\n if (err instanceof Error) stderrFn(` (lint output: ${err.message})`);\n continue;\n }\n // Rollback sprint file frontmatter\n atomicWriteStr(sprintFile!, sprintFileSnapshot!);\n stderrFn(\n `[cleargate sprint archive] post-stamp ${stepName} failed — sprint frontmatter reverted`,\n );\n if (err instanceof Error) stderrFn(err.message);\n return exitFn(1);\n }\n }\n }\n }\n\n // Step 5c: Live run — stamp + move each file\n for (const entry of plan) {\n if (!fs.existsSync(entry.src)) {\n stderrFn(`[cleargate sprint archive] source not found: ${entry.src} — skipping`);\n continue;\n }\n const raw = fs.readFileSync(entry.src, 'utf8');\n const stamped = stampFile(raw, entry.status, completedAt);\n const dest = path.join(archiveDir, entry.destName);\n atomicWriteStr(entry.src, stamped);\n fs.renameSync(entry.src, dest);\n stdoutFn(`archived: ${entry.destName}`);\n }\n\n // Step 6: Truncate .active\n try {\n atomicWriteStr(activePath, '');\n } catch {\n // .active may not exist — not fatal\n }\n\n // Step 7: git checkout main\n const step7 = spawnFn('git', ['checkout', 'main'], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step7.error || (step7.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git checkout main failed`);\n if (step7.stderr) stderrFn(String(step7.stderr));\n return exitFn(step7.status ?? 1);\n }\n\n // Step 8: git merge --no-ff -m \"merge: sprint/<branch> → main\" sprint/<branch>\n const mergeMsg = `merge: ${sprintBranch} → main`;\n const step8 = spawnFn(\n 'git',\n ['merge', '--no-ff', '-m', mergeMsg, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step8.error || (step8.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git merge failed — run \\`git merge --abort\\` if needed`);\n if (step8.stderr) stderrFn(String(step8.stderr));\n return exitFn(step8.status ?? 1);\n }\n const mergeSha = String(step8.stdout ?? '').trim().split('\\n')[0] ?? '';\n\n // Step 9: git branch -d sprint/<branch>\n const step9 = spawnFn('git', ['branch', '-d', sprintBranch], { stdio: 'pipe', cwd, encoding: 'utf8' });\n if (step9.error || (step9.status ?? 0) !== 0) {\n stderrFn(`[cleargate sprint archive] git branch -d ${sprintBranch} failed`);\n if (step9.stderr) stderrFn(String(step9.stderr));\n return exitFn(step9.status ?? 1);\n }\n\n stdoutFn(\n `archive complete: ${plan.length} files moved, branch ${sprintBranch} deleted` +\n (mergeSha ? `, merge SHA: ${mergeSha}` : ''),\n );\n return exitFn(0);\n } catch (e) {\n // The exitFn seam throws `Error(\"exit:<n>\")` as a synchronous control-flow\n // shortcut so the handler bails without running further steps. Under an\n // async handler that escapes as an unhandled rejection even when tests\n // `getCode()` confirms the expected exit. Swallow the sentinel here; any\n // other error re-throws.\n if (e instanceof Error && /^exit:\\d+$/.test(e.message)) return;\n throw e;\n }\n}\n\n// ─── sprintPreflightHandler ───────────────────────────────────────────────────\n\n/**\n * CR-021: `cleargate sprint preflight <sprint-id>`\n *\n * Runs the four Gate 3 (Sprint Execution) environment-health checks. All four\n * checks always run (operator sees full punch list in one pass). Reports\n * pass/skip/fail per check.\n *\n * Checks:\n * 1. Previous sprint is Completed (skipped for SPRINT-01)\n * 2. No leftover .worktrees/STORY-* paths\n * 3. sprint/S-NN ref does NOT exist\n * 4. main is clean (no uncommitted changes)\n *\n * Exit codes:\n * 0 — all checks pass (or skipped where applicable)\n * 1 — one or more checks failed; stderr lists each failure with hint\n * 2 — usage error (missing/malformed sprint-id arg)\n *\n * Implementation note: uses execSync (node:child_process) per Architect plan M2\n * decision. Synchronous handler — mirrors sprintCloseHandler pattern.\n */\n\n/** Result of a single preflight check. */\ninterface PreflightCheckResult {\n name: string;\n pass: boolean;\n skipped: boolean;\n message: string;\n hint?: string;\n}\n\n/** Options for sprintPreflightHandler — test seams for cwd and execSync. */\nexport interface SprintPreflightOptions {\n /** Working directory for git commands (default: process.cwd()). */\n cwd?: string;\n /** Test seam: override stdout sink (default: process.stdout.write). */\n stdout?: (s: string) => void;\n /** Test seam: override stderr sink (default: process.stderr.write). */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit. Throws Error(\"exit:<n>\") in tests. */\n exit?: (code: number) => never;\n /**\n * Test seam: override execSync. Receives the full shell command string.\n * Should throw on non-zero exit (matching real execSync behaviour).\n */\n execFn?: (cmd: string, opts: { cwd: string; encoding: 'utf8' }) => string;\n}\n\n/** Derive the sprint/S-NN branch name from a SPRINT-NN id. */\nfunction deriveSprintBranchForPreflight(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const num = match ? parseInt(match[1]!, 10) : NaN;\n if (isNaN(num)) return `sprint/${sprintId}`;\n return `sprint/S-${String(num).padStart(2, '0')}`;\n}\n\n/** Parse a numeric sprint sequence number from SPRINT-NN. Returns NaN if invalid. */\nfunction parseSprintNum(sprintId: string): number {\n const m = /^SPRINT-(\\d+)$/.exec(sprintId);\n return m ? parseInt(m[1]!, 10) : NaN;\n}\n\n/** Check 1: previous sprint is Completed (skip for SPRINT-01). */\nfunction checkPrevSprintCompleted(\n sprintId: string,\n cwd: string,\n): PreflightCheckResult {\n const name = 'Previous sprint Completed';\n const sprintNum = parseSprintNum(sprintId);\n if (isNaN(sprintNum) || sprintNum <= 1) {\n return { name, pass: true, skipped: true, message: 'skipped (no preceding sprint)' };\n }\n\n const prevNum = sprintNum - 1;\n const prevId = `SPRINT-${String(prevNum).padStart(2, '0')}`;\n // Also try without zero-pad (e.g. SPRINT-9 not SPRINT-09)\n const prevIdAlt = `SPRINT-${prevNum}`;\n\n const sprintRunsBase = path.join(cwd, '.cleargate', 'sprint-runs');\n\n // Try both padded and unpadded forms\n let stateJson: { sprint_status?: string } | null = null;\n let resolvedPrevId = prevId;\n\n for (const pid of [prevId, prevIdAlt]) {\n const stateFile = path.join(sprintRunsBase, pid, 'state.json');\n if (fs.existsSync(stateFile)) {\n try {\n const raw = fs.readFileSync(stateFile, 'utf8');\n stateJson = JSON.parse(raw) as { sprint_status?: string };\n resolvedPrevId = pid;\n break;\n } catch {\n // continue trying alternate form\n }\n }\n }\n\n if (stateJson === null) {\n // No state.json found — treat as skipped (prev sprint may not have one yet)\n return {\n name,\n pass: true,\n skipped: true,\n message: `skipped (no state.json found for ${prevId})`,\n };\n }\n\n const status = stateJson.sprint_status ?? 'unknown';\n if (status === 'Completed') {\n return { name, pass: true, skipped: false, message: `${resolvedPrevId} status is \"Completed\"` };\n }\n\n return {\n name,\n pass: false,\n skipped: false,\n message: `Previous sprint not Completed`,\n hint: `${resolvedPrevId} status is \"${status}\". Run \\`cleargate sprint close ${resolvedPrevId}\\` first.`,\n };\n}\n\n/** Check 2: no leftover .worktrees/STORY-* paths. */\nfunction checkNoLeftoverWorktrees(\n cwd: string,\n execFn: (cmd: string, opts: { cwd: string; encoding: 'utf8' }) => string,\n): PreflightCheckResult {\n const name = 'No leftover worktrees';\n\n let output = '';\n try {\n output = execFn('git worktree list --porcelain', { cwd, encoding: 'utf8' });\n } catch {\n // If git worktree list fails, skip this check\n return { name, pass: true, skipped: true, message: 'skipped (git worktree list unavailable)' };\n }\n\n // Parse line-by-line for \"worktree <path>\" prefix\n const leftover: string[] = [];\n for (const line of output.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed.startsWith('worktree ')) continue;\n const wtPath = trimmed.slice('worktree '.length);\n // Match paths ending in /.worktrees/STORY-* (any STORY-NNN-NN pattern)\n if (/[/\\\\]\\.worktrees[/\\\\]STORY-/.test(wtPath)) {\n // Normalize to a relative-looking display path: extract .worktrees/STORY-* suffix\n const m = /(\\.(worktrees)[/\\\\]STORY-.+)$/.exec(wtPath);\n leftover.push(m ? m[1] : wtPath);\n }\n }\n\n if (leftover.length === 0) {\n return { name, pass: true, skipped: false, message: 'no leftover .worktrees/STORY-* paths' };\n }\n\n return {\n name,\n pass: false,\n skipped: false,\n message: `Leftover worktree: ${leftover[0]}`,\n hint: `Run \\`git worktree remove ${leftover[0]}\\` if abandoned, or merge if work in progress.`,\n };\n}\n\n/** Check 3: sprint/S-NN ref does NOT exist. */\nfunction checkSprintBranchRefFree(\n sprintId: string,\n cwd: string,\n execFn: (cmd: string, opts: { cwd: string; encoding: 'utf8' }) => string,\n): PreflightCheckResult {\n const branch = deriveSprintBranchForPreflight(sprintId);\n const ref = `refs/heads/${branch}`;\n const name = 'Sprint branch ref free';\n\n try {\n execFn(`git show-ref --verify --quiet ${ref}`, { cwd, encoding: 'utf8' });\n // show-ref returned 0 — ref EXISTS (bad)\n return {\n name,\n pass: false,\n skipped: false,\n message: `Sprint branch ref already exists: ${ref}`,\n hint: `Investigate; force-deletion only with explicit human approval.`,\n };\n } catch {\n // Non-zero exit from show-ref means ref does NOT exist (good)\n return {\n name,\n pass: true,\n skipped: false,\n message: `${ref} does not exist`,\n };\n }\n}\n\n/** Check 4: main is clean (no uncommitted changes). */\nfunction checkMainClean(\n cwd: string,\n execFn: (cmd: string, opts: { cwd: string; encoding: 'utf8' }) => string,\n): PreflightCheckResult {\n const name = 'main is clean';\n\n let output = '';\n try {\n output = execFn('git status --porcelain', { cwd, encoding: 'utf8' }).trim();\n } catch {\n return { name, pass: true, skipped: true, message: 'skipped (git status unavailable)' };\n }\n\n if (output === '') {\n return { name, pass: true, skipped: false, message: 'main is clean' };\n }\n\n // Report the first dirty line for context\n const firstLine = output.split('\\n')[0] ?? output;\n return {\n name,\n pass: false,\n skipped: false,\n message: `main is dirty`,\n hint: `Uncommitted changes detected:\\n ${firstLine}\\n Commit, stash, or discard before starting a sprint.`,\n };\n}\n\n// ─── Check 5: per-item readiness gates ───────────────────────────────────────\n\n/**\n * Locate the sprint plan file by ID, searching pending-sync/ then archive/.\n * Returns null if no matching file is found. Re-implements discoverSprintFile\n * from execution-mode.ts inline (CR-027: avoid cross-command export).\n */\nfunction findSprintFile(sprintId: string, cwd: string): string | null {\n const searchDirs = [\n path.join(cwd, '.cleargate', 'delivery', 'pending-sync'),\n path.join(cwd, '.cleargate', 'delivery', 'archive'),\n ];\n for (const dir of searchDirs) {\n if (!fs.existsSync(dir)) continue;\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n continue;\n }\n const prefix = `${sprintId}_`;\n for (const entry of entries) {\n if ((entry.startsWith(prefix) || entry === `${sprintId}.md`) && entry.endsWith('.md')) {\n return path.join(dir, entry);\n }\n }\n }\n return null;\n}\n\n/**\n * Locate a work-item file by ID, searching pending-sync/ then archive/.\n * Returns null if not found. Re-implements findWorkItemFile from\n * assert_story_files.mjs inline to avoid a shell-out dependency here.\n */\nfunction findWorkItemFileLocal(cwd: string, workItemId: string): string | null {\n const searchDirs = [\n path.join(cwd, '.cleargate', 'delivery', 'pending-sync'),\n path.join(cwd, '.cleargate', 'delivery', 'archive'),\n ];\n const prefix = `${workItemId}_`;\n for (const dir of searchDirs) {\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n continue;\n }\n const match = entries.find((e) => (e.startsWith(prefix) || e === `${workItemId}.md`) && e.endsWith('.md'));\n if (match) return path.join(dir, match);\n }\n return null;\n}\n\n/**\n * Read the cached_gate_result from a file's frontmatter synchronously.\n *\n * CR-027: sync mirror of frontmatter-cache.ts:readCachedGate.\n * sprint preflight is sync; refactoring the handler to async cascades into\n * all 8 existing test scenarios. Inline 25-LOC sync implementation until\n * a future async refactor is justified.\n *\n * Returns null if the file is unreadable, has no frontmatter, or has no\n * cached_gate_result key (or if the key is null/absent).\n */\nfunction readCachedGateSync(absPath: string): { pass: boolean; failing_criteria: { id: string; detail: string }[]; last_gate_check: string } | null {\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch {\n return null;\n }\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n return null;\n }\n const val = fm['cached_gate_result'];\n if (val === undefined || val === null) return null;\n if (typeof val === 'object' && !Array.isArray(val)) {\n const c = val as Record<string, unknown>;\n // If pass is null (SPRINT-20 anchor shape: {pass: null, ...}), treat as no cached result.\n // Per GOTCHA-4: {pass: null} = \"gate check never ran\" = null.\n if (c['pass'] === null || c['pass'] === undefined) return null;\n return {\n pass: Boolean(c['pass']),\n failing_criteria: Array.isArray(c['failing_criteria'])\n ? (c['failing_criteria'] as { id: string; detail: string }[])\n : [],\n last_gate_check: String(c['last_gate_check'] ?? ''),\n };\n }\n return null;\n}\n\n/**\n * Extract work-item IDs from a sprint plan file via assert_story_files.mjs --emit-json.\n * Returns string[] on success, or null on JSON parse failure.\n *\n * The execFn seam allows tests to inject canned JSON — no real shell-out in unit tests.\n * If execFn throws (e.g. script not found in test fixture dirs), returns [] so the\n * sprint plan self-check still runs (graceful degradation, not a hard failure).\n */\nfunction extractInScopeWorkItemIds(\n sprintFilePath: string,\n cwd: string,\n execFn: (cmd: string, opts: { cwd: string; encoding: 'utf8' }) => string,\n): string[] | null {\n const scriptPath = path.join(cwd, '.cleargate', 'scripts', 'assert_story_files.mjs');\n const cmd = `node \"${scriptPath}\" \"${sprintFilePath}\" --emit-json`;\n let stdout: string;\n try {\n stdout = execFn(cmd, { cwd, encoding: 'utf8' });\n } catch {\n // Graceful fallback: script not found or execution failed.\n // No children are enumerable; the sprint plan self-check still runs.\n return [];\n }\n try {\n const parsed = JSON.parse(stdout.trim()) as { workItemIds?: string[] };\n if (!Array.isArray(parsed.workItemIds)) return null;\n return parsed.workItemIds;\n } catch {\n return null;\n }\n}\n\n/** Check 5: per-item readiness gates pass for all items in scope. */\nfunction checkPerItemReadinessGates(\n sprintId: string,\n cwd: string,\n execFn: (cmd: string, opts: { cwd: string; encoding: 'utf8' }) => string,\n mode: string,\n): PreflightCheckResult {\n const name = 'Per-item readiness gates';\n\n // Under v1, this check is advisory-only: skip it (exit-0-equivalent).\n if (mode === 'v1') {\n return {\n name,\n pass: true,\n skipped: true,\n message: 'skipped (execution_mode: v1 — advisory only)',\n };\n }\n\n // Find the sprint plan file\n const sprintFilePath = findSprintFile(sprintId, cwd);\n if (!sprintFilePath) {\n return {\n name,\n pass: false,\n skipped: false,\n message: 'Per-item readiness gates: sprint plan file not found',\n hint: `Cannot locate ${sprintId}*.md in pending-sync/ or archive/.`,\n };\n }\n\n // Extract child work-item IDs from §1 Consolidated Deliverables\n const childIds = extractInScopeWorkItemIds(sprintFilePath, cwd, execFn);\n if (childIds === null) {\n return {\n name,\n pass: false,\n skipped: false,\n message: 'Per-item readiness gates: failed to parse sprint plan deliverables',\n hint: 'Verify \"## 1. Consolidated Deliverables\" exists and lists work-item IDs.',\n };\n }\n\n // Failures accumulate here: { id, reason, failing_criteria }\n const failures: { id: string; reason: string; failingCriteria: string[] }[] = [];\n let totalChecked = 0;\n\n // Helper: evaluate one work-item file\n const evaluateItem = (id: string, absPath: string): void => {\n // Read frontmatter for status + updated_at\n let fm: Record<string, unknown>;\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n ({ fm } = parseFrontmatter(raw));\n } catch {\n // Unreadable — treat as fail\n totalChecked++;\n failures.push({ id, reason: 'unreadable', failingCriteria: [] });\n return;\n }\n\n // Skip terminal-status items (Done, Completed, Abandoned, Closed, Resolved)\n const status = String(fm['status'] ?? '');\n if (TERMINAL_STATUSES.has(status)) {\n return; // not counted in totalChecked\n }\n\n totalChecked++;\n\n // Read cached gate result\n const cachedGate = readCachedGateSync(absPath);\n const updatedAt = String(fm['updated_at'] ?? '');\n\n let reason: string | null = null;\n if (cachedGate === null) {\n reason = 'no cached_gate_result';\n } else if (cachedGate.pass !== true) {\n reason = 'pass=false';\n } else if (cachedGate.last_gate_check !== '' && updatedAt !== '' && cachedGate.last_gate_check < updatedAt) {\n reason = 'stale';\n }\n\n if (reason !== null) {\n failures.push({\n id,\n reason,\n failingCriteria: cachedGate?.failing_criteria?.map((c) => c.id) ?? [],\n });\n }\n };\n\n // Evaluate all child items\n for (const id of childIds) {\n const absPath = findWorkItemFileLocal(cwd, id);\n if (!absPath) {\n totalChecked++;\n failures.push({ id, reason: 'file not found', failingCriteria: [] });\n } else {\n evaluateItem(id, absPath);\n }\n }\n\n // Sprint plan self-check: the sprint plan itself is one of the items in scope\n // (per CR-027 §1: \"the sprint plan is itself one of the items in scope\")\n // assert_story_files.mjs --emit-json extracts only child IDs, so we check it separately.\n totalChecked++;\n const sprintGate = readCachedGateSync(sprintFilePath);\n let sprintRaw: string;\n let sprintFm: Record<string, unknown> = {};\n try {\n sprintRaw = fs.readFileSync(sprintFilePath, 'utf8');\n ({ fm: sprintFm } = parseFrontmatter(sprintRaw));\n } catch {\n // If unreadable, treat as fail (already handled by sprintFilePath check above)\n }\n const sprintUpdatedAt = String(sprintFm['updated_at'] ?? '');\n\n let sprintReason: string | null = null;\n if (sprintGate === null) {\n sprintReason = 'no cached_gate_result';\n } else if (sprintGate.pass !== true) {\n sprintReason = 'pass=false';\n } else if (sprintGate.last_gate_check !== '' && sprintUpdatedAt !== '' && sprintGate.last_gate_check < sprintUpdatedAt) {\n sprintReason = 'stale';\n }\n if (sprintReason !== null) {\n failures.push({\n id: sprintId,\n reason: sprintReason,\n failingCriteria: sprintGate?.failing_criteria?.map((c) => c.id) ?? [],\n });\n }\n\n if (failures.length === 0) {\n return {\n name,\n pass: true,\n skipped: false,\n message: 'all in-scope items pass readiness gates',\n };\n }\n\n // Build hint: bullet per failing item\n const bulletLines = failures\n .map(({ id, reason, failingCriteria }) => {\n const criteria = failingCriteria.length > 0 ? `: ${failingCriteria.join(', ')}` : ` (${reason})`;\n return ` - ${id}${criteria}`;\n })\n .join('\\n');\n\n return {\n name,\n pass: false,\n skipped: false,\n message: `Per-item readiness gates: ${failures.length}/${totalChecked} items not ready`,\n hint: `${bulletLines}\\n Run: cleargate gate check <file> -v for each`,\n };\n}\n\n/**\n * Emit the punch list to stdout (on pass) or stderr (on failure).\n * Format matches §1.2 R4 of STORY-025-02.\n */\nfunction emitPunchList(\n sprintId: string,\n results: PreflightCheckResult[],\n stdoutFn: (s: string) => void,\n stderrFn: (s: string) => void,\n): void {\n const failures = results.filter((r) => !r.pass && !r.skipped);\n\n if (failures.length === 0) {\n stdoutFn(`cleargate sprint preflight: all five checks pass for ${sprintId}`);\n return;\n }\n\n stderrFn(`cleargate sprint preflight: ${failures.length}/${results.length} checks failed for ${sprintId}`);\n stderrFn('');\n for (const r of results) {\n if (r.pass || r.skipped) {\n stderrFn(` ✓ ${r.name} — ${r.message}`);\n } else {\n stderrFn(` ✗ ${r.message}`);\n if (r.hint) {\n stderrFn(` ${r.hint}`);\n }\n }\n }\n}\n\n/**\n * Main handler for `cleargate sprint preflight <sprint-id>`.\n *\n * Synchronous. All five checks always run before exitFn is called\n * (so the operator sees the full punch list on a single invocation).\n *\n * CR-027: added check #5 — per-item readiness gates pass for every\n * work-item ID in §1 Consolidated Deliverables.\n */\nexport function sprintPreflightHandler(\n opts: { sprintId: string },\n cli?: SprintPreflightOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? ((code: number): never => process.exit(code) as never);\n const cwd = cli?.cwd ?? process.cwd();\n\n // Validate sprint-id format: SPRINT-NN or SPRINT-NNN\n if (!/^SPRINT-\\d{2,3}$/.test(opts.sprintId)) {\n stderrFn('Usage: cleargate sprint preflight <sprint-id>');\n stderrFn(' <sprint-id> must match SPRINT-NN or SPRINT-NNN (e.g. SPRINT-18)');\n return exitFn(2);\n }\n\n // Default execFn uses execSync from node:child_process.\n // execSync throws on non-zero exit, matching check contract.\n const execFn = cli?.execFn ?? ((cmd: string, execOpts: { cwd: string; encoding: 'utf8' }) =>\n execSync(cmd, { ...execOpts, stdio: 'pipe' })\n );\n\n // Read execution_mode once — passed to check #5 so it can apply v1/v2 severity.\n const mode = readSprintExecutionMode(opts.sprintId, { cwd });\n\n // Run all five checks — all run regardless of individual failures\n const results: PreflightCheckResult[] = [\n checkPrevSprintCompleted(opts.sprintId, cwd),\n checkNoLeftoverWorktrees(cwd, execFn),\n checkSprintBranchRefFree(opts.sprintId, cwd, execFn),\n checkMainClean(cwd, execFn),\n checkPerItemReadinessGates(opts.sprintId, cwd, execFn, mode),\n ];\n\n emitPunchList(opts.sprintId, results, stdoutFn, stderrFn);\n\n const allPass = results.every((r) => r.pass || r.skipped);\n if (allPass) { stdoutFn('→ Load skill: sprint-execution'); }\n return exitFn(allPass ? 0 : 1);\n}\n","/**\n * lifecycle-reconcile.ts — CR-017 Lifecycle Status Reconciliation + Decomposition Gate\n *\n * Public API:\n * reconcileLifecycle(opts) → { drift: DriftItem[], clean: number }\n * reconcileDecomposition(opts) → { missing: MissingDecomp[], clean: number }\n * parseCommitMessage(msg) → Array<{ verb, id, type }>\n * VERB_STATUS_MAP — verb-to-expected-status table\n *\n * TERMINAL_STATES referenced from .cleargate/scripts/constants.mjs:45.\n * Do NOT redefine; duplicate literal with source citation.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\n\n// ─── Constants ─────────────────────────────────────────────────────────────────\n\n/**\n * Terminal statuses for artifact lifecycle.\n * Source: .cleargate/scripts/constants.mjs:45 TERMINAL_STATES.\n * NOTE: These are the *artifact* terminal statuses (Done, Completed, Verified, etc.),\n * not state.json story states (Done, Escalated, Parking Lot).\n */\nexport const ARTIFACT_TERMINAL_STATUSES = new Set([\n 'Done',\n 'Completed',\n 'Verified',\n 'Abandoned',\n 'Closed',\n 'Resolved',\n 'Escalated',\n 'Parking Lot',\n]);\n\n/**\n * Verb-to-expected-status map (v1).\n * Key: verb pattern (lower-case), Value: { types, expected }.\n * types: which artifact types this verb applies to.\n * expected: accepted terminal statuses for this verb.\n */\nexport const VERB_STATUS_MAP: Readonly<Record<string, { types: string[]; expected: string[] }>> = {\n feat: {\n types: ['STORY', 'EPIC', 'CR'],\n expected: ['Done', 'Completed'],\n },\n fix: {\n types: ['BUG', 'HOTFIX'],\n expected: ['Verified', 'Done', 'Completed'],\n },\n};\n\n// ─── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface DriftItem {\n id: string;\n type: 'STORY' | 'CR' | 'BUG' | 'EPIC' | 'PROPOSAL' | 'HOTFIX';\n expected_status: string;\n actual_status: string | null;\n file_path: string | null;\n in_archive: boolean;\n commit_shas: string[];\n carry_over: boolean;\n}\n\nexport interface ReconcileLifecycleResult {\n drift: DriftItem[];\n clean: number;\n}\n\nexport interface ReconcileLifecycleOpts {\n since: Date;\n until?: Date;\n deliveryRoot: string;\n repoRoot: string;\n /** Test seam: replace spawnSync git calls */\n gitRunner?: (cmd: string, args: string[]) => string;\n}\n\nexport interface MissingDecomp {\n id: string;\n type: 'epic' | 'proposal';\n reason: 'no-child-stories' | 'no-decomposed-epic' | 'file-missing';\n expected_files: string[];\n}\n\nexport interface ReconcileDecompositionResult {\n missing: MissingDecomp[];\n clean: number;\n}\n\nexport interface ReconcileDecompositionOpts {\n sprintPlanPath: string;\n deliveryRoot: string;\n}\n\n// ─── ID shape regex (longest-alternative-first per BUG-010 + assert_story_files.mjs) ──\n\nconst ID_PATTERN = /\\b(STORY-\\d{3}-\\d{2}|(CR|BUG|EPIC|HOTFIX)-\\d{3}|(PROPOSAL|PROP)-\\d{3})\\b/g;\n\n/** Artifact type names recognized by the reconciler */\ntype ArtifactType = 'STORY' | 'CR' | 'BUG' | 'EPIC' | 'PROPOSAL' | 'HOTFIX';\n\nfunction normalizeId(raw: string): string {\n // PROP-NNN → PROPOSAL-NNN (BUG-009 lesson)\n return raw.replace(/^PROP-(\\d+)$/, 'PROPOSAL-$1');\n}\n\nfunction idType(id: string): ArtifactType | null {\n if (/^STORY-\\d{3}-\\d{2}$/.test(id)) return 'STORY';\n if (/^CR-\\d{3}$/.test(id)) return 'CR';\n if (/^BUG-\\d{3}$/.test(id)) return 'BUG';\n if (/^EPIC-\\d{3}$/.test(id)) return 'EPIC';\n if (/^PROPOSAL-\\d{3}$/.test(id)) return 'PROPOSAL';\n if (/^HOTFIX-\\d{3}$/.test(id)) return 'HOTFIX';\n return null;\n}\n\n// ─── parseCommitMessage ────────────────────────────────────────────────────────\n\n/**\n * Parse a commit message (subject + optional first body line) for work-item IDs.\n * Returns one entry per ID found with the verb inferred from conventional prefix.\n *\n * commit format: `<verb>(<scope>): <description>\\n\\n<body>`\n * multi-ID: `fix(cli)!: BUG-001 fix + CR-001 align`\n * merge: `merge: STORY-001-01 → main`\n */\nexport function parseCommitMessage(\n msg: string,\n): Array<{ verb: string; id: string; type: string }> {\n const lines = msg.split('\\n');\n const subject = lines[0] ?? '';\n\n // First non-empty body line (if any) after the blank separator\n let firstBodyLine = '';\n for (let i = 1; i < lines.length; i++) {\n if (lines[i]?.trim()) {\n firstBodyLine = lines[i]!;\n break;\n }\n }\n\n // Extract verb from subject: `feat(...)`, `fix(...)`, `merge:`, `chore(...)`, etc.\n const verbMatch = /^(\\w+)[(!]/.exec(subject) ?? /^(\\w+):/.exec(subject);\n const verb = verbMatch ? verbMatch[1]!.toLowerCase() : '';\n\n // Scan subject + first body line for IDs\n const searchText = subject + (firstBodyLine ? '\\n' + firstBodyLine : '');\n const results: Array<{ verb: string; id: string; type: string }> = [];\n const seen = new Set<string>();\n\n let m: RegExpExecArray | null;\n ID_PATTERN.lastIndex = 0;\n while ((m = ID_PATTERN.exec(searchText)) !== null) {\n const rawId = m[0]!;\n const id = normalizeId(rawId);\n if (seen.has(id)) continue;\n seen.add(id);\n const type = idType(id);\n if (!type) continue;\n results.push({ verb, id, type });\n }\n\n return results;\n}\n\n// ─── File finders ─────────────────────────────────────────────────────────────\n\ninterface FoundFile {\n absPath: string;\n inArchive: boolean;\n relPath: string; // relative to deliveryRoot\n}\n\nfunction findArtifactFile(deliveryRoot: string, id: string): FoundFile | null {\n const prefix = `${id}_`;\n const dirs: Array<{ rel: string; inArchive: boolean }> = [\n { rel: 'pending-sync', inArchive: false },\n { rel: 'archive', inArchive: true },\n ];\n for (const { rel, inArchive } of dirs) {\n const dir = path.join(deliveryRoot, rel);\n let entries: string[];\n try {\n entries = fs.readdirSync(dir);\n } catch {\n continue;\n }\n // match `ID_*.md` OR `ID.md`\n const match = entries.find(\n (e) => (e.startsWith(prefix) || e === `${id}.md`) && e.endsWith('.md'),\n );\n if (match) {\n const absPath = path.join(dir, match);\n return { absPath, inArchive, relPath: `${rel}/${match}` };\n }\n }\n return null;\n}\n\nfunction readArtifactStatus(absPath: string): { status: string | null; carryOver: boolean } {\n let raw: string;\n try {\n raw = fs.readFileSync(absPath, 'utf8');\n } catch {\n return { status: null, carryOver: false };\n }\n try {\n const { fm } = parseFrontmatter(raw);\n const status = typeof fm['status'] === 'string' ? fm['status'] : null;\n const carryOver = fm['carry_over'] === true;\n return { status, carryOver };\n } catch {\n return { status: null, carryOver: false };\n }\n}\n\n// ─── reconcileLifecycle ────────────────────────────────────────────────────────\n\n/**\n * Scan git log in [since, until] range and reconcile artifact statuses.\n *\n * For each commit touching feat/fix verbs with IDs:\n * - Find the artifact file in pending-sync or archive\n * - Check if status is at expected terminal status\n * - Report drift items for non-terminal artifacts\n * - Skip artifacts with carry_over: true\n */\nexport function reconcileLifecycle(opts: ReconcileLifecycleOpts): ReconcileLifecycleResult {\n const { since, until = new Date(), deliveryRoot, repoRoot } = opts;\n\n const gitRunner =\n opts.gitRunner ??\n ((cmd: string, args: string[]) => {\n const result = spawnSync(cmd, args, { encoding: 'utf8', cwd: repoRoot });\n return (result.stdout ?? '') as string;\n });\n\n // git log --format=\"%H %s%n%b%n---COMMIT---\" --after=<since> --before=<until>\n const sinceIso = since.toISOString();\n const untilIso = until.toISOString();\n const logOutput = gitRunner('git', [\n 'log',\n `--after=${sinceIso}`,\n `--before=${untilIso}`,\n '--format=%H%x00%s%x00%b%x00---COMMIT---',\n '--',\n ]);\n\n // Map: id → DriftItem (accumulates SHAs for bundled-commit grouping)\n // We track each id independently; bundled-commit = multiple SHAs per id\n const idToItem = new Map<string, DriftItem>();\n // Track ids that were found CLEAN (fully reconciled)\n const cleanIds = new Set<string>();\n\n if (logOutput.trim()) {\n // Split by commit separator\n const rawCommits = logOutput.split('---COMMIT---\\n').filter((c) => c.trim());\n\n for (const raw of rawCommits) {\n // Each commit entry: sha\\0subject\\0body\\0\n const [sha = '', subject = '', body = ''] = raw.split('\\x00');\n const trimSha = sha.trim();\n const trimSubject = subject.trim();\n const trimBody = body.trim();\n\n if (!trimSha || !trimSubject) continue;\n\n const commitMsg = trimSubject + (trimBody ? '\\n\\n' + trimBody : '');\n const parsed = parseCommitMessage(commitMsg);\n\n for (const { verb, id, type } of parsed) {\n // Skip merge, chore, docs, refactor, test, file, plan verbs (no expectation)\n if (verb === 'merge' || verb === 'chore' || verb === 'docs' || verb === 'refactor'\n || verb === 'test' || verb === 'file' || verb === 'plan') {\n continue;\n }\n\n // Skip PROPOSAL types — proposals aren't shipped via feat/fix commits\n if (type === 'PROPOSAL') continue;\n\n const verbConfig = VERB_STATUS_MAP[verb];\n if (!verbConfig) continue;\n\n // Verb mismatch: feat(BUG-NNN) → soft warning only, handled at call site\n // We still need to find the file and check status for the call site to report\n\n // Find the artifact file\n const found = findArtifactFile(deliveryRoot, id);\n if (!found) {\n // Unknown ID — log once at info level (no drift)\n // We skip unknown IDs (no file found); call site logs info\n continue;\n }\n\n // Read status + carry_over from CURRENT frontmatter\n const { status, carryOver } = readArtifactStatus(found.absPath);\n\n // carry_over: true → skip silently\n if (carryOver) continue;\n\n // Determine expected statuses for this (verb, type) pair\n let expectedStatuses: string[];\n if (verb === 'feat' && type === 'BUG') {\n // verb mismatch — soft warning, does not block; still check status\n // Use 'Verified' as expected for BUG even with feat verb\n expectedStatuses = ['Verified', 'Done', 'Completed'];\n } else if (!verbConfig.types.includes(type)) {\n // Type not covered by this verb's map — skip\n continue;\n } else {\n expectedStatuses = verbConfig.expected;\n }\n\n const isTerminal = status !== null && expectedStatuses.includes(status);\n const isArchived = found.inArchive;\n\n if (isTerminal && isArchived) {\n // Clean\n cleanIds.add(id);\n // If we previously recorded drift for this id (from another commit), remove it\n // (Most recent status check wins — carry_over already handled above)\n idToItem.delete(id);\n } else if (!idToItem.has(id)) {\n // New drift item\n const expectedStr = expectedStatuses[0] ?? 'Done';\n idToItem.set(id, {\n id,\n type: type as DriftItem['type'],\n expected_status: expectedStr,\n actual_status: status,\n file_path: found.relPath,\n in_archive: isArchived,\n commit_shas: [trimSha],\n carry_over: carryOver,\n });\n } else {\n // Existing drift item — add SHA if not already present\n const existing = idToItem.get(id)!;\n if (!existing.commit_shas.includes(trimSha)) {\n existing.commit_shas.push(trimSha);\n }\n }\n }\n }\n }\n\n // Remove from drift any IDs that ended up in cleanIds\n for (const id of cleanIds) {\n idToItem.delete(id);\n }\n\n const drift = Array.from(idToItem.values());\n return { drift, clean: cleanIds.size };\n}\n\n// ─── reconcileDecomposition ───────────────────────────────────────────────────\n\n/**\n * Read the sprint plan's epics: and proposals: frontmatter arrays and verify\n * that each referenced epic has ≥1 child story file, and each proposal has\n * a decomposed epic.\n */\nexport function reconcileDecomposition(opts: ReconcileDecompositionOpts): ReconcileDecompositionResult {\n const { sprintPlanPath, deliveryRoot } = opts;\n\n // Parse sprint plan frontmatter\n let raw: string;\n try {\n raw = fs.readFileSync(sprintPlanPath, 'utf8');\n } catch {\n return { missing: [], clean: 0 };\n }\n\n let fm: Record<string, unknown>;\n try {\n ({ fm } = parseFrontmatter(raw));\n } catch {\n return { missing: [], clean: 0 };\n }\n\n const epics: string[] = Array.isArray(fm['epics']) ? fm['epics'].map(String) : [];\n const proposals: string[] = Array.isArray(fm['proposals']) ? fm['proposals'].map(String) : [];\n\n const pendingDir = path.join(deliveryRoot, 'pending-sync');\n const archiveDir = path.join(deliveryRoot, 'archive');\n\n // Read both dirs for all .md files\n function listMdFiles(dir: string): string[] {\n try {\n return fs.readdirSync(dir).filter((f) => f.endsWith('.md'));\n } catch {\n return [];\n }\n }\n const pendingFiles = listMdFiles(pendingDir);\n const archiveFiles = listMdFiles(archiveDir);\n const allFiles = [...pendingFiles, ...archiveFiles];\n\n const missing: MissingDecomp[] = [];\n let clean = 0;\n\n // Check epics\n for (const epicId of epics) {\n // Find the epic file\n const epicFile = allFiles.find(\n (f) => f.startsWith(`${epicId}_`) || f === `${epicId}.md`,\n );\n if (!epicFile) {\n missing.push({\n id: epicId,\n type: 'epic',\n reason: 'file-missing',\n expected_files: [`pending-sync/${epicId}_<name>.md`],\n });\n continue;\n }\n\n // Find child stories: any STORY-*.md with parent_epic_ref: epicId\n const childStories = findChildStories(\n epicId,\n pendingDir,\n pendingFiles,\n archiveDir,\n archiveFiles,\n );\n\n if (childStories.length === 0) {\n missing.push({\n id: epicId,\n type: 'epic',\n reason: 'no-child-stories',\n expected_files: [\n `pending-sync/${epicId.replace('EPIC-', 'STORY-')}-01_<name>.md`,\n ],\n });\n } else {\n clean++;\n }\n }\n\n // Check proposals\n for (const proposalId of proposals) {\n // Find a decomposed epic that cites this proposal in context_source\n const decomposedEpic = findDecomposedEpic(\n proposalId,\n pendingDir,\n pendingFiles,\n );\n if (!decomposedEpic) {\n missing.push({\n id: proposalId,\n type: 'proposal',\n reason: 'no-decomposed-epic',\n expected_files: [`pending-sync/EPIC-<NNN>_<name>.md with context_source citing ${proposalId}`],\n });\n } else {\n clean++;\n }\n }\n\n return { missing, clean };\n}\n\n/**\n * Find story files in pending-sync or archive that have parent_epic_ref: epicId.\n */\nfunction findChildStories(\n epicId: string,\n pendingDir: string,\n pendingFiles: string[],\n archiveDir: string,\n archiveFiles: string[],\n): string[] {\n const results: string[] = [];\n const epicNumMatch = /^EPIC-(\\d+)$/.exec(epicId);\n if (!epicNumMatch) return results;\n const epicNum = epicNumMatch[1]!;\n\n const storyPrefix = `STORY-${epicNum}-`;\n\n for (const [files, dir] of [[pendingFiles, pendingDir], [archiveFiles, archiveDir]] as const) {\n for (const f of files) {\n if (!f.startsWith(storyPrefix) && !f.startsWith('STORY-')) continue;\n // Quick filename match first\n if (!f.includes(storyPrefix)) continue;\n const absPath = path.join(dir, f);\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n const parentRef = fm['parent_epic_ref'];\n if (parentRef === epicId) {\n results.push(f);\n }\n } catch {\n // skip malformed files\n }\n }\n }\n return results;\n}\n\n/**\n * Find an epic file in pending-sync whose context_source cites proposalId.\n */\nfunction findDecomposedEpic(\n proposalId: string,\n pendingDir: string,\n pendingFiles: string[],\n): string | null {\n for (const f of pendingFiles) {\n if (!f.startsWith('EPIC-')) continue;\n const absPath = path.join(pendingDir, f);\n try {\n const raw = fs.readFileSync(absPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n const contextSource = fm['context_source'];\n if (\n typeof contextSource === 'string' &&\n contextSource.includes(proposalId)\n ) {\n return f;\n }\n } catch {\n // skip\n }\n }\n return null;\n}\n\n// ─── Verb mismatch checker (exported for test use) ────────────────────────────\n\n/**\n * Check if a (verb, type) combination is a mismatch (soft warning only in v1).\n * Returns a warning message or null if no mismatch.\n */\nexport function checkVerbMismatch(verb: string, type: string): string | null {\n if (verb === 'feat' && type === 'BUG') {\n return `verb 'feat' unusual for BUG; expected 'fix'`;\n }\n if (verb === 'fix' && (type === 'STORY' || type === 'EPIC' || type === 'CR')) {\n return `verb 'fix' unusual for ${type}; expected 'feat'`;\n }\n return null;\n}\n","/**\n * story.ts — `cleargate story start|complete` command handlers.\n *\n * STORY-014-07: atomic orchestration of worktree + branch + merge + state.\n *\n * Both handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they orchestrate the full\n * worktree/merge sequence via `spawnSync` + a second-pass `state.json` write.\n *\n * `story start <story-id>`:\n * 1. `git worktree add <cwd>/.worktrees/<ID> -b story/<ID> <sprintBranch>`\n * 2. `bash run_script.sh update_state.mjs <ID> Bouncing`\n * 3. Re-read `state.json`, set `stories[<ID>].worktree` field, atomic write.\n *\n * `story complete <story-id>`:\n * 1. `git rev-list --count <sprintBranch>..story/<ID>` — if 0 → abort.\n * 2. `git -C <cwd> checkout <sprintBranch>`\n * 3. `git merge story/<ID> --no-ff -m \"merge: story/<ID> → <sprintBranch>\"`\n * — on conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n * 4. `git worktree remove .worktrees/<ID>`\n * 5. `git branch -d story/<ID>`\n * 6. `bash run_script.sh update_state.mjs <ID> Done`\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n * FLASHCARD #cli #test-seam #exit: exitFn only at handler top-level.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StoryCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID override for execution_mode discovery.\n * If absent, the handler reads `.cleargate/sprint-runs/.active`.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StoryCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n/**\n * Derive sprint branch from sprint ID.\n * `SPRINT-10` → `sprint/S-10` (zero-padded 2-digit).\n */\nfunction deriveSprintBranch(sprintId: string): string {\n const match = /^SPRINT-(\\d+)/.exec(sprintId);\n const branchNum = match ? match[1]!.replace(/^0+/, '') || '0' : sprintId;\n return `sprint/S-${branchNum.padStart(2, '0')}`;\n}\n\n/**\n * Atomic write: tmp + rename. Inlined per plan (do not import from .mjs).\n * Caller passes the raw JSON-stringified text (or any string content).\n */\nfunction atomicWriteString(filePath: string, text: string): void {\n const tmpFile = `${filePath}.tmp.${process.pid}`;\n fs.writeFileSync(tmpFile, text, 'utf8');\n fs.renameSync(tmpFile, filePath);\n}\n\n/**\n * Path to the sprint's state.json. The file may not exist yet under v2\n * when the sprint was just initialized — callers must guard.\n */\nfunction stateJsonPath(cwd: string, sprintId: string): string {\n return path.join(cwd, '.cleargate', 'sprint-runs', sprintId, 'state.json');\n}\n\n// ─── storyStartHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate story start <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 3-step spawn sequence + second-pass state.json mutation.\n */\nexport function storyStartHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve sprint ID: explicit override, else sentinel-fallback via .active.\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 3-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const worktreePath = path.join(cwd, '.worktrees', opts.storyId);\n const storyBranch = `story/${opts.storyId}`;\n\n // Step 1: git worktree add <path> -b story/<ID> <sprintBranch>\n // FLASHCARD (plan): flag order matters on macOS git — PATH before -b.\n const step1 = spawnFn(\n 'git',\n ['worktree', 'add', worktreePath, '-b', storyBranch, sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 1 (git worktree add) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n\n // Step 2: run_script.sh update_state.mjs <ID> Bouncing\n const runScript = resolveRunScript(cli ?? { cwd });\n const step2 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Bouncing'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story start] step 2 (update_state.mjs) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: re-read state.json (fresh bytes AFTER update_state.mjs), set\n // stories[<ID>].worktree = \".worktrees/<ID>\", atomic write.\n const stateFile = stateJsonPath(cwd, sprintId);\n if (!fs.existsSync(stateFile)) {\n stderrFn(`[cleargate story start] step 3: state.json not found at ${stateFile}`);\n return exitFn(1);\n }\n let state: {\n stories?: Record<string, { worktree?: string | null } & Record<string, unknown>>;\n } & Record<string, unknown>;\n try {\n state = JSON.parse(fs.readFileSync(stateFile, 'utf8'));\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: failed to parse state.json: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n state.stories = state.stories ?? {};\n const existing = state.stories[opts.storyId] ?? {\n state: 'Bouncing',\n qa_bounces: 0,\n arch_bounces: 0,\n worktree: null,\n updated_at: new Date().toISOString(),\n notes: '',\n };\n existing.worktree = `.worktrees/${opts.storyId}`;\n state.stories[opts.storyId] = existing;\n\n try {\n atomicWriteString(stateFile, JSON.stringify(state, null, 2) + '\\n');\n } catch (err) {\n stderrFn(`[cleargate story start] step 3: atomic write failed: ${(err as Error).message}`);\n return exitFn(1);\n }\n\n stdoutFn(`worktree created at .worktrees/${opts.storyId} on branch ${storyBranch}`);\n return exitFn(0);\n}\n\n// ─── storyCompleteHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate story complete <story-id>`\n *\n * v1: print inert message, exit 0.\n * v2: 6-step orchestration (pre-flight, checkout, merge, worktree remove,\n * branch -d, update_state.mjs Done).\n */\nexport function storyCompleteHandler(\n opts: { storyId: string },\n cli?: StoryCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n const cwd = cli?.cwd ?? process.cwd();\n\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // ── v2: 6-step orchestration ───────────────────────────────────────────────\n const sprintBranch = deriveSprintBranch(sprintId);\n const storyBranch = `story/${opts.storyId}`;\n const worktreeRel = path.join('.worktrees', opts.storyId);\n\n // Step 1: pre-flight rev-list — refuse if 0 commits on story branch.\n const step1 = spawnFn(\n 'git',\n ['rev-list', '--count', `${sprintBranch}..${storyBranch}`],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step1.error) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) error: ${step1.error.message}`);\n return exitFn(1);\n }\n if ((step1.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 1 (rev-list) failed with exit ${step1.status}`);\n if (step1.stderr) stderrFn(String(step1.stderr));\n return exitFn(step1.status ?? 1);\n }\n const count = parseInt(String(step1.stdout ?? '0').trim(), 10);\n if (!Number.isFinite(count) || count <= 0) {\n stderrFn('no commits on story branch — nothing to merge');\n return exitFn(1);\n }\n\n // Step 2: checkout sprint branch.\n const step2 = spawnFn(\n 'git',\n ['-C', cwd, 'checkout', sprintBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step2.error) {\n stderrFn(`[cleargate story complete] step 2 (checkout) error: ${step2.error.message}`);\n return exitFn(1);\n }\n if ((step2.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 2 (checkout ${sprintBranch}) failed with exit ${step2.status}`);\n if (step2.stderr) stderrFn(String(step2.stderr));\n return exitFn(step2.status ?? 1);\n }\n\n // Step 3: merge story/<ID> --no-ff -m \"...\"\n // On conflict: leave markers, print `git merge --abort` suggestion, exit 1.\n const mergeMsg = `merge: ${storyBranch} → ${sprintBranch}`;\n const step3 = spawnFn(\n 'git',\n ['merge', storyBranch, '--no-ff', '-m', mergeMsg],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step3.error) {\n stderrFn(`[cleargate story complete] step 3 (merge) error: ${step3.error.message}`);\n return exitFn(1);\n }\n if ((step3.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] merge conflict — run \\`git merge --abort\\` then re-run after resolution`);\n if (step3.stderr) stderrFn(String(step3.stderr));\n return exitFn(1);\n }\n\n // Step 4: git worktree remove .worktrees/<ID>\n const step4 = spawnFn(\n 'git',\n ['worktree', 'remove', worktreeRel],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step4.error) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove) error: ${step4.error.message}`);\n return exitFn(1);\n }\n if ((step4.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 4 (worktree remove ${worktreeRel}) failed with exit ${step4.status}`);\n if (step4.stderr) stderrFn(String(step4.stderr));\n return exitFn(step4.status ?? 1);\n }\n\n // Step 5: git branch -d story/<ID>\n const step5 = spawnFn(\n 'git',\n ['branch', '-d', storyBranch],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step5.error) {\n stderrFn(`[cleargate story complete] step 5 (branch -d) error: ${step5.error.message}`);\n return exitFn(1);\n }\n if ((step5.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 5 (branch -d ${storyBranch}) failed with exit ${step5.status}`);\n if (step5.stderr) stderrFn(String(step5.stderr));\n return exitFn(step5.status ?? 1);\n }\n\n // Step 6: run_script.sh update_state.mjs <ID> Done\n const runScript = resolveRunScript(cli ?? { cwd });\n const step6 = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, 'Done'],\n { stdio: 'pipe', cwd, encoding: 'utf8' },\n );\n if (step6.error) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) error: ${step6.error.message}`);\n return exitFn(1);\n }\n if ((step6.status ?? 0) !== 0) {\n stderrFn(`[cleargate story complete] step 6 (update_state.mjs) failed with exit ${step6.status}`);\n if (step6.stderr) stderrFn(String(step6.stderr));\n return exitFn(step6.status ?? 1);\n }\n\n stdoutFn(`merged ${storyBranch} → ${sprintBranch}; worktree + branch removed; state = Done`);\n return exitFn(0);\n}\n","/**\n * state.ts — `cleargate state update|validate` command handlers.\n *\n * STORY-013-08: CLI wrappers for state management scripts.\n * All handlers are v1-inert: when execution_mode is \"v1\", they print the\n * inert-mode message and exit 0. Under \"v2\", they shell out via run_script.sh.\n *\n * EPIC-013 §0 rule 5: never invoke `node .cleargate/scripts/*.mjs` directly.\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as path from 'node:path';\nimport { spawnSync } from 'node:child_process';\nimport {\n readSprintExecutionMode,\n resolveSprintIdFromSentinel,\n printInertAndExit,\n type ExecutionModeOptions,\n} from './execution-mode.js';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface StateCliOptions extends ExecutionModeOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override path to run_script.sh (test seam). */\n runScriptPath?: string;\n /** Override spawnSync (test seam). */\n spawnFn?: typeof spawnSync;\n /**\n * Sprint ID for execution_mode discovery. Required for state update/validate.\n */\n sprintId?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nfunction resolveRunScript(opts: StateCliOptions): string {\n if (opts.runScriptPath) return opts.runScriptPath;\n const cwd = opts.cwd ?? process.cwd();\n return path.join(cwd, '.cleargate', 'scripts', 'run_script.sh');\n}\n\n// ─── stateUpdateHandler ───────────────────────────────────────────────────────\n\n/**\n * `cleargate state update <story-id> <new-state>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh update_state.mjs <story-id> <new-state>`\n */\nexport function stateUpdateHandler(\n opts: { storyId: string; newState: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n // Sprint ID resolution order:\n // 1. Explicit --sprint <id> from CLI (cli?.sprintId)\n // 2. .cleargate/sprint-runs/.active sentinel\n // 3. Fall through to SPRINT-UNKNOWN (v1-inert)\n const cwd = cli?.cwd;\n const sprintId =\n cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? 'SPRINT-UNKNOWN';\n\n const mode = readSprintExecutionMode(sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'update_state.mjs', opts.storyId, opts.newState],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state update] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n\n// ─── stateValidateHandler ─────────────────────────────────────────────────────\n\n/**\n * `cleargate state validate <sprint-id>`\n *\n * v1: print inert message, exit 0.\n * v2: run `run_script.sh validate_state.mjs <sprint-id>`\n */\nexport function stateValidateHandler(\n opts: { sprintId: string },\n cli?: StateCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const spawnFn = cli?.spawnFn ?? spawnSync;\n\n const mode = readSprintExecutionMode(opts.sprintId, {\n sprintFilePath: cli?.sprintFilePath,\n cwd: cli?.cwd,\n });\n\n if (mode === 'v1') {\n return printInertAndExit(stdoutFn, exitFn);\n }\n\n // v2: shell out via run_script.sh\n const runScript = resolveRunScript(cli ?? {});\n const result = spawnFn(\n 'bash',\n [runScript, 'validate_state.mjs', opts.sprintId],\n { stdio: 'inherit' },\n );\n\n if (result.error) {\n stderrFn(`[cleargate state validate] error: ${result.error.message}`);\n return exitFn(1);\n }\n\n const code = result.status ?? 0;\n return exitFn(code);\n}\n","/**\n * stamp-tokens.ts — STORY-008-05\n *\n * `cleargate stamp-tokens <file>` CLI command.\n * Reads token-ledger rows for a work item, aggregates per-session totals, and\n * stamps `draft_tokens:` into the file's YAML frontmatter.\n *\n * Hook-invoked. Idempotent within a session (last_stamp check), accumulative\n * across sessions (sessions[] array).\n *\n * No top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter, toIsoSecond } from '../lib/frontmatter-yaml.js';\nimport { readLedgerForWorkItem } from '../lib/ledger-reader.js';\nimport { detectWorkItemType } from '../lib/work-item-type.js';\nimport type { SessionBucket } from '../lib/ledger-reader.js';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\nexport interface StampTokensCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n exit?: (code: number) => never;\n sprintRunsRoot?: string;\n}\n\nexport interface DraftTokensSession {\n session: string;\n model: string;\n input: number;\n output: number;\n cache_read: number;\n cache_creation: number;\n ts: string;\n}\n\nexport interface DraftTokens {\n input: number | null;\n output: number | null;\n cache_creation: number | null;\n cache_read: number | null;\n model: string | null;\n last_stamp: string;\n sessions: DraftTokensSession[];\n}\n\nexport async function stampTokensHandler(\n file: string,\n opts: { dryRun?: boolean },\n cli?: StampTokensCliOptions,\n): Promise<void> {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const exitFn =\n cli?.exit ??\n ((code: number) => {\n process.exit(code);\n });\n const nowFn = cli?.now ?? (() => new Date());\n const cwd = cli?.cwd ?? process.cwd();\n\n // Resolve file to absolute path\n const absPath = path.isAbsolute(file) ? file : path.resolve(cwd, file);\n\n // Archive freeze: if path contains /.cleargate/delivery/archive/, noop\n if (/\\/\\.cleargate\\/delivery\\/archive\\//.test(absPath)) {\n stdoutFn(`[frozen] ${absPath}`);\n exitFn(0);\n return;\n }\n\n // Read the file\n let rawContent: string;\n try {\n rawContent = fs.readFileSync(absPath, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot read file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Parse frontmatter\n let fm: Record<string, unknown> = {};\n let body = '';\n\n const hasFrontmatter = rawContent.trimStart().startsWith('---');\n if (hasFrontmatter) {\n try {\n const parsed = parseFrontmatter(rawContent);\n fm = parsed.fm;\n body = parsed.body;\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot parse frontmatter in: ${absPath}`);\n exitFn(1);\n return;\n }\n } else {\n body = rawContent;\n }\n\n // Extract work_item_id from frontmatter ID key or filename fallback\n const workItemId = extractWorkItemId(fm, absPath);\n if (!workItemId) {\n stdoutFn(`[stamp-tokens] error: cannot determine work_item_id from frontmatter or filename: ${absPath}`);\n exitFn(1);\n return;\n }\n\n // Read existing draft_tokens from frontmatter (parsed as native nested object\n // since parseFrontmatter now returns typed values)\n const existingDraftTokens = coerceDraftTokens(fm['draft_tokens']);\n const existingLastStamp = existingDraftTokens?.last_stamp ?? null;\n\n // Read ledger\n const buckets = readLedgerForWorkItem(workItemId, { sprintRunsRoot: cli?.sprintRunsRoot });\n\n // Idempotency check: if all ledger rows are older than last_stamp, and we\n // already have sessions, no-op.\n if (existingLastStamp && buckets.length > 0) {\n const allRowsOlderThanLastStamp = buckets.every((bucket) =>\n bucket.rows.every((row) => row.ts < existingLastStamp),\n );\n if (allRowsOlderThanLastStamp && existingDraftTokens !== null) {\n // No new rows since last stamp — no-op\n exitFn(0);\n return;\n }\n }\n\n const nowIso = toIsoSecond(nowFn());\n\n let newFm: Record<string, unknown>;\n let stampError: string | undefined;\n\n if (buckets.length === 0) {\n // Missing ledger: write all-null draft_tokens + stamp_error\n stampError = `no ledger rows for work_item_id ${workItemId}`;\n const nullTokens: DraftTokens = {\n input: null,\n output: null,\n cache_creation: null,\n cache_read: null,\n model: null,\n last_stamp: nowIso,\n sessions: [],\n };\n newFm = buildNewFrontmatter(fm, nullTokens, stampError);\n } else {\n // Aggregate across all sessions\n const tokens = aggregateBuckets(buckets, nowIso);\n newFm = buildNewFrontmatter(fm, tokens, undefined);\n // Remove stale stamp_error if ledger now has rows\n delete newFm['stamp_error'];\n }\n\n const serialized = buildSerializedContent(newFm, body);\n\n if (opts.dryRun) {\n // Print planned diff without writing\n stdoutFn(`[dry-run] stamp-tokens would write draft_tokens for ${workItemId}:`);\n const draftTokensVal = newFm['draft_tokens'];\n stdoutFn(` draft_tokens: ${JSON.stringify(draftTokensVal)}`);\n if (stampError) {\n stdoutFn(` stamp_error: \"${stampError}\"`);\n }\n exitFn(0);\n return;\n }\n\n // Write the file\n try {\n fs.writeFileSync(absPath, serialized, 'utf-8');\n } catch {\n stdoutFn(`[stamp-tokens] error: cannot write file: ${absPath}`);\n exitFn(1);\n return;\n }\n\n stdoutFn(`[stamped] ${absPath} (${workItemId})`);\n exitFn(0);\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Extract the work_item_id from frontmatter ID fields or filename regex fallback.\n */\nfunction extractWorkItemId(fm: Record<string, unknown>, absPath: string): string | null {\n // Try frontmatter ID keys in order\n const idKeys = ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id'];\n for (const key of idKeys) {\n const val = fm[key];\n if (typeof val === 'string' && val.trim() !== '') {\n return val.trim();\n }\n }\n\n // Fallback: extract from filename\n const basename = path.basename(absPath);\n const match = basename.match(/^(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/i);\n if (match) {\n return match[0].toUpperCase();\n }\n\n // Also try detectWorkItemType for any prefix match\n const typeFromPath = detectWorkItemType(absPath);\n if (typeFromPath) {\n // Try to find the ID segment in the basename\n const idMatch = basename.match(/((?:STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(?:-\\d+)?)/i);\n if (idMatch) {\n return idMatch[1].toUpperCase();\n }\n }\n\n return null;\n}\n\n/**\n * Coerce the existing draft_tokens value from frontmatter into a DraftTokens shape.\n * Handles two on-disk shapes:\n * 1. Native nested YAML map (current format, parsed by js-yaml as an object)\n * 2. Legacy JSON-in-a-string, from pre-fix files written before BUG-001\n */\nfunction coerceDraftTokens(val: unknown): DraftTokens | null {\n if (val == null) return null;\n\n if (typeof val === 'object' && !Array.isArray(val)) {\n const o = val as Record<string, unknown>;\n return {\n input: typeof o['input'] === 'number' ? o['input'] : null,\n output: typeof o['output'] === 'number' ? o['output'] : null,\n cache_creation: typeof o['cache_creation'] === 'number' ? o['cache_creation'] : null,\n cache_read: typeof o['cache_read'] === 'number' ? o['cache_read'] : null,\n model: typeof o['model'] === 'string' ? o['model'] : null,\n last_stamp: typeof o['last_stamp'] === 'string' ? o['last_stamp'] : '',\n sessions: Array.isArray(o['sessions']) ? (o['sessions'] as DraftTokensSession[]) : [],\n };\n }\n\n if (typeof val === 'string') {\n try {\n const parsed = JSON.parse(val) as DraftTokens;\n return parsed;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Aggregate ledger buckets into a DraftTokens object.\n * model: comma-joined sorted unique models across all buckets' rows.\n * sessions[]: one entry per bucket, using bucket totals.\n */\nexport function aggregateBuckets(buckets: SessionBucket[], nowIso: string): DraftTokens {\n let totalInput = 0;\n let totalOutput = 0;\n let totalCacheCreation = 0;\n let totalCacheRead = 0;\n\n const uniqueModels = new Set<string>();\n const sessions: DraftTokensSession[] = [];\n\n for (const bucket of buckets) {\n totalInput += bucket.totals.input;\n totalOutput += bucket.totals.output;\n totalCacheCreation += bucket.totals.cache_creation;\n totalCacheRead += bucket.totals.cache_read;\n\n // Collect unique models from rows and derive session model\n const sessionModels = new Set<string>();\n for (const row of bucket.rows) {\n if (row.model) {\n uniqueModels.add(row.model);\n sessionModels.add(row.model);\n }\n }\n\n // Session model: comma-joined unique models for this session\n const sessionModel = Array.from(sessionModels).sort().join(', ');\n\n sessions.push({\n session: bucket.session_id,\n model: sessionModel,\n input: bucket.totals.input,\n output: bucket.totals.output,\n cache_read: bucket.totals.cache_read,\n cache_creation: bucket.totals.cache_creation,\n ts: bucket.rows[0]?.ts ?? '',\n });\n }\n\n const model = Array.from(uniqueModels).sort().join(', ') || null;\n\n return {\n input: totalInput,\n output: totalOutput,\n cache_creation: totalCacheCreation,\n cache_read: totalCacheRead,\n model,\n last_stamp: nowIso,\n sessions,\n };\n}\n\n/**\n * Build the new frontmatter record with draft_tokens as a native nested\n * object. serializeFrontmatter (js-yaml) emits it as a block-style YAML map.\n */\nfunction buildNewFrontmatter(\n existingFm: Record<string, unknown>,\n tokens: DraftTokens,\n stampError: string | undefined,\n): Record<string, unknown> {\n const newFm: Record<string, unknown> = {};\n\n // Preserve all existing keys except draft_tokens and stamp_error (we'll re-add)\n for (const [k, v] of Object.entries(existingFm)) {\n if (k !== 'draft_tokens' && k !== 'stamp_error') {\n newFm[k] = v;\n }\n }\n\n if (stampError) {\n newFm['stamp_error'] = stampError;\n }\n\n // Store as a plain object; serializer emits block-style YAML\n newFm['draft_tokens'] = tokens as unknown as Record<string, unknown>;\n\n return newFm;\n}\n\n/**\n * Build the final file content: frontmatter block + body.\n */\nfunction buildSerializedContent(fm: Record<string, unknown>, body: string): string {\n const fmBlock = serializeFrontmatter(fm);\n if (body.length > 0) {\n return `${fmBlock}\\n\\n${body}`;\n }\n return `${fmBlock}\\n`;\n}\n\n","/**\n * ledger-reader.ts — STORY-008-04\n *\n * Read-only library for scanning token-ledger.jsonl files across all sprint-run\n * directories and grouping rows by session for per-work-item cost attribution.\n *\n * Node built-ins only. No runtime deps.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface LedgerRow {\n ts: string;\n sprint_id: string;\n agent_type: string;\n /** Populated only for STORY-* items (backward compat; empty string for non-story items) */\n story_id: string;\n /** Always populated when detection succeeded. Equals story_id for STORY items. */\n work_item_id: string;\n session_id: string;\n transcript: string;\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n model: string;\n turns: number;\n}\n\nexport interface SessionBucket {\n session_id: string;\n rows: LedgerRow[];\n totals: {\n input: number;\n output: number;\n cache_creation: number;\n cache_read: number;\n turns: number;\n };\n}\n\nexport interface ReadLedgerOptions {\n /** ISO timestamp string; rows with ts < since are excluded */\n since?: string;\n /**\n * Root directory for sprint-runs/. Defaults to\n * <repo_root>/.cleargate/sprint-runs where repo_root is resolved by\n * walking up from cwd to find .cleargate/.\n * Override in tests to avoid touching the real repo.\n */\n sprintRunsRoot?: string;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/**\n * Walk up from cwd until we find a directory containing `.cleargate/sprint-runs/`.\n * Returns the sprint-runs path or null if not found.\n */\nfunction findSprintRunsRoot(startDir: string): string | null {\n let dir = startDir;\n while (true) {\n const candidate = path.join(dir, '.cleargate', 'sprint-runs');\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n return null;\n }\n dir = parent;\n }\n}\n\nfunction normalizeRow(raw: Record<string, unknown>): LedgerRow {\n // work_item_id may be absent in pre-STORY-008-04 rows; default from story_id.\n const story_id = typeof raw['story_id'] === 'string' ? raw['story_id'] : '';\n const work_item_id =\n typeof raw['work_item_id'] === 'string' && raw['work_item_id'] !== ''\n ? raw['work_item_id']\n : story_id;\n\n return {\n ts: typeof raw['ts'] === 'string' ? raw['ts'] : '',\n sprint_id: typeof raw['sprint_id'] === 'string' ? raw['sprint_id'] : '',\n agent_type: typeof raw['agent_type'] === 'string' ? raw['agent_type'] : 'unknown',\n story_id,\n work_item_id,\n session_id: typeof raw['session_id'] === 'string' ? raw['session_id'] : '',\n transcript: typeof raw['transcript'] === 'string' ? raw['transcript'] : '',\n input: typeof raw['input'] === 'number' ? raw['input'] : 0,\n output: typeof raw['output'] === 'number' ? raw['output'] : 0,\n cache_creation: typeof raw['cache_creation'] === 'number' ? raw['cache_creation'] : 0,\n cache_read: typeof raw['cache_read'] === 'number' ? raw['cache_read'] : 0,\n model: typeof raw['model'] === 'string' ? raw['model'] : '',\n turns: typeof raw['turns'] === 'number' ? raw['turns'] : 0,\n };\n}\n\nfunction rowMatchesWorkItem(row: LedgerRow, workItemId: string): boolean {\n return row.work_item_id === workItemId || row.story_id === workItemId;\n}\n\nfunction buildBucket(session_id: string, rows: LedgerRow[]): SessionBucket {\n const totals = rows.reduce(\n (acc, r) => ({\n input: acc.input + r.input,\n output: acc.output + r.output,\n cache_creation: acc.cache_creation + r.cache_creation,\n cache_read: acc.cache_read + r.cache_read,\n turns: acc.turns + r.turns,\n }),\n { input: 0, output: 0, cache_creation: 0, cache_read: 0, turns: 0 }\n );\n return { session_id, rows, totals };\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Scan all sprint-runs/<sprint>/token-ledger.jsonl files and return rows\n * matching the given workItemId, grouped by session_id.\n *\n * Rows are compared by work_item_id first, then story_id (for backward compat\n * with pre-STORY-008-04 rows that lack work_item_id).\n *\n * Pre-fix rows (missing work_item_id) default work_item_id from story_id.\n *\n * @param workItemId e.g. \"STORY-008-04\", \"EPIC-008\", \"PROPOSAL-042\"\n * @param opts optional filters and path overrides\n * @returns array of SessionBucket, one per unique session_id, sorted\n * by the ts of the earliest row in each bucket\n */\nexport function readLedgerForWorkItem(\n workItemId: string,\n opts: ReadLedgerOptions = {}\n): SessionBucket[] {\n // Resolve sprint-runs root\n let sprintRunsRoot: string;\n if (opts.sprintRunsRoot) {\n sprintRunsRoot = opts.sprintRunsRoot;\n } else {\n const found = findSprintRunsRoot(process.cwd());\n if (!found) {\n return [];\n }\n sprintRunsRoot = found;\n }\n\n if (!fs.existsSync(sprintRunsRoot)) {\n return [];\n }\n\n // Collect all token-ledger.jsonl files across all sprint dirs\n let ledgerFiles: string[];\n try {\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n ledgerFiles = entries\n .filter((e) => e.isDirectory())\n .map((e) => path.join(sprintRunsRoot, e.name, 'token-ledger.jsonl'))\n .filter((f) => fs.existsSync(f));\n } catch {\n return [];\n }\n\n // Parse matching rows from each file\n const matchingRows: LedgerRow[] = [];\n\n for (const ledgerFile of ledgerFiles) {\n let content: string;\n try {\n content = fs.readFileSync(ledgerFile, 'utf-8');\n } catch {\n continue;\n }\n\n const lines = content.split('\\n').filter((l) => l.trim() !== '');\n for (const line of lines) {\n let raw: Record<string, unknown>;\n try {\n raw = JSON.parse(line) as Record<string, unknown>;\n } catch {\n continue;\n }\n\n const row = normalizeRow(raw);\n\n // Apply since filter\n if (opts.since && row.ts < opts.since) {\n continue;\n }\n\n if (rowMatchesWorkItem(row, workItemId)) {\n matchingRows.push(row);\n }\n }\n }\n\n // Group by session_id\n const sessionMap = new Map<string, LedgerRow[]>();\n for (const row of matchingRows) {\n const key = row.session_id || '(unknown-session)';\n const existing = sessionMap.get(key);\n if (existing) {\n existing.push(row);\n } else {\n sessionMap.set(key, [row]);\n }\n }\n\n // Build buckets and sort by earliest row ts within each bucket\n const buckets = Array.from(sessionMap.entries()).map(([session_id, rows]) => {\n rows.sort((a, b) => a.ts.localeCompare(b.ts));\n return buildBucket(session_id, rows);\n });\n\n // Sort buckets by earliest row ts\n buckets.sort((a, b) => {\n const aTs = a.rows[0]?.ts ?? '';\n const bTs = b.rows[0]?.ts ?? '';\n return aTs.localeCompare(bTs);\n });\n\n return buckets;\n}\n\n","/**\n * upgrade.ts — STORY-009-05\n *\n * `cleargate upgrade [--dry-run] [--yes] [--only <tier>]`\n *\n * Three-way merge driver: walks each tracked scaffold file, classifies its\n * drift state, routes by overwrite_policy, and applies the user-chosen merge\n * action. Snapshot is updated atomically after every successfully handled file\n * so the command is resumable (re-run continues from the last clean state).\n *\n * Special-case handling:\n * - CLAUDE.md tier (cli-config): uses claude-md-surgery to merge only the\n * CLEARGATE:START…CLEARGATE:END bounded block, leaving user prose intact.\n * - settings.json tier (cli-config): uses settings-json-surgery to merge\n * only ClearGate-owned hooks, leaving user hooks intact.\n *\n * CRITICAL: all file mutations are atomic (write .tmp → fs.rename).\n * INCREMENTAL: each file is independent. Failure on file N does not roll back\n * file N-1. Re-running re-classifies against the updated snapshot.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n * Inject `promptMergeChoice` + `openInEditor` via `UpgradeCliOptions` for tests\n * (FLASHCARD #cli #determinism #test-seam).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport {\n loadPackageManifest,\n loadInstallSnapshot,\n computeCurrentSha,\n classify,\n writeDriftState,\n type ManifestFile,\n type ManifestEntry,\n type DriftMap,\n type Tier,\n} from '../lib/manifest.js';\nimport { sliceChangelog } from '../lib/changelog.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { readBlock, writeBlock } from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\nimport {\n promptMergeChoice as defaultPromptMergeChoice,\n type MergeChoice,\n} from '../lib/merge-ui.js';\nimport {\n openInEditor as defaultOpenInEditor,\n containsConflictMarkers,\n} from '../lib/editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport interface UpgradeCliOptions {\n cwd?: string;\n now?: () => Date;\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: override the package root for loadPackageManifest. */\n packageRoot?: string;\n /** Test seam: inject a custom promptMergeChoice (avoids interactive stdin). */\n promptMergeChoice?: typeof defaultPromptMergeChoice;\n /** Test seam: inject a custom openInEditor (avoids forking real editors). */\n openInEditor?: typeof defaultOpenInEditor;\n /** Test seam: inject a custom stdin for promptMergeChoice. */\n stdin?: NodeJS.ReadableStream;\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────────────\n\n/** Write file atomically: write to .tmp, then rename. Never leaves partial writes. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = filePath + '.tmp.' + Date.now();\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/**\n * Update a single file's sha256 in the install snapshot atomically.\n * Reads the snapshot file, patches the matching entry, and re-writes atomically.\n */\nasync function updateSnapshotEntry(\n projectRoot: string,\n filePath: string,\n newSha: string | null\n): Promise<void> {\n const snapshotPath = path.join(projectRoot, '.cleargate', '.install-manifest.json');\n let snapshot: ManifestFile;\n\n try {\n const raw = await fsp.readFile(snapshotPath, 'utf-8');\n snapshot = JSON.parse(raw) as ManifestFile;\n } catch {\n // If no snapshot exists yet, cannot update — silently skip.\n return;\n }\n\n const updated: ManifestFile = {\n ...snapshot,\n files: snapshot.files.map((entry) =>\n entry.path === filePath ? { ...entry, sha256: newSha } : entry\n ),\n };\n\n await writeAtomic(snapshotPath, JSON.stringify(updated, null, 2) + '\\n');\n}\n\n/**\n * Determine if this is a CLAUDE.md file (needs block surgery).\n */\nfunction isClaudeMd(filePath: string): boolean {\n return path.basename(filePath) === 'CLAUDE.md';\n}\n\n/**\n * Determine if this is a settings.json file (needs hook surgery).\n */\nfunction isSettingsJson(filePath: string): boolean {\n return path.basename(filePath) === 'settings.json' && filePath.includes('.claude');\n}\n\n/**\n * Merge-3way handler: always-policy — overwrite file with package content.\n * Used for `always` overwrite_policy files.\n */\nasync function applyAlwaysOverwrite(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n stdout: (s: string) => void\n): Promise<void> {\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n try {\n const pkgContent = await fsp.readFile(sourcePath, 'utf-8');\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, pkgContent);\n await updateSnapshotEntry(projectRoot, entry.path, entry.sha256);\n stdout(`[always] overwritten: ${entry.path}`);\n } catch (err) {\n stdout(`[always] error overwriting ${entry.path}: ${(err as Error).message}`);\n }\n}\n\n/**\n * Merge-3way handler for `merge-3way` overwrite_policy files.\n * Supports k/t/e choices with conflict-marker semantics for 'e'.\n * Returns the post-merge sha or null if skipped/failed.\n */\nasync function applyMerge3Way(\n entry: ManifestEntry,\n projectRoot: string,\n packageRoot: string,\n installSha: string | null,\n currentSha: string | null,\n flags: { yes?: boolean; dryRun?: boolean },\n opts: {\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n promptMergeChoiceFn: typeof defaultPromptMergeChoice;\n openInEditorFn: typeof defaultOpenInEditor;\n stdin?: NodeJS.ReadableStream;\n }\n): Promise<{ updated: boolean; newSha: string | null }> {\n const { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin } = opts;\n\n const targetPath = path.join(projectRoot, entry.path);\n const sourcePath = path.join(packageRoot, entry.path);\n\n // Read current (ours) and package (theirs) content\n let ours = '';\n let theirs = '';\n\n try {\n ours = await fsp.readFile(targetPath, 'utf-8');\n } catch {\n // File missing on disk — treat as empty\n ours = '';\n }\n\n try {\n theirs = await fsp.readFile(sourcePath, 'utf-8');\n } catch {\n // Package file missing — skip\n stdout(`[merge] skip: package file not found for ${entry.path}`);\n return { updated: false, newSha: null };\n }\n\n // Compute drift state for display\n const state = classify(entry.sha256, installSha, currentSha, entry.tier);\n\n let choice: MergeChoice;\n\n if (flags.yes) {\n // --yes: auto-take-theirs\n choice = 't';\n stdout(`[yes] taking theirs: ${entry.path} state=${state}`);\n } else {\n // Interactive prompt\n choice = await promptMergeChoiceFn({\n path: entry.path,\n state,\n ours,\n theirs,\n stdin,\n stdout,\n });\n }\n\n if (choice === 'k') {\n // Keep mine: file unchanged, snapshot records current_sha as installed_sha\n stdout(`[keep] ${entry.path}`);\n await updateSnapshotEntry(projectRoot, entry.path, currentSha);\n return { updated: true, newSha: currentSha };\n }\n\n if (choice === 't') {\n // Take theirs: apply CLAUDE.md or settings.json surgery if needed, else raw overwrite\n let mergedContent = theirs;\n\n if (isClaudeMd(entry.path)) {\n // Merge only the bounded block; preserve user prose\n try {\n const ourBlock = readBlock(ours);\n const theirBlock = readBlock(theirs);\n if (ourBlock !== null && theirBlock !== null) {\n mergedContent = writeBlock(ours, theirBlock);\n } else if (theirBlock !== null) {\n // No block in ours — full overwrite\n mergedContent = theirs;\n }\n // If no block in theirs, skip (no ClearGate content to take)\n } catch {\n // Surgery failed — fall back to full overwrite\n mergedContent = theirs;\n }\n } else if (isSettingsJson(entry.path)) {\n // Merge only ClearGate-owned hooks; preserve user hooks\n try {\n const ourSettings = JSON.parse(ours) as ClaudeSettings;\n const theirSettings = JSON.parse(theirs) as ClaudeSettings;\n // Remove ClearGate hooks from ours, then add ClearGate hooks from theirs\n const withoutOurCg = removeClearGateHooks(ourSettings);\n // Build merged: user hooks from ours + ClearGate hooks from theirs\n const cgHooks = theirSettings.hooks ?? {};\n const merged: ClaudeSettings = { ...withoutOurCg };\n if (Object.keys(cgHooks).length > 0) {\n merged.hooks = { ...(withoutOurCg.hooks ?? {}), ...cgHooks };\n }\n mergedContent = JSON.stringify(merged, null, 2) + '\\n';\n } catch {\n // Surgery failed — full overwrite\n mergedContent = theirs;\n }\n }\n\n await fsp.mkdir(path.dirname(targetPath), { recursive: true });\n await writeAtomic(targetPath, mergedContent);\n const newSha = hashNormalized(mergedContent);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[take] ${entry.path}`);\n return { updated: true, newSha };\n }\n\n // choice === 'e': write conflict-marker file + open editor\n const mergeFilePath = targetPath + '.cleargate-merge';\n const conflictContent =\n `<<<<<<< ours (installed)\\n${ours}=======\\n${theirs}>>>>>>> theirs (upstream)\\n`;\n\n await fsp.mkdir(path.dirname(mergeFilePath), { recursive: true });\n await writeAtomic(mergeFilePath, conflictContent);\n\n try {\n const result = await openInEditorFn(mergeFilePath);\n if (result.exitCode !== 0) {\n stderr(`[edit] editor exited with code ${result.exitCode}; markers may remain in ${mergeFilePath}`);\n }\n } catch (err) {\n stderr(`[edit] could not open editor: ${(err as Error).message}`);\n stderr(`[edit] resolve markers manually in: ${mergeFilePath}`);\n return { updated: false, newSha: null };\n }\n\n // Read post-edit content\n let edited = '';\n try {\n edited = await fsp.readFile(mergeFilePath, 'utf-8');\n } catch {\n stderr(`[edit] could not read ${mergeFilePath} after editor exit`);\n return { updated: false, newSha: null };\n }\n\n if (containsConflictMarkers(edited)) {\n stderr(`[edit] unresolved conflict markers remain in ${mergeFilePath}`);\n stderr(`[edit] file NOT updated. Resolve manually and re-run upgrade.`);\n // Leave .cleargate-merge in place for manual resolution\n return { updated: false, newSha: null };\n }\n\n // Markers resolved — overwrite target file, remove merge file\n await writeAtomic(targetPath, edited);\n try {\n await fsp.unlink(mergeFilePath);\n } catch {\n // Non-fatal if cleanup fails\n }\n\n const newSha = hashNormalized(edited);\n await updateSnapshotEntry(projectRoot, entry.path, newSha);\n stdout(`[edit] resolved: ${entry.path}`);\n return { updated: true, newSha };\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function upgradeHandler(\n flags: { dryRun?: boolean; yes?: boolean; only?: string },\n cli?: UpgradeCliOptions\n): Promise<void> {\n const cwd = cli?.cwd ?? process.cwd();\n const now = cli?.now ? cli.now() : new Date();\n const stdout = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = cli?.exit ?? ((code: number) => process.exit(code) as never);\n const promptMergeChoiceFn = cli?.promptMergeChoice ?? defaultPromptMergeChoice;\n const openInEditorFn = cli?.openInEditor ?? defaultOpenInEditor;\n const stdin = cli?.stdin;\n\n // ─── 1. Load manifests + compute drift ──────────────────────────────────────\n\n let pkgManifest: ManifestFile;\n try {\n pkgManifest = loadPackageManifest({ packageRoot: cli?.packageRoot });\n } catch (err) {\n stderr(`[upgrade] ${(err as Error).message}`);\n exit(1);\n return;\n }\n\n const installSnapshot = await loadInstallSnapshot(cwd);\n\n // Build a lookup from the snapshot\n const snapshotByPath = new Map<string, string | null>();\n for (const entry of installSnapshot?.files ?? []) {\n snapshotByPath.set(entry.path, entry.sha256);\n }\n\n // ─── 1b. Print CHANGELOG delta ───────────────────────────────────────────────\n\n {\n const installedVersion = installSnapshot?.cleargate_version ?? pkgManifest.cleargate_version;\n const targetVersion = pkgManifest.cleargate_version;\n\n // Only print delta when there's actually a version change\n if (installedVersion !== targetVersion) {\n // Resolve CHANGELOG.md path from package root (same resolution as loadPackageManifest)\n const pkgRoot = cli?.packageRoot ?? path.join(path.dirname(new URL(import.meta.url).pathname), '..', '..');\n const changelogPath = path.join(pkgRoot, 'CHANGELOG.md');\n\n try {\n const changelogContent = fs.readFileSync(changelogPath, 'utf-8');\n const sections = sliceChangelog(changelogContent, installedVersion, targetVersion);\n\n if (sections.length > 0) {\n // Emit all matching sections verbatim, separated by blank lines between sections\n // (each section body already includes the heading + content)\n const deltaText = sections.map((s) => s.body).join('\\n\\n');\n stdout(deltaText);\n stdout('\\n---');\n }\n } catch {\n // ENOENT or any read error — warn on stderr and continue; never block upgrade\n stderr('cleargate: CHANGELOG.md not readable; skipping release notes');\n }\n }\n }\n\n // ─── 2. Apply --only <tier> filter ──────────────────────────────────────────\n\n const onlyTier: Tier | undefined = flags.only as Tier | undefined;\n const filteredFiles = onlyTier\n ? pkgManifest.files.filter((e) => e.tier === onlyTier)\n : pkgManifest.files;\n\n // ─── 3. Classify all files ──────────────────────────────────────────────────\n\n interface FileWork {\n entry: ManifestEntry;\n currentSha: string | null;\n installSha: string | null;\n action: 'overwrite' | 'skip' | 'merge-3way';\n }\n\n const workItems: FileWork[] = [];\n\n await Promise.all(\n filteredFiles.map(async (entry) => {\n if (entry.tier === 'user-artifact') {\n // Always skip user-artifact tier (never tracked)\n return;\n }\n\n // BUG-023: pass pinVersion so pin-aware hook files are reverse-substituted before hashing.\n const currentSha = await computeCurrentSha(entry, cwd, { pinVersion: installSnapshot?.pin_version });\n const installSha = snapshotByPath.get(entry.path) ?? null;\n\n let action: FileWork['action'];\n switch (entry.overwrite_policy) {\n case 'always':\n action = 'overwrite';\n break;\n case 'skip':\n case 'preserve':\n action = 'skip';\n break;\n case 'merge-3way':\n default:\n action = 'merge-3way';\n break;\n }\n\n workItems.push({ entry, currentSha, installSha, action });\n })\n );\n\n // Sort for deterministic output\n workItems.sort((a, b) => a.entry.path.localeCompare(b.entry.path));\n\n // ─── 4. --dry-run: print plan and exit ──────────────────────────────────────\n\n if (flags.dryRun) {\n let count = 0;\n for (const item of workItems) {\n const state = classify(item.entry.sha256, item.installSha, item.currentSha, item.entry.tier);\n stdout(`[dry-run] ${item.entry.path} action=${item.action} state=${state}`);\n count++;\n }\n stdout(`[dry-run] ${count} files planned. No changes made.`);\n return;\n }\n\n // ─── 5. Execute per file ─────────────────────────────────────────────────────\n\n // Determine package root for reading source files.\n // cli.packageRoot is the test seam (always injected in tests).\n // In production, use the same default resolution as loadPackageManifest.\n // Since we already loaded the manifest above (which resolves the path internally),\n // we simply use cli.packageRoot when provided, otherwise fall back to cwd\n // (in production the actual resolution is inside loadPackageManifest).\n const packageRoot = cli?.packageRoot ?? cwd;\n\n const driftMap: DriftMap = {};\n\n for (const item of workItems) {\n const { entry, currentSha, installSha, action } = item;\n\n switch (action) {\n case 'skip': {\n // never / preserve — no prompt, no write\n stdout(`[skip] ${entry.path} policy=${entry.overwrite_policy}`);\n break;\n }\n\n case 'overwrite': {\n // always — overwrite silently\n await applyAlwaysOverwrite(entry, cwd, packageRoot, stdout);\n break;\n }\n\n case 'merge-3way': {\n // Interactive 3-way merge (unless --yes)\n await applyMerge3Way(\n entry,\n cwd,\n packageRoot,\n installSha,\n currentSha,\n { yes: flags.yes, dryRun: false },\n { stdout, stderr, promptMergeChoiceFn, openInEditorFn, stdin }\n );\n break;\n }\n }\n\n // Re-compute current sha after potential mutation (for drift map).\n // BUG-023: pass pinVersion for pin-aware files.\n const postSha = await computeCurrentSha(entry, cwd, { pinVersion: installSnapshot?.pin_version });\n driftMap[entry.path] = {\n state: classify(entry.sha256, installSha, postSha, entry.tier),\n entry,\n install_sha: installSha,\n current_sha: postSha,\n package_sha: entry.sha256,\n };\n }\n\n // ─── 6. Refresh .drift-state.json ────────────────────────────────────────────\n\n await writeDriftState(cwd, driftMap, { lastRefreshed: now.toISOString() });\n\n stdout('[upgrade] complete.');\n}\n","/**\n * changelog.ts — STORY-016-04\n *\n * Parses and slices Common-Changelog format CHANGELOG.md files.\n * Shared by `cleargate upgrade` (print delta before merge loop).\n *\n * API contract is LOCKED per M1.md §3 (STORY-016-04 blueprint).\n */\n\nimport { compareSemver } from './registry-check.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/** Section heading regex — single source of truth shared with 016-03's format test */\nexport const HEADING_RE = /^## \\[(\\d+\\.\\d+\\.\\d+)\\] — (\\d{4}-\\d{2}-\\d{2})/m;\n\nexport interface ChangelogSection {\n version: string; // \"0.9.0\"\n date: string; // \"2026-04-30\"\n body: string; // section content INCLUDING the heading line, trimmed of trailing blank\n}\n\n// ─── Implementation ───────────────────────────────────────────────────────────\n\n/**\n * Parse the entire CHANGELOG into sections, most-recent first.\n * Returns [] on parse fail or empty input.\n */\nexport function parseChangelog(content: string): ChangelogSection[] {\n if (!content || typeof content !== 'string') return [];\n\n // Global regex to find all section headings\n const HEADING_RE_GLOBAL = /^## \\[(\\d+\\.\\d+\\.\\d+)\\] — (\\d{4}-\\d{2}-\\d{2})/gm;\n\n const sections: ChangelogSection[] = [];\n const matches: Array<{ version: string; date: string; index: number }> = [];\n\n let match: RegExpExecArray | null;\n while ((match = HEADING_RE_GLOBAL.exec(content)) !== null) {\n matches.push({\n version: match[1],\n date: match[2],\n index: match.index,\n });\n }\n\n if (matches.length === 0) return [];\n\n for (let i = 0; i < matches.length; i++) {\n const start = matches[i].index;\n // End is either start of next section or end of content\n const end = i + 1 < matches.length ? matches[i + 1].index : content.length;\n // Extract body (trim trailing whitespace/blank lines but keep heading)\n const rawBody = content.slice(start, end);\n const body = rawBody.trimEnd();\n\n sections.push({\n version: matches[i].version,\n date: matches[i].date,\n body,\n });\n }\n\n return sections;\n}\n\n/**\n * Slice sections where fromExclusive < section.version <= toInclusive.\n *\n * - Returns sections in the original CHANGELOG order (most-recent first).\n * - If fromExclusive is older than all entries (or not found), all sections\n * up to toInclusive are returned.\n * - If toInclusive is not found, returns empty [].\n * - If fromExclusive === toInclusive, returns [].\n */\nexport function sliceChangelog(\n content: string,\n fromExclusive: string,\n toInclusive: string\n): ChangelogSection[] {\n if (!content || typeof content !== 'string') return [];\n\n // Same-version: nothing to show\n if (compareSemver(fromExclusive, toInclusive) === 0) return [];\n\n const sections = parseChangelog(content);\n if (sections.length === 0) return [];\n\n // Filter: version must be > fromExclusive AND <= toInclusive\n const result = sections.filter((s) => {\n const cmpFrom = compareSemver(s.version, fromExclusive); // >0 means s > from\n const cmpTo = compareSemver(s.version, toInclusive); // <=0 means s <= to\n\n return cmpFrom > 0 && cmpTo <= 0;\n });\n\n return result;\n}\n","export const CLEARGATE_START = '<!-- CLEARGATE:START -->';\nexport const CLEARGATE_END = '<!-- CLEARGATE:END -->';\n\n// IMPORTANT: regex MUST be GREEDY ([\\s\\S]* not [\\s\\S]*?)\n// The block body itself may reference both markers in prose (FLASHCARD 2026-04-19 #init #inject-claude-md #regex).\n// Non-greedy would stop at the first inline END marker in prose, cutting off the real block.\nconst BLOCK_REGEX = /<!-- CLEARGATE:START -->([\\s\\S]*)<!-- CLEARGATE:END -->/;\n\n/**\n * Returns the content between CLEARGATE:START and CLEARGATE:END markers.\n * Returns null if either marker is missing.\n */\nexport function readBlock(content: string): string | null {\n const match = BLOCK_REGEX.exec(content);\n if (!match) return null;\n return match[1];\n}\n\n/**\n * Replaces the content between CLEARGATE:START and CLEARGATE:END markers,\n * preserving the markers themselves and all surrounding content.\n * Throws if markers are missing.\n */\nexport function writeBlock(content: string, newBlockBody: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, `${CLEARGATE_START}${newBlockBody}${CLEARGATE_END}`);\n}\n\n/**\n * Removes both markers AND the content between them, leaving surrounding content intact.\n * Throws if markers are missing.\n */\nexport function removeBlock(content: string): string {\n if (!content.includes(CLEARGATE_START)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n }\n if (!content.includes(CLEARGATE_END)) {\n throw new Error('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n }\n return content.replace(BLOCK_REGEX, '');\n}\n","export interface HookCommand {\n type: 'command';\n command: string;\n if?: string;\n}\n\nexport interface HookEntry {\n matcher?: string;\n hooks?: HookCommand[];\n}\n\nexport interface ClaudeSettings {\n hooks?: {\n PostToolUse?: HookEntry[];\n SessionStart?: HookEntry[];\n SubagentStop?: HookEntry[];\n [k: string]: HookEntry[] | undefined;\n };\n [k: string]: unknown;\n}\n\n/**\n * Returns true if the given command string belongs to ClearGate.\n * Matches:\n * - .claude/hooks/token-ledger.sh\n * - .claude/hooks/stamp-and-gate.sh\n * - .claude/hooks/session-start.sh\n * - .claude/hooks/wiki-ingest.sh (legacy)\n * - .claude/hooks/cleargate-*.sh (catch-all for future hooks)\n * - inline commands containing 'wiki ingest' (legacy SPRINT-04 PostToolUse inline)\n */\nfunction isClearGateCommand(command: string): boolean {\n if (command.includes('wiki ingest')) return true;\n return /\\/\\.claude\\/hooks\\/(token-ledger|stamp-and-gate|session-start|wiki-ingest|cleargate-[^/]*)\\.sh/.test(command);\n}\n\n/**\n * Removes ClearGate-owned hook entries from a ClaudeSettings object.\n * - Removes individual inner `hooks[]` sub-entries whose `command` matches ClearGate patterns.\n * - If the parent HookEntry's `hooks[]` array becomes empty, removes that HookEntry.\n * - If an event-category array (e.g. hooks.PostToolUse) becomes empty, removes the key.\n * - Preserves all other hook entries and all other top-level keys.\n */\nexport function removeClearGateHooks(settings: ClaudeSettings): ClaudeSettings {\n if (!settings.hooks) return { ...settings };\n\n const newHooks: NonNullable<ClaudeSettings['hooks']> = {};\n\n for (const [eventName, entries] of Object.entries(settings.hooks)) {\n if (!entries) continue;\n\n const filteredEntries: HookEntry[] = [];\n\n for (const entry of entries) {\n if (!entry.hooks || entry.hooks.length === 0) {\n // No inner hooks — keep as-is (not a ClearGate entry)\n filteredEntries.push(entry);\n continue;\n }\n\n const remainingInnerHooks = entry.hooks.filter(\n (h) => !isClearGateCommand(h.command)\n );\n\n if (remainingInnerHooks.length === 0) {\n // All inner hooks were ClearGate — drop this HookEntry entirely\n continue;\n }\n\n if (remainingInnerHooks.length === entry.hooks.length) {\n // No change — keep original entry reference\n filteredEntries.push(entry);\n } else {\n // Some removed — keep entry with remaining inner hooks\n filteredEntries.push({ ...entry, hooks: remainingInnerHooks });\n }\n }\n\n if (filteredEntries.length > 0) {\n newHooks[eventName] = filteredEntries;\n }\n // If filteredEntries is empty, skip the key entirely\n }\n\n const result: ClaudeSettings = { ...settings };\n\n if (Object.keys(newHooks).length > 0) {\n result.hooks = newHooks;\n } else {\n delete result.hooks;\n }\n\n return result;\n}\n\n/**\n * Returns true if settings contains any ClearGate-owned hook entries.\n */\nexport function hasClearGateHooks(settings: ClaudeSettings): boolean {\n if (!settings.hooks) return false;\n\n for (const entries of Object.values(settings.hooks)) {\n if (!entries) continue;\n for (const entry of entries) {\n if (!entry.hooks) continue;\n for (const h of entry.hooks) {\n if (isClearGateCommand(h.command)) return true;\n }\n }\n }\n\n return false;\n}\n","/**\n * merge-ui.ts — STORY-009-05\n *\n * Diff renderer + 3-choice interactive prompt for `cleargate upgrade`.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { createPatch } from 'diff';\nimport type { DriftState } from './manifest.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeChoice = 'k' | 't' | 'e';\n\n// ─── Diff renderer ────────────────────────────────────────────────────────────\n\n/**\n * Render an inline unified diff of `ours` vs `theirs` for the given `filePath`.\n * Uses the `diff` npm package's `createPatch`.\n */\nexport function renderInlineDiff(ours: string, theirs: string, filePath: string): string {\n return createPatch(filePath, ours, theirs, 'installed', 'upstream');\n}\n\n// ─── Prompt ───────────────────────────────────────────────────────────────────\n\n/**\n * Print file info + inline diff, then prompt the user for a merge choice.\n * Returns one of: 'k' (keep mine), 't' (take theirs), 'e' (edit in $EDITOR).\n *\n * Test seam: pass `stdin` to drive from a buffer instead of process.stdin.\n * Test seam: pass `stdout` to capture output instead of process.stdout.write.\n */\nexport async function promptMergeChoice(opts: {\n path: string;\n state: DriftState;\n ours: string;\n theirs: string;\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n}): Promise<MergeChoice> {\n const { path: filePath, state, ours, theirs } = opts;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stdin = opts.stdin ?? process.stdin;\n\n // Print file header\n stdout(`\\n[merge] ${filePath} state=${state}\\n`);\n\n // Print diff\n const patch = renderInlineDiff(ours, theirs, filePath);\n stdout(patch + '\\n');\n\n // Prompt\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR: ');\n\n return new Promise<MergeChoice>((resolve, reject) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n const answer = buf.slice(0, newline).trim().toLowerCase();\n stdin.removeListener('data', onData);\n stdin.removeListener('error', onError);\n if (answer === 'k' || answer === 't' || answer === 'e') {\n resolve(answer as MergeChoice);\n } else {\n // Default to 'k' (keep mine) for unrecognised input\n stdout(`Unknown choice '${answer}'; defaulting to [k]eep mine.\\n`);\n resolve('k');\n }\n }\n };\n\n const onError = (err: Error) => {\n stdin.removeListener('data', onData);\n reject(err);\n };\n\n stdin.on('data', onData);\n stdin.once('error', onError);\n });\n}\n","/**\n * editor.ts — STORY-009-05\n *\n * Safely spawn $EDITOR for the `cleargate upgrade` edit-in-editor flow.\n * Uses child_process.spawn with stdio: 'inherit' — does NOT use execSync\n * (would block the event loop and prevent test injection).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { spawn } from 'node:child_process';\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Spawn $EDITOR for `filePath`, waiting for the editor process to exit.\n *\n * Returns the editor's exit code.\n * If `opts.editor` is provided, uses it instead of $EDITOR.\n * If neither is set, returns `{ exitCode: -1 }` with an error (handled by caller).\n *\n * Uses `stdio: 'inherit'` so the terminal is fully connected to the editor.\n * Do NOT use execSync — it blocks the event loop and breaks tests.\n */\nexport async function openInEditor(\n filePath: string,\n opts?: { editor?: string; env?: NodeJS.ProcessEnv }\n): Promise<{ exitCode: number }> {\n const env = opts?.env ?? process.env;\n const editor = opts?.editor ?? env['EDITOR'] ?? env['VISUAL'];\n\n if (!editor) {\n throw new Error('$EDITOR not set; cannot [e]dit option. Set the EDITOR environment variable.');\n }\n\n return new Promise<{ exitCode: number }>((resolve, reject) => {\n const child = spawn(editor, [filePath], {\n stdio: 'inherit',\n env: { ...env },\n });\n\n child.on('error', (err) => {\n reject(new Error(`Failed to start editor '${editor}': ${err.message}`));\n });\n\n child.on('close', (code) => {\n resolve({ exitCode: code ?? 0 });\n });\n });\n}\n\n/**\n * Return true if `content` contains unresolved conflict markers.\n *\n * Checks for any of:\n * <<<<<<< ours\n * =======\n * >>>>>>> theirs\n */\nexport function containsConflictMarkers(content: string): boolean {\n return (\n content.includes('<<<<<<< ours') ||\n content.includes('>>>>>>> theirs') ||\n // Also catch generic git-style markers in case user merged manually\n /^<<<<<<< /m.test(content) ||\n /^>>>>>>> /m.test(content)\n );\n}\n","/**\n * uninstall.ts — STORY-009-07\n *\n * `cleargate uninstall [--dry-run] [--preserve <tiers>] [--remove <tiers>] [--yes] [--path <dir>] [--force]`\n *\n * Most-destructive command in the CLI. Preservation-first: user artifacts\n * (FLASHCARD, archive, pending-sync, sprint retrospectives) are kept by default.\n * Framework files (knowledge, templates, wiki, hook-log, agents, hooks, skills)\n * are removed. CLAUDE.md and settings.json are surgically stripped — the rest\n * of those files is left intact.\n *\n * Safety rails:\n * - Typed confirmation: user must type the project name (or pass --yes).\n * - Uncommitted-changes check: git status --porcelain on manifest-tracked files.\n * - --dry-run: preview only, zero disk writes.\n * - Single-target: does NOT recurse into nested .cleargate/ directories.\n * - Idempotency: if .uninstalled marker exists and .install-manifest.json is absent → \"already uninstalled\".\n *\n * All test-facing behaviours are injectable via UninstallOptions seams.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport {\n loadInstallSnapshot,\n type ManifestFile,\n type ManifestEntry,\n type Tier,\n} from '../lib/manifest.js';\nimport {\n removeBlock,\n CLEARGATE_START,\n CLEARGATE_END,\n} from '../lib/claude-md-surgery.js';\nimport { removeClearGateHooks, type ClaudeSettings } from '../lib/settings-json-surgery.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\n/** Tiers that belong to \"user-artifact\" bucket (preserved by default). */\nconst USER_ARTIFACT_TIERS: Tier[] = ['user-artifact'];\n\n/** Framework tiers that are removed by default. */\nconst FRAMEWORK_TIERS: Tier[] = ['protocol', 'template', 'agent', 'hook', 'skill', 'cli-config', 'derived'];\n\nexport interface UninstallOptions {\n cwd?: string;\n path?: string; // resolved target dir (defaults to cwd)\n dryRun?: boolean;\n yes?: boolean; // skip typed confirmation\n force?: boolean; // override uncommitted-changes refusal\n preserve?: string[]; // tier ids to force-preserve (comma-split at CLI level)\n remove?: string[]; // tier ids to force-remove (comma-split at CLI level, 'all' = all tiers)\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Test seam: prompt for typed project-name confirmation. */\n promptName?: () => Promise<string>;\n /** Test seam: prompt yes/no for interactive category decisions. */\n promptYesNo?: (q: string) => Promise<boolean>;\n /** Test seam: injectable clock. */\n now?: () => Date;\n /** Test seam: git status --porcelain runner. Returns { stdout, code }. */\n git?: (args: string[]) => { stdout: string; code: number };\n}\n\nexport interface UninstalledMarker {\n uninstalled_at: string;\n prior_version: string;\n preserved: string[];\n removed: string[];\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\n/**\n * Parse a tier list string (comma-separated) into a Set.\n * 'all' expands to all known tiers.\n */\nfunction parseTierList(raw: string[]): Set<Tier> {\n const result = new Set<Tier>();\n for (const item of raw) {\n for (const t of item.split(',')) {\n const tier = t.trim();\n if (tier === 'all') {\n for (const f of FRAMEWORK_TIERS) result.add(f);\n for (const u of USER_ARTIFACT_TIERS) result.add(u);\n } else {\n result.add(tier as Tier);\n }\n }\n }\n return result;\n}\n\n/**\n * Determine if an entry should be preserved (true) or removed (false).\n *\n * Rules (highest priority first):\n * 1. --remove tier override → remove\n * 2. --preserve tier override → preserve\n * 3. user-artifact tier → preserve (default)\n * 4. Framework tiers (protocol/template/agent/hook/skill/cli-config/derived) → remove (default)\n */\nexport function shouldPreserve(\n entry: ManifestEntry,\n preserveSet: Set<Tier>,\n removeSet: Set<Tier>\n): boolean {\n if (removeSet.has(entry.tier)) return false;\n if (preserveSet.has(entry.tier)) return true;\n if (USER_ARTIFACT_TIERS.includes(entry.tier)) return true;\n return false;\n}\n\n/**\n * Read the project name from package.json (name field), or fall back to\n * the basename of the target directory.\n */\nfunction resolveProjectName(target: string): string {\n const pkgPath = path.join(target, 'package.json');\n if (fs.existsSync(pkgPath)) {\n try {\n const raw = fs.readFileSync(pkgPath, 'utf-8');\n const parsed = JSON.parse(raw) as { name?: string };\n if (parsed.name && typeof parsed.name === 'string') {\n return parsed.name;\n }\n } catch {\n // Fall through to basename\n }\n }\n return path.basename(target);\n}\n\n/**\n * Run git status --porcelain in the target directory, filtered to manifest-tracked paths.\n * Returns the list of uncommitted files that overlap with the manifest.\n *\n * Non-git targets return an empty array (skip silently per orchestrator decision).\n */\nfunction detectUncommittedChanges(\n target: string,\n manifestPaths: string[],\n gitRunner?: UninstallOptions['git']\n): string[] {\n const run = gitRunner ?? ((args: string[]) => {\n try {\n const out = execSync(['git', ...args].join(' '), {\n cwd: target,\n stdio: ['pipe', 'pipe', 'pipe'],\n encoding: 'utf-8',\n });\n return { stdout: out, code: 0 };\n } catch (e: unknown) {\n const err = e as { stdout?: string; status?: number };\n return { stdout: err.stdout ?? '', code: err.status ?? 1 };\n }\n });\n\n // First check if this is a git repo at all\n const isGit = run(['-C', target, 'rev-parse', '--is-inside-work-tree']);\n if (isGit.code !== 0) {\n // Not a git repo — skip silently (orchestrator decision)\n return [];\n }\n\n const result = run(['-C', target, 'status', '--porcelain']);\n if (result.code !== 0) {\n return [];\n }\n\n // Parse porcelain output — each line: XY path\n const changedFiles = result.stdout\n .split('\\n')\n .filter(Boolean)\n .map((line) => line.slice(3).trim());\n\n // Only flag files that are listed in the install manifest\n const manifestSet = new Set(manifestPaths);\n return changedFiles.filter((f) => manifestSet.has(f));\n}\n\n/**\n * Remove @cleargate/cli from package.json dependencies/devDependencies.\n * Writes back if modified. Returns true if modified.\n */\nasync function removeFromPackageJson(target: string, dryRun: boolean): Promise<boolean> {\n const pkgPath = path.join(target, 'package.json');\n if (!fs.existsSync(pkgPath)) return false;\n\n let raw: string;\n try {\n raw = await fsp.readFile(pkgPath, 'utf-8');\n } catch {\n return false;\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return false;\n }\n\n let modified = false;\n\n for (const key of ['dependencies', 'devDependencies'] as const) {\n const deps = parsed[key];\n if (deps && typeof deps === 'object' && '@cleargate/cli' in (deps as Record<string, unknown>)) {\n const updated = { ...(deps as Record<string, unknown>) };\n delete updated['@cleargate/cli'];\n parsed[key] = updated;\n modified = true;\n }\n }\n\n if (modified && !dryRun) {\n await fsp.writeFile(pkgPath, JSON.stringify(parsed, null, 2) + '\\n', 'utf-8');\n }\n\n return modified;\n}\n\n/** Atomically write a file (tmp → rename). */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsp.writeFile(tmpPath, content, 'utf-8');\n await fsp.rename(tmpPath, filePath);\n}\n\n/** Remove a file, silently ignoring missing files. */\nasync function removeFile(filePath: string): Promise<void> {\n try {\n await fsp.unlink(filePath);\n } catch {\n // Ignore ENOENT and other errors\n }\n}\n\n/** Remove a directory tree, silently ignoring missing directories. */\nasync function removeDir(dirPath: string): Promise<void> {\n try {\n fs.rmSync(dirPath, { recursive: true, force: true });\n } catch {\n // Ignore\n }\n}\n\n// ─── Main handler ─────────────────────────────────────────────────────────────\n\nexport async function uninstallHandler(opts: UninstallOptions): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exit = opts.exit ?? ((code: number) => process.exit(code) as never);\n const now = opts.now ?? (() => new Date());\n const dryRun = opts.dryRun ?? false;\n const yes = opts.yes ?? false;\n const force = opts.force ?? false;\n\n // Parse tier overrides\n const preserveSet = parseTierList(opts.preserve ?? []);\n const removeSet = parseTierList(opts.remove ?? []);\n const removeAll = (opts.remove ?? []).some((r) => r === 'all');\n if (removeAll) {\n for (const t of FRAMEWORK_TIERS) removeSet.add(t);\n for (const u of USER_ARTIFACT_TIERS) removeSet.add(u);\n }\n\n // ─── 1. Resolve target ────────────────────────────────────────────────────────\n\n const target = opts.path ? path.resolve(opts.path) : cwd;\n const cleargateDir = path.join(target, '.cleargate');\n const manifestPath = path.join(cleargateDir, '.install-manifest.json');\n const uninstalledPath = path.join(cleargateDir, '.uninstalled');\n\n // ─── 2. Missing manifest → no install detected ────────────────────────────────\n\n if (!fs.existsSync(manifestPath)) {\n // Check idempotency: if .uninstalled exists without manifest → already done\n if (fs.existsSync(uninstalledPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 3. Idempotency short-circuit ────────────────────────────────────────────\n\n if (fs.existsSync(uninstalledPath) && !fs.existsSync(manifestPath)) {\n stdout('already uninstalled');\n exit(0);\n return;\n }\n\n // ─── 4. Load install manifest ────────────────────────────────────────────────\n\n const snapshot: ManifestFile | null = await loadInstallSnapshot(target);\n if (!snapshot) {\n stdout(`no ClearGate install detected at ${target}`);\n exit(0);\n return;\n }\n\n // ─── 5. Uncommitted-changes check ────────────────────────────────────────────\n\n if (!force) {\n const manifestPaths = snapshot.files.map((e) => e.path);\n const uncommitted = detectUncommittedChanges(target, manifestPaths, opts.git);\n if (uncommitted.length > 0) {\n stderr(\n `Uncommitted changes to tracked files: ${uncommitted.slice(0, 5).join(', ')}${uncommitted.length > 5 ? ` and ${uncommitted.length - 5} more` : ''}. Commit, stash, or pass --force.`\n );\n exit(1);\n return;\n }\n }\n\n // ─── 6. CLAUDE.md marker check ───────────────────────────────────────────────\n\n const claudeMdPath = path.join(target, 'CLAUDE.md');\n let claudeMdContent: string | null = null;\n\n if (fs.existsSync(claudeMdPath)) {\n claudeMdContent = fs.readFileSync(claudeMdPath, 'utf-8');\n if (!claudeMdContent.includes(CLEARGATE_START)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:START --> marker');\n exit(1);\n return;\n }\n if (!claudeMdContent.includes(CLEARGATE_END)) {\n stderr('CLAUDE.md is missing <!-- CLEARGATE:END --> marker');\n exit(1);\n return;\n }\n }\n\n // ─── 7. Classify entries ──────────────────────────────────────────────────────\n\n const toRemove: ManifestEntry[] = [];\n const toPreserve: ManifestEntry[] = [];\n const toSkip: ManifestEntry[] = []; // missing from disk\n\n for (const entry of snapshot.files) {\n const filePath = path.join(target, entry.path);\n if (!fs.existsSync(filePath)) {\n toSkip.push(entry);\n continue;\n }\n if (shouldPreserve(entry, preserveSet, removeSet)) {\n toPreserve.push(entry);\n } else {\n toRemove.push(entry);\n }\n }\n\n // ─── 8. Preview summary ───────────────────────────────────────────────────────\n\n stdout(`Will remove ${toRemove.length} files, keep ${toPreserve.length} files, update CLAUDE.md to strip CLEARGATE block, remove @cleargate/cli from package.json.`);\n\n if (dryRun) {\n stdout('');\n stdout('[dry-run] Planned removals:');\n for (const e of toRemove) {\n stdout(` [remove] ${e.path}`);\n }\n stdout('');\n stdout('[dry-run] Planned preservations:');\n for (const e of toPreserve) {\n stdout(` [keep] ${e.path}`);\n }\n if (toSkip.length > 0) {\n stdout('');\n stdout('[dry-run] Untracked (already missing on disk):');\n for (const e of toSkip) {\n stdout(` [skip] ${e.path}`);\n }\n }\n stdout('');\n stdout('[dry-run] No files changed.');\n exit(0);\n return;\n }\n\n // ─── 9. Typed confirmation (skipped with --yes) ───────────────────────────────\n\n if (!yes) {\n const projectName = resolveProjectName(target);\n\n const promptNameFn = opts.promptName ?? (() => {\n // Real interactive prompt — readline\n return new Promise<string>((resolve) => {\n process.stdout.write(`Type the project name \"${projectName}\" to confirm uninstall: `);\n let buf = '';\n process.stdin.setEncoding('utf-8');\n process.stdin.once('data', (chunk: Buffer | string) => {\n buf = chunk.toString().trim();\n resolve(buf);\n });\n });\n });\n\n const typed = await promptNameFn();\n if (typed !== projectName) {\n stdout('name mismatch — aborting');\n exit(1);\n return;\n }\n }\n\n // ─── 10. Execute removal ──────────────────────────────────────────────────────\n\n const removedPaths: string[] = [];\n const preservedPaths: string[] = [];\n\n // Remove classified entries\n for (const entry of toRemove) {\n const filePath = path.join(target, entry.path);\n await removeFile(filePath);\n removedPaths.push(entry.path);\n }\n\n // Collect preserved paths\n for (const entry of toPreserve) {\n preservedPaths.push(entry.path);\n }\n\n // Surgery: CLAUDE.md — strip only the CLEARGATE block\n if (claudeMdContent !== null) {\n try {\n const stripped = removeBlock(claudeMdContent);\n await writeAtomic(claudeMdPath, stripped);\n removedPaths.push('CLAUDE.md (CLEARGATE block)');\n } catch (err) {\n stderr(`Warning: could not strip CLAUDE.md block: ${(err as Error).message}`);\n }\n }\n\n // Surgery: settings.json — remove ClearGate hooks only\n const settingsPath = path.join(target, '.claude', 'settings.json');\n if (fs.existsSync(settingsPath)) {\n try {\n const raw = fs.readFileSync(settingsPath, 'utf-8');\n const settings = JSON.parse(raw) as ClaudeSettings;\n const cleaned = removeClearGateHooks(settings);\n await writeAtomic(settingsPath, JSON.stringify(cleaned, null, 2) + '\\n');\n removedPaths.push('.claude/settings.json (ClearGate hooks)');\n } catch (err) {\n stderr(`Warning: could not update settings.json: ${(err as Error).message}`);\n }\n }\n\n // Remove @cleargate/cli from package.json\n const pkgModified = await removeFromPackageJson(target, false);\n if (pkgModified) {\n removedPaths.push('package.json (@cleargate/cli dep)');\n stdout('Removed @cleargate/cli from package.json. Run `npm install` to update package-lock.json.');\n }\n\n // Remove the install manifest itself (and drift-state)\n await removeFile(manifestPath);\n await removeFile(path.join(cleargateDir, '.drift-state.json'));\n\n // ─── 11. Write .uninstalled marker ───────────────────────────────────────────\n\n const marker: UninstalledMarker = {\n uninstalled_at: now().toISOString(),\n prior_version: snapshot.cleargate_version,\n preserved: preservedPaths,\n removed: removedPaths,\n };\n\n // Ensure .cleargate/ dir still exists (may have had files removed from it)\n await fsp.mkdir(cleargateDir, { recursive: true });\n await writeAtomic(uninstalledPath, JSON.stringify(marker, null, 2) + '\\n');\n\n // ─── 12. Empty .cleargate/ cleanup ───────────────────────────────────────────\n\n // Per story Gherkin scenario 11: when --remove all removes all user-artifact\n // items too (nothing preserved inside .cleargate/), the directory itself is\n // removed (including the .uninstalled marker we just wrote).\n // In the default case (preservations exist), we leave .cleargate/ with the\n // preserved files + .uninstalled marker.\n if (removeAll) {\n const hasPreservedInsideCleargate = preservedPaths.some((p) =>\n p.startsWith('.cleargate/')\n );\n if (!hasPreservedInsideCleargate) {\n // Remove the entire .cleargate/ directory (including the marker we wrote)\n await removeDir(cleargateDir);\n }\n }\n\n // ─── 13. Restore hint ────────────────────────────────────────────────────────\n\n if (preservedPaths.length > 0) {\n stdout(`Preserved ${preservedPaths.length} items. Run cleargate init in this directory to restore.`);\n }\n}\n","/**\n * sync.ts — STORY-010-04\n *\n * `cleargate sync` — canonical 6-step driver:\n * 1. Identity + sprint resolve\n * 2. List remote updates + pull each item\n * (3. STORY-010-05 insertion point: runIntakeBranch)\n * 4. Classify conflicts per local work items\n * 5. Resolve (merge prompt / silent merge / remote-wins / refuse)\n * 6. Apply + log\n *\n * R2 invariant: ALL pulls complete before ANY push begins.\n *\n * --dry-run: steps 1–4 only; zero fs writes; zero sync-log entries.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { classify } from '../lib/conflict-detector.js';\nimport type { SinceLastSync, LocalSnapshot, RemoteSnapshot } from '../lib/conflict-detector.js';\nimport { promptThreeWayMerge } from '../lib/merge-helper.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteUpdateRef } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { runIntakeBranch } from '../lib/intake.js';\nimport type { IntakeResult } from '../lib/intake.js';\nimport { resolveActiveItems } from '../lib/active-criteria.js';\nimport type { LocalWorkItemRef } from '../lib/active-criteria.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\nimport type { RemoteComment } from '../lib/mcp-client.js';\n\n// ── Public Options ─────────────────────────────────────────────────────────────\n\nexport interface SyncOptions {\n dryRun?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (bypasses acquireAccessToken) */\n mcp?: McpClient;\n /** Test seam: override process.stdin for merge prompt */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── SyncCheckOptions ──────────────────────────────────────────────────────────\n\nexport interface SyncCheckOptions {\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n/**\n * syncCheckHandler — `cleargate sync --check`\n *\n * Hook-safe read-only drift probe. Exits 0 on ALL failure paths.\n * Emits a single JSON line to stdout: {\"updates\":<N>,\"since\":\"<iso>\"} on success\n * or {\"updates\":0,\"error\":\"<msg>\"} on failure.\n *\n * Never writes sync-log entries, never touches .conflicts.json, never pushes.\n * Updates .cleargate/.sync-marker.json with last_check timestamp (even on error,\n * to throttle repeat attempts).\n *\n * FLASHCARD: #hook-safe #sync-check #exit-code\n * syncHandler exits 2 on missing token/URL/adapter. This handler MUST NOT.\n */\nexport async function syncCheckHandler(opts: SyncCheckOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n const markerPath = path.join(projectRoot, '.cleargate', '.sync-marker.json');\n\n // Helper: write marker atomically, even on error (throttle repeat calls)\n const updateMarker = async (nowIso: string): Promise<void> => {\n try {\n const content = JSON.stringify({ last_check: nowIso });\n await fsPromises.mkdir(path.dirname(markerPath), { recursive: true });\n const tmpPath = `${markerPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, markerPath);\n } catch {\n // Ignore marker write failures — do not surface to caller\n }\n };\n\n // Helper: emit error JSON and exit 0\n const emitError = async (msg: string, nowIso: string): Promise<void> => {\n stdout(JSON.stringify({ updates: 0, error: msg.slice(0, 200) }) + '\\n');\n await updateMarker(nowIso);\n };\n\n const nowIso = nowFn();\n\n // Read last_check from marker; fall back to epoch\n let since = '1970-01-01T00:00:00.000Z';\n try {\n const raw = await fsPromises.readFile(markerPath, 'utf8');\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n if (typeof parsed['last_check'] === 'string') {\n since = parsed['last_check'];\n }\n } catch {\n // No marker file or parse error — use epoch\n }\n\n // Resolve MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n // Acquire token — hook-safe: any AcquireError → emitError, exit 0\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Pre-flight: adapter info (but don't exit 2 — emit error JSON instead)\n try {\n const adapterInfo = await mcp.adapterInfo();\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n await emitError('adapter-not-configured', nowIso);\n return;\n }\n } catch {\n // If adapterInfo fails, proceed — older MCP may not have this tool\n }\n\n // Call list_remote_updates\n let refs: RemoteUpdateRef[];\n try {\n refs = await mcp.call<RemoteUpdateRef[]>('cleargate_list_remote_updates', { since });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n await emitError(msg, nowIso);\n return;\n }\n\n // Success path\n stdout(JSON.stringify({ updates: refs.length, since }) + '\\n');\n await updateMarker(nowIso);\n}\n\n// ── ConflictJson shape ────────────────────────────────────────────────────────\n\nexport interface ConflictEntry {\n item_id: string;\n remote_id: string;\n state: string;\n resolution: string;\n reason: string;\n local_path: string;\n}\n\nexport interface ConflictsJson {\n generated_at: string;\n sprint_id: string;\n unresolved: ConflictEntry[];\n}\n\n// ── Main handler ──────────────────────────────────────────────────────────────\n\nexport async function syncHandler(opts: SyncOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const dryRun = opts.dryRun ?? false;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // ── Step 1: Identity + sprint resolve ───────────────────────────────────────\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintRoot);\n\n // ── MCP client setup ────────────────────────────────────────────────────────\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n if (err.code === 'token_revoked') {\n stderr(`Error: ${err.message}\\n`);\n } else if (err.code === 'no_stored_token' || err.code === 'invalid_token') {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${err.message}\\n`);\n }\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Pre-flight: adapter-info probe ──────────────────────────────────────────\n let adapterInfo: { configured: boolean; name: string };\n try {\n adapterInfo = await mcp.adapterInfo();\n } catch {\n // Tool may not exist on older MCP versions — warn and proceed\n adapterInfo = { configured: true, name: 'unknown' };\n }\n\n if (!adapterInfo.configured || adapterInfo.name === 'no-adapter-configured') {\n stderr(\n 'Error: ClearGate MCP has no PM adapter configured (LINEAR_API_KEY missing server-side). ' +\n 'Sync cannot proceed.\\n',\n );\n exit(2);\n return;\n }\n\n // ── Step 2: List remote updates + pull ──────────────────────────────────────\n // Read last_remote_sync from wiki meta or use epoch\n const wikiMetaPath = path.join(projectRoot, '.cleargate', 'wiki', 'meta.json');\n let lastRemoteSync = '1970-01-01T00:00:00.000Z';\n try {\n const metaRaw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n const meta = JSON.parse(metaRaw) as Record<string, unknown>;\n if (typeof meta['last_remote_sync'] === 'string') {\n lastRemoteSync = meta['last_remote_sync'];\n }\n } catch {\n // No meta file — start from epoch\n }\n\n const remoteRefs = await mcp.call<RemoteUpdateRef[]>(\n 'cleargate_list_remote_updates',\n { since: lastRemoteSync },\n );\n\n // Pull each item — all awaited BEFORE any push (R2 invariant)\n const pulled: RemoteItem[] = [];\n for (const ref of remoteRefs) {\n const item = await mcp.call<RemoteItem | null>(\n 'cleargate_pull_item',\n { remote_id: ref.remote_id },\n );\n if (item !== null) {\n pulled.push(item);\n }\n }\n\n // ── Step 3: Stakeholder proposal intake (STORY-010-05) ──────────────────────\n const labelFilter = (opts.env ?? process.env)['CLEARGATE_PROPOSAL_LABEL'] ?? 'cleargate:proposal';\n let intakeResult: IntakeResult = { created: 0, items: [] };\n try {\n intakeResult = await runIntakeBranch({\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter,\n now: nowFn,\n });\n } catch (err) {\n // Non-fatal: intake errors do not abort the main sync loop\n stderr(`warn: intake branch failed: ${String(err)}\\n`);\n }\n\n // Emit R10 warning if present\n if (intakeResult.warning) {\n stderr(`${intakeResult.warning}\\n`);\n }\n\n // ── Step 3b: Pull comments for active items (STORY-010-06) ─────────────────\n // Load all local items first (needed for active-criteria + wiki render).\n const localItems = await scanLocalItems(projectRoot);\n\n if (!dryRun) {\n const localRefs: LocalWorkItemRef[] = localItems.map(({ fm }) => ({\n primaryId: getItemId(fm),\n remoteId: typeof fm['remote_id'] === 'string' && fm['remote_id'] ? fm['remote_id'] : undefined,\n lastRemoteUpdate: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : undefined,\n }));\n\n const activeSet = await resolveActiveItems(projectRoot, localRefs, nowFn);\n\n for (const remoteId of activeSet) {\n try {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n await renderCommentsSection({ projectRoot, remoteId, comments, localItems });\n } catch (err: unknown) {\n // R4 mitigation: per-item try/catch — do NOT break the outer loop\n const errMsg = err instanceof Error ? err.message : String(err);\n if (/MCP HTTP 429/.test(errMsg)) {\n // Find primary ID for logging\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'skipped-rate-limit',\n detail: '429',\n });\n } else {\n // Other errors: log as error-transport, continue\n const localRef = localRefs.find((r) => r.remoteId === remoteId);\n const target = localRef?.primaryId ?? remoteId;\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull-comments',\n target,\n remote_id: remoteId,\n result: 'error-transport',\n detail: errMsg.slice(0, 200),\n });\n }\n }\n }\n }\n\n // ── Step 4: Classify local work items ───────────────────────────────────────\n // localItems already loaded above for comment-pull; reuse here.\n const conflictsJson: ConflictEntry[] = [];\n\n // Maps for tracking what to apply\n type QueuedPull = { item: RemoteItem; localPath: string; fm: Record<string, unknown>; body: string };\n type QueuedPush = { localPath: string; fm: Record<string, unknown>; body: string; itemId: string };\n\n const pullQueue: QueuedPull[] = [];\n const pushQueue: QueuedPush[] = [];\n\n const pulledByRemoteId = new Map<string, RemoteItem>();\n for (const item of pulled) {\n pulledByRemoteId.set(item.remote_id, item);\n }\n\n let dryRunPulls = 0;\n let dryRunPushes = 0;\n let dryRunConflicts = 0;\n\n for (const { localPath, fm, body } of localItems) {\n const remoteIdVal = fm['remote_id'];\n if (typeof remoteIdVal !== 'string' || !remoteIdVal) continue;\n\n const remoteItem = pulledByRemoteId.get(remoteIdVal);\n if (!remoteItem) continue;\n\n const lastBodySha = typeof fm['last_synced_body_sha'] === 'string' ? fm['last_synced_body_sha'] : null;\n const localBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n\n const localSnap: LocalSnapshot = {\n updated_at: typeof fm['updated_at'] === 'string' ? fm['updated_at'] : '1970-01-01T00:00:00Z',\n body_sha: localBodySha,\n status: typeof fm['status'] === 'string' ? fm['status'] : '',\n deleted: false,\n };\n\n const remoteSnap: RemoteSnapshot = {\n updated_at: remoteItem.updated_at,\n body_sha: remoteBodySha,\n status: remoteItem.status,\n deleted: false,\n };\n\n const since: SinceLastSync = {\n last_pushed_at: typeof fm['pushed_at'] === 'string' ? fm['pushed_at'] : null,\n last_pulled_at: typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null,\n last_remote_update: typeof fm['last_remote_update'] === 'string' ? fm['last_remote_update'] : null,\n last_body_sha: lastBodySha,\n last_synced_status: typeof fm['last_synced_status'] === 'string' ? fm['last_synced_status'] : null,\n };\n\n const classification = classify(localSnap, remoteSnap, since);\n\n if (dryRun) {\n if (classification.resolution === 'pull') dryRunPulls++;\n else if (classification.resolution === 'push') dryRunPushes++;\n else if (classification.resolution === 'refuse' || classification.resolution === 'halt') dryRunConflicts++;\n continue;\n }\n\n // ── Step 5: Resolve ──────────────────────────────────────────────────────\n switch (classification.resolution) {\n case 'pull':\n case 'merge-silent':\n case 'remote-wins':\n pullQueue.push({ item: remoteItem, localPath, fm, body });\n break;\n\n case 'push':\n if (fm['approved'] === true) {\n pushQueue.push({ localPath, fm, body, itemId: getItemId(fm) });\n }\n break;\n\n case 'merge': {\n // Three-way merge prompt\n const mergeResult = await promptThreeWayMerge({\n local: body,\n remote: remoteItem.body ?? '',\n base: '',\n itemId: getItemId(fm),\n stdin: opts.stdin ?? process.stdin,\n stdout,\n });\n if (mergeResult.resolution === 'aborted') {\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n } else {\n // Apply merge result\n pullQueue.push({\n item: { ...remoteItem, body: mergeResult.body },\n localPath,\n fm,\n body,\n });\n }\n break;\n }\n\n case 'refuse':\n case 'halt':\n conflictsJson.push({\n item_id: getItemId(fm),\n remote_id: remoteIdVal,\n state: classification.state,\n resolution: classification.resolution,\n reason: classification.reason,\n local_path: localPath,\n });\n // Do NOT abort — continue processing remaining items (AC §2.1)\n break;\n\n default:\n break;\n }\n }\n\n // ── dry-run summary and early exit ──────────────────────────────────────────\n if (dryRun) {\n stdout(\n `Would pull: ${dryRunPulls}, push: ${dryRunPushes}, ` +\n `intake: ${intakeResult.created}, conflicts: ${dryRunConflicts}\\n`,\n );\n return;\n }\n\n // ── Step 6: Apply + log ──────────────────────────────────────────────────────\n // R2: ALL pulls applied before pushes\n for (const { item, localPath, fm } of pullQueue) {\n await applyPull(item, localPath, fm, identity.email, nowFn);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n for (const { localPath, fm, body, itemId } of pushQueue) {\n // Push item to MCP\n await mcp.call('push_item', {\n cleargate_id: itemId,\n type: typeof fm['story_id'] === 'string' ? 'story'\n : typeof fm['epic_id'] === 'string' ? 'epic'\n : typeof fm['proposal_id'] === 'string' ? 'proposal'\n : 'story',\n payload: fm,\n });\n\n // Stamp last_synced_status + last_synced_body_sha so classify() sees a clean\n // baseline on the next sync run (mirrors the pull-apply path at applyPull:393-394).\n const pushedFm: Record<string, unknown> = {\n ...fm,\n last_synced_status: fm['status'],\n last_synced_body_sha: hashNormalized(body),\n };\n const newContent = serializeFrontmatter(pushedFm) + '\\n\\n' + body;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Log conflicts\n for (const c of conflictsJson) {\n const entry: SyncLogEntry = {\n ts: nowFn(),\n actor: identity.email,\n op: 'conflict-refused',\n target: c.item_id,\n remote_id: c.remote_id,\n result: 'halted',\n detail: c.reason,\n };\n await appendSyncLog(sprintRoot, entry);\n }\n\n // Atomic-write .conflicts.json\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n const conflictsContent: ConflictsJson = {\n generated_at: nowFn(),\n sprint_id: sprintId,\n unresolved: conflictsJson,\n };\n await writeAtomic(conflictsFile, JSON.stringify(conflictsContent, null, 2) + '\\n');\n\n // Update wiki meta last_remote_sync\n try {\n await fsPromises.mkdir(path.dirname(wikiMetaPath), { recursive: true });\n let meta: Record<string, unknown> = {};\n try {\n const raw = await fsPromises.readFile(wikiMetaPath, 'utf8');\n meta = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // New meta\n }\n meta['last_remote_sync'] = nowFn();\n await writeAtomic(wikiMetaPath, JSON.stringify(meta, null, 2) + '\\n');\n } catch {\n // Non-fatal\n }\n\n const totalPulls = pullQueue.length;\n const totalPushes = pushQueue.length;\n const totalConflicts = conflictsJson.length;\n stdout(`sync: pulled ${totalPulls}, pushed ${totalPushes}, conflicts ${totalConflicts}\\n`);\n\n // Print intake summary (orchestrator stdout format)\n if (intakeResult.created > 0) {\n const plural = intakeResult.created === 1 ? 'proposal' : 'proposals';\n const inlineList = intakeResult.items\n .map((item) => `${item.proposalId} (${item.remoteId} '${item.title}')`)\n .join(', ');\n stdout(`📥 ${intakeResult.created} new stakeholder ${plural} pulled: ${inlineList}\\n`);\n stdout(` — review at .cleargate/delivery/pending-sync/\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/** Apply a remote item to the local file: update frontmatter + body. */\nasync function applyPull(\n item: RemoteItem,\n localPath: string,\n fm: Record<string, unknown>,\n actorEmail: string,\n nowFn: () => string,\n): Promise<void> {\n const now = nowFn();\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: item.status,\n last_pulled_by: actorEmail,\n last_pulled_at: now,\n last_remote_update: item.updated_at,\n last_synced_status: item.status,\n last_synced_body_sha: hashNormalized(item.body ?? ''),\n };\n\n const newBody = item.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n}\n\n/** Atomic write: write to .tmp file then rename. */\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\ninterface LocalWorkItem {\n localPath: string;\n fm: Record<string, unknown>;\n body: string;\n}\n\n/** Scan .cleargate/delivery/pending-sync/ for tracked work items with remote_id. */\nasync function scanLocalItems(projectRoot: string): Promise<LocalWorkItem[]> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const results: LocalWorkItem[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm, body } = parseFrontmatter(raw);\n if (typeof fm['remote_id'] === 'string' && fm['remote_id']) {\n results.push({ localPath: fullPath, fm, body });\n }\n } catch {\n // Skip malformed files\n }\n }\n\n return results;\n}\n\n/** Extract primary item ID from frontmatter. */\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * sync-log.ts — append-only JSONL sync audit log.\n *\n * STORY-010-01: defines types + appendSyncLog + readSyncLog + resolveActiveSprintDir.\n *\n * Atomicity guarantee for appendSyncLog:\n * Uses fs.promises.appendFile with default flag 'a' (O_APPEND).\n * POSIX guarantees that concurrent O_APPEND writes ≤ PIPE_BUF (4 KB) are atomic\n * — a sync-log line is < 500 bytes so lines from concurrent writers never interleave.\n * We deliberately use appendFile (not read-then-write) to preserve this guarantee.\n *\n * Token redaction:\n * detail fields containing JWT tokens (eyJ…) are redacted before writing.\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\n/** R-014 rule ID constant (defined here for STORY-010-08 to wire into lint pipeline). */\nexport const R014 = 'sync-attribution-missing' as const;\n\nexport type SyncLogOp =\n | 'push'\n | 'pull'\n | 'pull-intake' // STORY-010-05: stakeholder proposal intake\n | 'pull-comments' // STORY-010-06: comment snapshot pull\n | 'push-revert'\n | 'sync-status'\n | 'conflict-remote-wins'\n | 'conflict-refused';\n\nexport type SyncLogResult =\n | 'ok'\n | 'no-op'\n | 'error-not-found'\n | 'error-transport'\n | 'skipped-rate-limit'\n | 'halted';\n\nexport interface SyncLogEntry {\n ts: string;\n actor: string;\n op: SyncLogOp;\n target: string;\n remote_id?: string;\n result: SyncLogResult;\n detail?: string;\n}\n\n// ── Active-sprint resolution ─────────────────────────────────────────────────\n\n/**\n * Resolve the active sprint directory under .cleargate/sprint-runs/.\n *\n * Strategy: scan sprint-runs/* (excluding _off-sprint), pick the entry with the\n * newest mtime. If none exist, create and return _off-sprint/.\n *\n * Open Decision (flagged to orchestrator): story §1.2 says \"read INDEX.md for\n * active sprint\", but INDEX.md has no machine-readable status:active field —\n * only execution_order integers inside sprint frontmatter. Newest-mtime dir is\n * the reliable signal available today.\n */\nexport function resolveActiveSprintDir(\n projectRoot: string,\n _opts?: { now?: () => string },\n): string {\n const sprintRunsRoot = path.join(projectRoot, '.cleargate', 'sprint-runs');\n const offSprint = path.join(sprintRunsRoot, '_off-sprint');\n\n if (!fs.existsSync(sprintRunsRoot)) {\n fs.mkdirSync(sprintRunsRoot, { recursive: true });\n fs.mkdirSync(offSprint, { recursive: true });\n return offSprint;\n }\n\n const entries = fs.readdirSync(sprintRunsRoot, { withFileTypes: true });\n const sprintDirs = entries\n .filter((e) => e.isDirectory() && e.name !== '_off-sprint')\n .map((e) => {\n const fullPath = path.join(sprintRunsRoot, e.name);\n const stat = fs.statSync(fullPath);\n return { name: e.name, fullPath, mtimeMs: stat.mtimeMs };\n })\n .sort((a, b) => b.mtimeMs - a.mtimeMs);\n\n if (sprintDirs.length === 0) {\n if (!fs.existsSync(offSprint)) {\n fs.mkdirSync(offSprint, { recursive: true });\n }\n return offSprint;\n }\n\n return sprintDirs[0].fullPath;\n}\n\n// ── JWT redaction ─────────────────────────────────────────────────────────────\n\nfunction redactDetail(detail: string | undefined): string | undefined {\n if (detail === undefined) return undefined;\n return detail.replace(/eyJ[A-Za-z0-9._-]+/g, '[REDACTED]');\n}\n\n// ── Append ────────────────────────────────────────────────────────────────────\n\n/**\n * Append one JSONL entry to <sprintRoot>/sync-log.jsonl.\n * Creates the file and parent directory if absent.\n * Uses O_APPEND for POSIX atomicity — never read-modify-write.\n */\nexport async function appendSyncLog(\n sprintRoot: string,\n entry: SyncLogEntry,\n): Promise<void> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n // Ensure directory exists\n await fsPromises.mkdir(sprintRoot, { recursive: true });\n\n const safeEntry: SyncLogEntry = {\n ...entry,\n detail: redactDetail(entry.detail),\n };\n\n const line = JSON.stringify(safeEntry) + '\\n';\n\n // appendFile with flag 'a' → O_APPEND; POSIX guarantees line-atomicity for <PIPE_BUF writes\n await fsPromises.appendFile(logPath, line, { encoding: 'utf8' });\n}\n\n// ── Read ──────────────────────────────────────────────────────────────────────\n\n/**\n * Read and optionally filter sync-log entries, returning newest-first.\n * Skips malformed lines silently — partial writes never crash the reader.\n */\nexport async function readSyncLog(\n sprintRoot: string,\n filters?: { actor?: string; op?: SyncLogOp; target?: string },\n): Promise<SyncLogEntry[]> {\n const logPath = path.join(sprintRoot, 'sync-log.jsonl');\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(logPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw err;\n }\n\n const entries: SyncLogEntry[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed === '') continue;\n try {\n const parsed = JSON.parse(trimmed) as SyncLogEntry;\n entries.push(parsed);\n } catch {\n // malformed line — skip silently\n }\n }\n\n // Apply filters\n let result = entries;\n if (filters?.actor !== undefined) {\n result = result.filter((e) => e.actor === filters.actor);\n }\n if (filters?.op !== undefined) {\n result = result.filter((e) => e.op === filters.op);\n }\n if (filters?.target !== undefined) {\n result = result.filter((e) => e.target === filters.target);\n }\n\n // Newest first (entries are appended oldest-first)\n return result.reverse();\n}\n","/**\n * conflict-detector.ts — STORY-010-03\n *\n * Pure classifier for local-vs-remote sync conflicts.\n * Implements the 8-state PROP-007 §2.3 matrix + explicit 9th \"unknown\" fallthrough.\n *\n * No I/O. No imports from node:fs, node:child_process, node:os.\n * No imports from commands/ or bin/.\n */\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type ConflictState =\n | 'no-change'\n | 'local-only' // local content edit, no remote change\n | 'remote-only' // remote status/metadata change, local untouched\n | 'content-content' // both bodies diverged since last sync\n | 'content-status' // local body + remote status\n | 'status-status' // both statuses diverged\n | 'local-delete-remote-edit'\n | 'remote-delete-local-edit'\n | 'unknown'; // R3: explicit fallthrough — never silently wrong\n\nexport type Resolution =\n | 'push'\n | 'pull'\n | 'merge' // three-way merge prompt\n | 'merge-silent' // content+status: no prompt, apply both sides\n | 'remote-wins' // status+status: silent remote takes, log conflict-remote-wins\n | 'refuse' // halt, surface to human\n | 'halt'; // unknown state — halt sync with explicit message\n\nexport interface LocalSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface RemoteSnapshot {\n updated_at: string;\n body_sha: string;\n status: string;\n deleted: boolean;\n}\n\nexport interface SinceLastSync {\n last_pushed_at: string | null;\n last_pulled_at: string | null;\n last_remote_update: string | null; // ISO-8601 string or null; opaque per M1 lock\n last_body_sha: string | null; // sha at last successful sync (merge-base)\n last_synced_status: string | null; // status recorded at last successful sync (rule 6)\n}\n\nexport interface Classification {\n state: ConflictState;\n resolution: Resolution;\n reason: string; // human-readable; used for sync-log detail + halt messages\n}\n\n// ─── Classifier ───────────────────────────────────────────────────────────────\n\n/**\n * classify — pure function; maps a (local, remote, since) triple to a Classification.\n *\n * Decision table follows PROP-007 §2.3 + M2 plan \"8-state + 9th fallthrough\" exactly.\n * States are evaluated in priority order; first match wins.\n */\nexport function classify(\n local: LocalSnapshot,\n remote: RemoteSnapshot,\n since: SinceLastSync,\n): Classification {\n const baseSha = since.last_body_sha ?? '';\n const lastPulled = since.last_pulled_at ?? '0';\n const lastPushed = since.last_pushed_at ?? '0';\n\n // Rule 7 — local-delete-remote-edit (check deletes first — highest priority)\n if (local.deleted && remote.updated_at > lastPulled) {\n return {\n state: 'local-delete-remote-edit',\n resolution: 'refuse',\n reason: 'local deletion conflicts with remote edit',\n };\n }\n\n // Rule 8 — remote-delete-local-edit\n if (remote.deleted && local.updated_at > lastPushed) {\n return {\n state: 'remote-delete-local-edit',\n resolution: 'refuse',\n reason: 'remote deletion conflicts with local edit',\n };\n }\n\n // Rule 1 — no-change: bodies and status are identical at sync base; nothing deleted\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status === remote.status &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'no-change',\n resolution: 'pull',\n reason: 'no change since last sync',\n };\n }\n\n // Rule 4 — content-content: both bodies diverged\n if (\n local.body_sha !== baseSha &&\n remote.body_sha !== baseSha &&\n !local.deleted &&\n !remote.deleted\n ) {\n return {\n state: 'content-content',\n resolution: 'merge',\n reason: 'both bodies diverged — three-way merge required',\n };\n }\n\n // Rule 5 — content-status: local body changed + remote status changed, bodies otherwise aligned\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status !== local.status\n ) {\n return {\n state: 'content-status',\n resolution: 'merge-silent',\n reason: 'local body edit + remote status change — merged without prompt',\n };\n }\n\n // Rule 2 — local-only: local content edited, remote unchanged, same status\n if (\n local.body_sha !== baseSha &&\n remote.body_sha === baseSha &&\n remote.status === local.status &&\n !remote.deleted\n ) {\n return {\n state: 'local-only',\n resolution: 'push',\n reason: 'local content edit only',\n };\n }\n\n // Rule 6 — status-status: both sides changed status since last sync\n // Requires since.last_synced_status to distinguish from remote-only.\n if (\n local.body_sha === baseSha &&\n remote.body_sha === baseSha &&\n local.status !== remote.status &&\n since.last_synced_status !== null &&\n local.status !== since.last_synced_status &&\n remote.status !== since.last_synced_status\n ) {\n return {\n state: 'status-status',\n resolution: 'remote-wins',\n reason: 'status diverged on both sides; remote authoritative',\n };\n }\n\n // Rule 3 — remote-only: local body unchanged, remote status or metadata changed\n if (\n local.body_sha === baseSha &&\n (remote.status !== local.status || remote.updated_at > lastPulled)\n ) {\n return {\n state: 'remote-only',\n resolution: 'pull',\n reason: 'remote status/metadata change only',\n };\n }\n\n // Rule 9 — unknown fallthrough (R3): explicitly refuse to guess\n return {\n state: 'unknown',\n resolution: 'halt',\n reason:\n 'conflict shape not recognized — please resolve manually and file a ClearGate bug with this sync-log entry',\n };\n}\n","/**\n * merge-helper.ts — STORY-010-03\n *\n * Three-way merge prompt for `cleargate sync` content-content conflicts.\n * Reuses EPIC-009 primitives: renderInlineDiff (merge-ui.ts), openInEditor +\n * containsConflictMarkers (editor.ts).\n *\n * Adds the fourth [a]bort branch on top of merge-ui's k/t/e set.\n * Never writes to the caller's file — returns MergeResult; caller applies.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { promises as fs } from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { renderInlineDiff } from './merge-ui.js';\nimport { openInEditor, containsConflictMarkers } from './editor.js';\n\n// ─── Public types ─────────────────────────────────────────────────────────────\n\nexport type MergeResolution = 'keep' | 'take' | 'edited' | 'aborted';\n\nexport interface MergeResult {\n resolution: MergeResolution;\n body: string; // the chosen/edited body\n}\n\nexport interface PromptThreeWayMergeOpts {\n local: string;\n remote: string;\n base: string; // merge-base body; used to construct git-merge-markers on [e]dit\n itemId: string; // for diff header + temp-file name\n stdin?: NodeJS.ReadableStream;\n stdout?: (s: string) => void;\n editor?: string; // override $EDITOR for tests\n now?: () => string; // temp-file name determinism seam\n}\n\n// ─── Four-choice prompt (extends merge-ui's k/t/e with [a]bort) ──────────────\n\ntype FourChoice = 'k' | 't' | 'e' | 'a';\n\n/**\n * promptFourChoice — renders the four-option prompt and reads one line from stdin.\n * Ctrl-C (stream 'close' without data) and 'a' both resolve to 'a' (abort).\n */\nfunction promptFourChoice(opts: {\n stdin: NodeJS.ReadableStream;\n stdout: (s: string) => void;\n}): Promise<FourChoice> {\n const { stdin, stdout } = opts;\n\n stdout('[k]eep mine / [t]ake theirs / [e]dit in $EDITOR / [a]bort: ');\n\n return new Promise<FourChoice>((resolve) => {\n let buf = '';\n\n const onData = (chunk: Buffer | string) => {\n buf += typeof chunk === 'string' ? chunk : chunk.toString('utf-8');\n const newline = buf.indexOf('\\n');\n if (newline !== -1) {\n cleanup();\n const answer = buf.slice(0, newline).trim().toLowerCase();\n if (answer === 'k' || answer === 't' || answer === 'e' || answer === 'a') {\n resolve(answer as FourChoice);\n } else {\n stdout(`Unknown choice '${answer}'; treating as [a]bort.\\n`);\n resolve('a');\n }\n }\n };\n\n // Ctrl-C or stdin close without data → abort\n const onClose = () => {\n cleanup();\n resolve('a');\n };\n\n const onEnd = () => {\n cleanup();\n resolve('a');\n };\n\n const onError = () => {\n cleanup();\n resolve('a');\n };\n\n function cleanup() {\n stdin.removeListener('data', onData);\n stdin.removeListener('close', onClose);\n stdin.removeListener('end', onEnd);\n stdin.removeListener('error', onError);\n }\n\n stdin.on('data', onData);\n stdin.once('close', onClose);\n stdin.once('end', onEnd);\n stdin.once('error', onError);\n });\n}\n\n// ─── Main export ──────────────────────────────────────────────────────────────\n\n/**\n * promptThreeWayMerge — interactive three-way merge UX.\n *\n * Flow:\n * 1. Renders unified diff of local vs remote.\n * 2. Prompts [k]eep / [t]ake / [e]dit / [a]bort.\n * 3. On [e]dit: writes a temp file with git-merge-marker content, spawns $EDITOR,\n * re-reads on close, re-prompts if conflict markers remain.\n * Temp file is always unlinked (finally block).\n * 4. On [a]bort or Ctrl-C: returns { resolution: 'aborted', body: local }.\n * 5. Never writes to caller's file.\n */\nexport async function promptThreeWayMerge(opts: PromptThreeWayMergeOpts): Promise<MergeResult> {\n const {\n local,\n remote,\n itemId,\n stdin = process.stdin,\n editor,\n } = opts;\n\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const now = opts.now ?? (() => Date.now().toString());\n\n // 1. Render diff\n const patch = renderInlineDiff(local, remote, itemId);\n stdout(`\\n[merge] ${itemId}\\n`);\n stdout(patch + '\\n');\n\n // 2–3. Prompt loop (re-prompt if editor leaves unresolved markers)\n for (;;) {\n const choice = await promptFourChoice({ stdin, stdout });\n\n switch (choice) {\n case 'k':\n return { resolution: 'keep', body: local };\n\n case 't':\n return { resolution: 'take', body: remote };\n\n case 'a':\n return { resolution: 'aborted', body: local };\n\n case 'e': {\n const tmpFile = path.join(os.tmpdir(), `cleargate-merge-${itemId}-${now()}.md`);\n const markerContent = `<<<<<<< local\\n${local}\\n=======\\n${remote}\\n>>>>>>> remote\\n`;\n\n try {\n await fs.writeFile(tmpFile, markerContent, 'utf-8');\n\n await openInEditor(tmpFile, { editor: editor ?? process.env['EDITOR'] ?? 'vi' });\n\n const edited = await fs.readFile(tmpFile, 'utf-8');\n\n if (containsConflictMarkers(edited)) {\n stdout('File still contains conflict markers — please resolve all conflicts.\\n');\n // re-prompt\n continue;\n }\n\n return { resolution: 'edited', body: edited };\n } finally {\n // Always clean up temp file even on error\n await fs.unlink(tmpFile).catch(() => {/* already gone — ignore */});\n }\n }\n }\n }\n}\n","/**\n * mcp-client.ts — STORY-010-04 / updated STORY-011-01\n *\n * Minimal JSON-RPC-over-HTTP client for ClearGate's MCP server.\n *\n * Token acquisition: callers obtain a token via acquireAccessToken() from\n * cleargate-cli/src/auth/acquire.ts and pass it via McpClientOptions.token.\n * This file does NOT read CLEARGATE_MCP_TOKEN or call acquireAccessToken.\n *\n * MCP endpoint: passed directly as McpClientOptions.baseUrl by the caller.\n *\n * Flashcard: no pre-existing MCP client in cleargate-cli — built from scratch here.\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\n// ── Wire types (re-exported for consumers) ───────────────────────────────────\n\nexport interface RemoteUpdateRef {\n remote_id: string;\n updated_at: string;\n}\n\nexport interface RemoteItem {\n remote_id: string;\n title: string;\n body: string | null;\n status: string;\n assignees: string[];\n labels: string[];\n updated_at: string;\n source_tool: string;\n raw: unknown;\n}\n\nexport interface RemoteComment {\n id: string;\n author_email: string | null;\n author_name: string;\n body: string;\n created_at: string;\n remote_id: string;\n}\n\nexport interface AdapterInfo {\n configured: boolean;\n name: 'linear' | 'jira' | 'github-projects' | 'no-adapter-configured';\n}\n\n// ── McpClient interface ───────────────────────────────────────────────────────\n\nexport interface McpClient {\n call<T>(tool: string, args: Record<string, unknown>): Promise<T>;\n adapterInfo(): Promise<AdapterInfo>;\n}\n\nexport interface McpClientOptions {\n baseUrl: string;\n token: string;\n /** Test seam: override globalThis.fetch */\n fetch?: typeof globalThis.fetch;\n}\n\n// ── JSON-RPC envelope ─────────────────────────────────────────────────────────\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n method: 'tools/call';\n params: {\n name: string;\n arguments: Record<string, unknown>;\n };\n id: number;\n}\n\ninterface JsonRpcResponse<T> {\n jsonrpc: '2.0';\n id: number;\n result?: {\n content?: Array<{ type: 'text'; text: string }>;\n structuredContent?: T;\n };\n error?: {\n code: number;\n message: string;\n };\n}\n\nlet _reqId = 1;\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\n/**\n * createMcpClient — build a JSON-RPC client for the ClearGate MCP server.\n *\n * Sends POST to ${baseUrl}/mcp with Authorization: Bearer <token>.\n * Parses the StreamableHTTP response (may be plain JSON or SSE stream).\n */\nexport function createMcpClient(opts: McpClientOptions): McpClient {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n\n async function call<T>(tool: string, args: Record<string, unknown>): Promise<T> {\n const body: JsonRpcRequest = {\n jsonrpc: '2.0',\n method: 'tools/call',\n params: { name: tool, arguments: args },\n id: _reqId++,\n };\n\n let response: Response;\n try {\n response = await fetchFn(`${opts.baseUrl}/mcp`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json, text/event-stream',\n 'Authorization': `Bearer ${opts.token}`,\n },\n body: JSON.stringify(body),\n });\n } catch (err) {\n throw new Error(`MCP transport error calling ${tool}: ${String(err)}`);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(`MCP HTTP ${response.status} calling ${tool}: ${text.slice(0, 256)}`);\n }\n\n const text = await response.text();\n\n // Handle SSE stream: extract last JSON from event-stream data lines\n let jsonText = text;\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('text/event-stream')) {\n const dataLines = text\n .split('\\n')\n .filter((l) => l.startsWith('data: '))\n .map((l) => l.slice('data: '.length).trim())\n .filter((l) => l !== '' && l !== '[DONE]');\n if (dataLines.length === 0) {\n throw new Error(`MCP SSE response for ${tool} contained no data lines`);\n }\n jsonText = dataLines[dataLines.length - 1];\n }\n\n let parsed: JsonRpcResponse<T>;\n try {\n parsed = JSON.parse(jsonText) as JsonRpcResponse<T>;\n } catch {\n throw new Error(`MCP response for ${tool} is not valid JSON: ${jsonText.slice(0, 256)}`);\n }\n\n if (parsed.error) {\n throw new Error(`MCP tool ${tool} returned error ${parsed.error.code}: ${parsed.error.message}`);\n }\n\n // structuredContent preferred; fall back to parsing text content\n if (parsed.result?.structuredContent !== undefined) {\n return parsed.result.structuredContent as T;\n }\n\n const textContent = parsed.result?.content?.find((c) => c.type === 'text')?.text;\n if (textContent !== undefined) {\n try {\n return JSON.parse(textContent) as T;\n } catch {\n throw new Error(`MCP tool ${tool} text content is not valid JSON: ${textContent.slice(0, 256)}`);\n }\n }\n\n throw new Error(`MCP tool ${tool} returned no content`);\n }\n\n async function adapterInfo(): Promise<AdapterInfo> {\n return call<AdapterInfo>('cleargate_adapter_info', {});\n }\n\n return { call, adapterInfo };\n}\n\n","/**\n * intake.ts — STORY-010-05\n *\n * Stakeholder proposal intake branch for `cleargate sync`.\n *\n * `runIntakeBranch` is called from `syncHandler` at step 3 (after pull, before\n * conflict classification). It:\n * 1. Calls `cleargate_detect_new_items({ label })` to get remote proposals.\n * 2. Deduplicates against pending-sync + archive by `remote_id` frontmatter.\n * 3. For each new item: allocates a `PROP-NNN` ID, slugifies the title,\n * writes a draft proposal file from the template, and appends a sync-log\n * entry with op:'pull-intake'.\n * 4. Emits an R10 warning to stderr if zero items matched AND this appears to\n * be the first intake run for this workspace (no prior `source: remote-authored`\n * files detected).\n * 5. Returns a summary for the end-of-sync stdout print.\n *\n * Respects dryRun: zero fs writes, zero sync-log entries, returns the plan.\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { slugify, nextProposalId, findByRemoteId } from './slug.js';\nimport { appendSyncLog, type SyncLogEntry } from './sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from './frontmatter-yaml.js';\nimport type { McpClient, RemoteItem } from './mcp-client.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface IntakeItem {\n proposalId: string;\n remoteId: string;\n title: string;\n path: string;\n}\n\nexport interface IntakeResult {\n created: number;\n items: IntakeItem[];\n warning?: string;\n}\n\nexport interface IntakeBranchOptions {\n mcp: McpClient;\n identity: { email: string };\n sprintRoot: string;\n projectRoot: string;\n dryRun: boolean;\n labelFilter?: string;\n /** Test seam: override now() */\n now?: () => string;\n}\n\n// ── runIntakeBranch ───────────────────────────────────────────────────────────\n\nexport async function runIntakeBranch(opts: IntakeBranchOptions): Promise<IntakeResult> {\n const {\n mcp,\n identity,\n sprintRoot,\n projectRoot,\n dryRun,\n labelFilter = 'cleargate:proposal',\n now = () => new Date().toISOString(),\n } = opts;\n\n const pendingSyncDir = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n\n // Detect new remote items with the cleargate:proposal label\n let remoteItems: RemoteItem[] = [];\n try {\n remoteItems = await mcp.call<RemoteItem[]>(\n 'cleargate_detect_new_items',\n { label: labelFilter },\n );\n if (!Array.isArray(remoteItems)) {\n remoteItems = [];\n }\n } catch {\n // Non-fatal: if the tool doesn't exist yet, treat as zero items\n remoteItems = [];\n }\n\n // R10: zero-label warning — fires when zero items returned AND this is the\n // first intake run (no existing `source: remote-authored` files found)\n let warning: string | undefined;\n if (remoteItems.length === 0) {\n const hasExistingIntake = await hasAnyRemoteAuthored(projectRoot);\n if (!hasExistingIntake) {\n warning =\n `warn: no Linear issues match label '${labelFilter}' — ` +\n `confirm the label exists in your workspace. See EPIC-010 R10.`;\n }\n }\n\n const createdItems: IntakeItem[] = [];\n\n for (const item of remoteItems) {\n // Idempotency: skip if a local counterpart already exists (in either dir)\n const existingPath = await findByRemoteId(projectRoot, item.remote_id);\n if (existingPath !== null) {\n continue;\n }\n\n if (dryRun) {\n // In dry-run mode: plan the intake without writing anything\n const proposalId = await nextProposalId(projectRoot);\n const slug = slugify(item.title ?? 'untitled');\n const num = proposalId.replace('PROP-', '');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n continue;\n }\n\n // Compute the next proposal ID and filename\n // Note: we recompute after each write so IDs are consistent even when multiple\n // items are being created in the same run\n const proposalId = await nextProposalId(projectRoot);\n const num = proposalId.replace('PROP-', '');\n const slug = slugify(item.title ?? 'untitled');\n const filename = `PROPOSAL-${num}-remote-${slug}.md`;\n const targetPath = path.join(pendingSyncDir, filename);\n const nowTs = now();\n\n // Build frontmatter from template + our sync fields\n const fm: Record<string, unknown> = {\n proposal_id: proposalId,\n remote_id: item.remote_id,\n status: 'Draft',\n approved: false,\n source: 'remote-authored',\n last_pulled_by: identity.email,\n last_pulled_at: nowTs,\n last_remote_update: item.updated_at,\n created_at: nowTs,\n updated_at: nowTs,\n pushed_by: null,\n pushed_at: null,\n last_synced_status: null,\n last_synced_body_sha: null,\n };\n\n // Build body from the proposal template + pre-fill §1 body from remote item\n const body = buildProposalBody(item, projectRoot);\n\n // Atomic write: .tmp + rename\n await fsPromises.mkdir(pendingSyncDir, { recursive: true });\n const content = serializeFrontmatter(fm) + '\\n\\n' + body;\n const tmpPath = `${targetPath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, targetPath);\n\n // Sync-log entry\n const logEntry: SyncLogEntry = {\n ts: nowTs,\n actor: identity.email,\n op: 'pull-intake',\n target: proposalId,\n remote_id: item.remote_id,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, logEntry);\n\n createdItems.push({\n proposalId,\n remoteId: item.remote_id,\n title: item.title ?? '',\n path: targetPath,\n });\n }\n\n return {\n created: createdItems.length,\n items: createdItems,\n warning,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Build the body for an intake proposal file.\n * Seeds §1 \"Initiative & Context\" with the remote item body;\n * leaves other sections as template placeholders.\n */\nfunction buildProposalBody(item: RemoteItem, _projectRoot: string): string {\n const title = item.title ?? '(untitled)';\n const remoteBody = item.body ?? '';\n\n return `# ${title}\n\n## 1. Initiative & Context\n\n### 1.1 Objective\n\n${remoteBody || '(pre-filled from Linear issue body)'}\n\n### 1.2 The \"Why\"\n\n{Reason 1}\n{Reason 2}\n\n## 2. Technical Architecture & Constraints\n\n### 2.1 Dependencies\n\n{List required external APIs, packages, or systems}\n\n### 2.2 System Constraints\n\n| Constraint | Details |\n|---|---|\n| Architectural Rules | {e.g., Must use purely functional components} |\n| Security | {e.g., Data must be encrypted at rest.} |\n\n## 3. Scope Impact (Touched Files & Data)\n\n### 3.1 Known Files\n\npath/to/existing/file.ext - {Explanation of expected change}\n\n### 3.2 Expected New Entities\n\npath/to/new/file.ext - {Explanation of purpose}\n\n## Approval Gate\n\n(Vibe Coder: Review this proposal. If the architecture and context are correct, change approved: false to approved: true in the YAML frontmatter. Only then is the AI authorized to proceed with Epic/Story decomposition.)\n`;\n}\n\n/**\n * Check if any `source: remote-authored` file already exists in pending-sync or archive.\n * Used to gate the R10 zero-label warning.\n */\nasync function hasAnyRemoteAuthored(projectRoot: string): Promise<boolean> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n // Quick scan without full parse — look for source: remote-authored in frontmatter\n const fmEnd = raw.indexOf('\\n---', 4);\n if (fmEnd === -1) continue;\n const fmBlock = raw.slice(0, fmEnd);\n if (/source:\\s*['\"]?remote-authored['\"]?/.test(fmBlock)) {\n return true;\n }\n } catch {\n // Skip\n }\n }\n }\n\n return false;\n}\n\n// Re-export parseFrontmatter for consumers that read template files\nexport { parseFrontmatter };\n","/**\n * slug.ts — STORY-010-05\n *\n * Filename slug helper + proposal ID scanner for stakeholder intake.\n *\n * `slugify(title, max)` — deterministic, locale-free slug from a title string.\n * `nextProposalId(projectRoot)` — scans pending-sync + archive for max PROP-NNN\n * and returns the next sequential ID as a zero-padded string.\n * `findByRemoteId(projectRoot, remoteId)` — dedup helper; returns local path on\n * hit, null on miss. Scans frontmatter block only (first --- ... --- delimiters).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\n\n// ── slugify ──────────────────────────────────────────────────────────────────\n\n/**\n * Convert a title string into a URL-safe slug.\n *\n * Algorithm:\n * 1. NFKD-normalize → strip combining marks (é→e, ü→u, etc.)\n * 2. Lowercase.\n * 3. Replace runs of [^a-z0-9]+ with a single dash.\n * 4. Trim leading/trailing dashes.\n * 5. Truncate to `max` characters; re-trim trailing dash.\n * 6. If empty/all-dashes after step 5 → return \"untitled\".\n */\nexport function slugify(title: string, max: number = 40): string {\n // Step 1: NFKD normalize + strip combining marks (Unicode category M)\n const normalized = title.normalize('NFKD').replace(/\\p{M}/gu, '');\n // Step 2: lowercase\n const lowered = normalized.toLowerCase();\n // Step 3: replace non-alphanumeric runs with dash\n const dashed = lowered.replace(/[^a-z0-9]+/g, '-');\n // Step 4: trim leading/trailing dashes\n const trimmed = dashed.replace(/^-+|-+$/g, '');\n // Step 5: truncate to max; re-trim trailing dash\n const truncated = trimmed.slice(0, max).replace(/-+$/, '');\n // Step 6: fallback for empty result\n if (!truncated) {\n return 'untitled';\n }\n return truncated;\n}\n\n// ── nextProposalId ────────────────────────────────────────────────────────────\n\n/** Pattern matching `proposal_id: \"PROP-NNN\"` or `proposal_id: PROP-NNN` */\nconst PROPOSAL_ID_RE = /^proposal_id:\\s*\"?PROP-(\\d+)\"?/m;\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for `.md` files whose frontmatter contains `proposal_id: \"PROP-NNN\"`.\n * Returns the next ID as `\"PROP-<max+1, zero-padded to 3>\"`.\n *\n * Gap-tolerant: returns max+1, NOT the first gap.\n * Empty dirs or no proposals found → `\"PROP-001\"`.\n */\nexport async function nextProposalId(projectRoot: string): Promise<string> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n let maxN = 0;\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n // Read only the frontmatter block (first --- ... ---)\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fmEnd = extractFrontmatterBlock(raw);\n if (!fmEnd) continue;\n const match = PROPOSAL_ID_RE.exec(fmEnd);\n if (!match) continue;\n const n = parseInt(match[1]!, 10);\n if (n > maxN) maxN = n;\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n const next = maxN + 1;\n return `PROP-${String(next).padStart(3, '0')}`;\n}\n\n// ── findByRemoteId ────────────────────────────────────────────────────────────\n\n/**\n * Scan `.cleargate/delivery/pending-sync/` AND `.cleargate/delivery/archive/`\n * for a `.md` file whose frontmatter contains `remote_id: \"<remoteId>\"`.\n *\n * Returns the absolute path of the first match, or `null` if not found.\n * Reads only the frontmatter block (first --- ... ---) for efficiency.\n */\nexport async function findByRemoteId(\n projectRoot: string,\n remoteId: string,\n): Promise<string | null> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n // Build a regex matching `remote_id: \"LIN-NNN\"` or `remote_id: LIN-NNN`\n const escaped = remoteId.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n const re = new RegExp(`^remote_id:\\\\s*\"?${escaped}\"?\\\\s*$`, 'm');\n\n for (const dir of dirs) {\n let entries;\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const fm = extractFrontmatterBlock(raw);\n if (!fm) continue;\n if (re.test(fm)) return fullPath;\n } catch {\n // Skip\n }\n }\n }\n\n return null;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Extract the YAML content between the first `---` delimiters.\n * Returns the raw YAML text (without the delimiters), or null if not found.\n */\nfunction extractFrontmatterBlock(raw: string): string | null {\n const lines = raw.split('\\n');\n if (lines[0] !== '---') return null;\n let closeIdx = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === '---') { closeIdx = i; break; }\n }\n if (closeIdx === -1) return null;\n return lines.slice(1, closeIdx).join('\\n');\n}\n","/**\n * active-criteria.ts — STORY-010-06\n *\n * Resolves which work items are \"active\" for the purpose of comment-pull.\n * Active = (item is referenced in the current sprint) OR (item's last_remote_update is within 30 days).\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but NO `stories:` list.\n * Body-regex scan is the only reliable signal available today.\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveActiveSprintDir } from './sync-log.js';\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface LocalWorkItemRef {\n /** Primary cleargate ID (e.g. STORY-010-06, EPIC-010). */\n primaryId: string;\n /** Remote ID in the PM tool (e.g. LIN-1042). May be undefined if not yet pushed. */\n remoteId: string | undefined;\n /** ISO string from `last_remote_update` frontmatter field. */\n lastRemoteUpdate: string | undefined;\n}\n\n// ── Active set resolver ───────────────────────────────────────────────────────\n\n/**\n * Resolve which items (by remote_id) are \"active\" and should have comments fetched.\n *\n * @param projectRoot - absolute path to the project root\n * @param localItems - all local work items with their IDs\n * @param nowFn - injectable clock (for 30-day window)\n * @returns - Set of remote_id values that are active\n */\nexport async function resolveActiveItems(\n projectRoot: string,\n localItems: LocalWorkItemRef[],\n nowFn: () => string = () => new Date().toISOString(),\n): Promise<Set<string>> {\n const active = new Set<string>();\n const now = Date.parse(nowFn());\n const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;\n\n // ── Branch 1: items referenced in the current sprint ─────────────────────\n const inSprintIds = await resolveInSprintIds(projectRoot);\n\n // ── Branch 2: items updated in last 30 days + union of sprint ─────────────\n for (const item of localItems) {\n if (!item.remoteId) continue;\n\n // In-sprint check\n if (inSprintIds.has(item.primaryId)) {\n active.add(item.remoteId);\n continue;\n }\n\n // 30-day window check\n if (item.lastRemoteUpdate) {\n const itemMs = Date.parse(item.lastRemoteUpdate);\n if (!isNaN(itemMs) && (now - itemMs) <= thirtyDaysMs) {\n active.add(item.remoteId);\n }\n }\n }\n\n return active;\n}\n\n// ── Internal: resolve sprint item references ──────────────────────────────────\n\n/**\n * Read the active sprint file and extract all work-item ID references from its body.\n *\n * NOTE: Sprint frontmatter has `epics: [...]` but no `stories:` array.\n * We scan the full body text for IDs matching:\n * (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n *\n * TODO: STORY-010-08 to introduce stories: [] frontmatter array for precise lookup.\n */\nasync function resolveInSprintIds(projectRoot: string): Promise<Set<string>> {\n const ids = new Set<string>();\n\n try {\n const sprintDir = resolveActiveSprintDir(projectRoot);\n const sprintId = path.basename(sprintDir);\n\n if (sprintId === '_off-sprint') return ids;\n\n // Try pending-sync first, then archive\n const sprintFile = await findSprintFile(projectRoot, sprintId);\n if (!sprintFile) return ids;\n\n const content = await fsPromises.readFile(sprintFile, 'utf8');\n\n // Scan body for work-item ID patterns\n // (STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?\n const pattern = /(STORY|EPIC|PROPOSAL|CR|BUG)-\\d+(-\\d+)?/g;\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(content)) !== null) {\n ids.add(match[0]);\n }\n } catch {\n // Non-fatal: if we can't resolve sprint, return empty set\n }\n\n return ids;\n}\n\nasync function findSprintFile(projectRoot: string, sprintId: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isFile() && entry.name.startsWith(sprintId) && entry.name.endsWith('.md')) {\n return path.join(dir, entry.name);\n }\n }\n } catch {\n // Directory not found — try next\n }\n }\n\n return null;\n}\n","/**\n * comments-cache.ts — STORY-010-06\n *\n * Atomic read/write for comment cache files at\n * .cleargate/.comments-cache/<remote_id>.json\n *\n * Contents = raw RemoteComment[] array from cleargate_pull_comments.\n * Write is atomic via .tmp + rename (mirrors sync.ts:writeAtomic).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Cache directory ───────────────────────────────────────────────────────────\n\nfunction cacheDir(projectRoot: string): string {\n return path.join(projectRoot, '.cleargate', '.comments-cache');\n}\n\nfunction cachePath(projectRoot: string, remoteId: string): string {\n return path.join(cacheDir(projectRoot), `${remoteId}.json`);\n}\n\n// ── Write ──────────────────────────────────────────────────────────────────────\n\n/**\n * Atomically write a RemoteComment[] array to the cache for `remoteId`.\n * Creates the cache directory if absent.\n */\nexport async function writeCommentCache(\n projectRoot: string,\n remoteId: string,\n comments: RemoteComment[],\n): Promise<void> {\n const dir = cacheDir(projectRoot);\n await fsPromises.mkdir(dir, { recursive: true });\n\n const filePath = cachePath(projectRoot, remoteId);\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n const content = JSON.stringify(comments, null, 2) + '\\n';\n\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Read ───────────────────────────────────────────────────────────────────────\n\n/**\n * Read cached comments for `remoteId`.\n * Returns null if the cache file is absent or contains malformed JSON.\n */\nexport async function readCommentCache(\n projectRoot: string,\n remoteId: string,\n): Promise<RemoteComment[] | null> {\n const filePath = cachePath(projectRoot, remoteId);\n\n let raw: string;\n try {\n raw = await fsPromises.readFile(filePath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return null;\n throw err;\n }\n\n try {\n return JSON.parse(raw) as RemoteComment[];\n } catch {\n return null;\n }\n}\n","/**\n * wiki-comments-render.ts — STORY-010-06\n *\n * Renders (inserts / replaces / removes) the \"## Remote comments\" section\n * on an existing wiki page at .cleargate/wiki/<bucket>/<primaryId>.md.\n *\n * DELIBERATELY separate from commands/wiki-ingest.ts which owns full-page\n * rebuild from raw. Section overlay is a different concern and would fight\n * wiki-ingest's SHA-idempotency guard.\n *\n * Delimiter matching uses literal-string indexOf, NOT regex.\n * FLASHCARD #regex #inject-claude-md: fuzzy whitespace regex breaks when the\n * block body itself references both markers in prose — use indexOf exclusively.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { RemoteComment } from './mcp-client.js';\n\n// ── Delimiters (literal strings — never change to regex) ───────────────────────\n\nconst START = '<!-- cleargate:comments:start -->';\nconst END = '<!-- cleargate:comments:end -->';\n\n// ── Bucket resolution ─────────────────────────────────────────────────────────\n\n/**\n * Map a frontmatter record to the wiki bucket directory name.\n * Returns null if the item type cannot be determined.\n */\nexport function resolveBucket(fm: Record<string, unknown>): string | null {\n if (typeof fm['story_id'] === 'string' && fm['story_id']) return 'stories';\n if (typeof fm['epic_id'] === 'string' && fm['epic_id']) return 'epics';\n if (typeof fm['proposal_id'] === 'string' && fm['proposal_id']) return 'proposals';\n if (typeof fm['cr_id'] === 'string' && fm['cr_id']) return 'crs';\n if (typeof fm['bug_id'] === 'string' && fm['bug_id']) return 'bugs';\n return null;\n}\n\n/**\n * Extract the primary item ID (e.g. STORY-010-06) from frontmatter.\n */\nexport function getPrimaryId(fm: Record<string, unknown>): string | null {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return null;\n}\n\n// ── Section builder ───────────────────────────────────────────────────────────\n\n/**\n * Build the full delimited comment section string.\n * Sorts comments by created_at ascending.\n */\nexport function buildCommentSection(comments: RemoteComment[]): string {\n const sorted = [...comments].sort((a, b) => {\n return a.created_at < b.created_at ? -1 : a.created_at > b.created_at ? 1 : 0;\n });\n\n const entries = sorted.map((c) => {\n const author = c.author_email\n ? `${c.author_name} (${c.author_email})`\n : c.author_name;\n\n // Multi-line body: prefix every line with \"> \"\n const bodyLines = c.body.split('\\n').map((line) => `> ${line}`).join('\\n');\n\n return `### ${author} · ${c.created_at}\\n${bodyLines}`;\n });\n\n return (\n `${START}\\n` +\n `## Remote comments\\n` +\n `\\n` +\n `_Read-only snapshot. Comments live in the PM tool — reply there, not here._\\n` +\n `\\n` +\n entries.join('\\n\\n') +\n `\\n${END}`\n );\n}\n\n// ── Main export ───────────────────────────────────────────────────────────────\n\nexport interface RenderCommentsSectionOpts {\n /** Absolute path to project root */\n projectRoot: string;\n /** Remote ID (e.g. LIN-1042) */\n remoteId: string;\n /** Comment array from cleargate_pull_comments */\n comments: RemoteComment[];\n /** Local work items to resolve wiki path from */\n localItems: Array<{ fm: Record<string, unknown> }>;\n}\n\n/**\n * Insert / replace / remove the ## Remote comments section on the wiki page\n * corresponding to the given remote_id.\n *\n * - If the wiki page does not exist: no-op (wiki-ingest may not have run yet).\n * - Byte-idempotent: running twice with identical input produces identical output.\n * - Atomic write via .tmp + rename.\n */\nexport async function renderCommentsSection(\n opts: RenderCommentsSectionOpts,\n): Promise<void> {\n const { projectRoot, remoteId, comments, localItems } = opts;\n\n // Find the local item whose remote_id matches\n const localItem = localItems.find(\n (item) => item.fm['remote_id'] === remoteId,\n );\n if (!localItem) return;\n\n const bucket = resolveBucket(localItem.fm);\n const primaryId = getPrimaryId(localItem.fm);\n if (!bucket || !primaryId) return;\n\n const wikiPath = path.join(\n projectRoot,\n '.cleargate',\n 'wiki',\n bucket,\n `${primaryId}.md`,\n );\n\n // Read existing wiki page\n let existing: string;\n try {\n existing = await fsPromises.readFile(wikiPath, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return; // wiki page not yet built\n throw err;\n }\n\n const startIdx = existing.indexOf(START);\n const endIdx = existing.indexOf(END);\n\n let updated: string;\n\n if (startIdx === -1 && comments.length === 0) {\n // No section, no comments — no-op\n return;\n } else if (startIdx === -1 && comments.length > 0) {\n // Insert new section at end of file\n const section = buildCommentSection(comments);\n const base = existing.endsWith('\\n\\n')\n ? existing.slice(0, -1) // trim one trailing newline\n : existing.endsWith('\\n')\n ? existing\n : existing + '\\n';\n updated = base + '\\n' + section + '\\n';\n } else if (startIdx !== -1 && comments.length > 0) {\n // Replace existing section\n const section = buildCommentSection(comments);\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n\\n' + section + '\\n' + (after ? '\\n' + after : '');\n } else {\n // startIdx !== -1 && comments.length === 0: remove section\n const before = existing.slice(0, startIdx).replace(/\\n+$/, '');\n const after = existing.slice(endIdx + END.length).replace(/^\\n+/, '');\n updated = before + '\\n' + (after ? after : '');\n }\n\n // Atomic write\n await writeAtomic(wikiPath, updated);\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n","/**\n * work-items.ts — STORY-023-01\n *\n * Sync driver for `cleargate sync work-items`.\n *\n * Walks .cleargate/delivery/{pending-sync,archive}/ for *.md files,\n * computes sha256(body + serializeFrontmatter(fm)) for each, skips\n * items whose sha matches last_synced_body_sha in frontmatter (idempotent),\n * batches changed items (cap=100 per request), and calls\n * cleargate_sync_work_items on the MCP server.\n *\n * After each accepted item in the response, atomically writes back\n * last_synced_body_sha + server_pushed_at_version to the local file.\n *\n * Wire format: EPIC-023 §2.3 — project_id NOT in the body (JWT-derived).\n *\n * Token safety: JWT tokens (eyJ…) NEVER written to stdout/stderr/sync-log\n * (FLASHCARD 2026-04-18 #cli #plaintext-redact).\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { createHash } from 'node:crypto';\nimport { parseFrontmatter } from '../../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../frontmatter-yaml.js';\nimport type { McpClient } from '../mcp-client.js';\n\n// ── Batch cap (EPIC-023 §2.3: max 100 items per request) ─────────────────────\n\nconst BATCH_SIZE = 100;\n\n// ── Wire types (EPIC-023 §2.3 / §2.4) ─────────────────────────────────────────\n\nexport interface SyncItemPayload {\n cleargate_id: string;\n type: string;\n status: string;\n frontmatter: Record<string, unknown>;\n body: string;\n file_sha: string;\n last_synced_body_sha: string | null;\n}\n\nexport interface AcceptedItem {\n cleargate_id: string;\n version: number;\n pushed_at: string;\n body_sha: string;\n}\n\nexport interface ConflictItem {\n cleargate_id: string;\n local_sha: string;\n remote_sha: string;\n divergence_path: string;\n}\n\nexport interface ErrorItem {\n cleargate_id: string;\n code: string;\n message: string;\n}\n\nexport interface SyncWorkItemsResponse {\n accepted: AcceptedItem[];\n conflicts: ConflictItem[];\n errors: ErrorItem[];\n}\n\nexport interface SyncWorkItemsResult {\n accepted: number;\n conflicts: number;\n errors: number;\n conflictItems: ConflictItem[];\n errorItems: ErrorItem[];\n}\n\n// ── Options ────────────────────────────────────────────────────────────────────\n\nexport interface SyncWorkItemsOpts {\n projectRoot: string;\n mcp: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── Internal shape for a changed work item ─────────────────────────────────────\n\ninterface WorkItemFile {\n localPath: string;\n fm: Record<string, unknown>;\n body: string;\n cleargate_id: string;\n type: string;\n file_sha: string;\n last_synced_body_sha: string | null;\n}\n\n// ── SHA computation ─────────────────────────────────────────────────────────────\n\n/**\n * Attribution fields written back by the CLI after a successful sync.\n * These are EXCLUDED from the sha computation so that write-back does not\n * dirty the sha on the next run (idempotency requirement, EPIC-023 §2.1).\n *\n * The server-side sha computation also strips these fields before computing\n * body_sha (STORY-023-02 mcp/src/utils/serialize-frontmatter.ts must do the\n * same for idempotency to hold end-to-end).\n */\nconst ATTRIBUTION_FIELDS: ReadonlySet<string> = new Set([\n 'last_synced_body_sha',\n 'server_pushed_at_version',\n]);\n\n/**\n * Compute the canonical file sha per EPIC-023 §2.3:\n * sha256(body + serializeFrontmatter(fm_without_attribution_fields))\n *\n * Attribution fields (last_synced_body_sha, server_pushed_at_version) are\n * excluded so that write-back after a successful sync does not immediately\n * dirty the sha on the next invocation (idempotency).\n *\n * IMPORTANT: Server reimplements byte-identical in\n * mcp/src/utils/serialize-frontmatter.ts — DO NOT change the\n * serializer options, field exclusions, or sha ordering.\n */\nfunction computeFileSha(fm: Record<string, unknown>, body: string): string {\n const fmForSha: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(fm)) {\n if (!ATTRIBUTION_FIELDS.has(key)) {\n fmForSha[key] = value;\n }\n }\n return createHash('sha256')\n .update(body + serializeFrontmatter(fmForSha))\n .digest('hex');\n}\n\n// ── Item-type resolver ─────────────────────────────────────────────────────────\n\nfunction getItemType(fm: Record<string, unknown>): string {\n const typeMap: Record<string, string> = {\n story_id: 'story',\n epic_id: 'epic',\n proposal_id: 'proposal',\n cr_id: 'cr',\n bug_id: 'bug',\n initiative_id: 'initiative',\n };\n for (const [key, type] of Object.entries(typeMap)) {\n if (typeof fm[key] === 'string' && fm[key]) return type;\n }\n // Fall back to cleargate_id prefix pattern\n const cgId = typeof fm['cleargate_id'] === 'string' ? (fm['cleargate_id'] as string) : '';\n if (cgId.startsWith('STORY-')) return 'story';\n if (cgId.startsWith('EPIC-')) return 'epic';\n if (cgId.startsWith('PROPOSAL-')) return 'proposal';\n if (cgId.startsWith('CR-')) return 'cr';\n if (cgId.startsWith('BUG-')) return 'bug';\n if (cgId.startsWith('HOTFIX-')) return 'hotfix';\n return 'story';\n}\n\n// ── Item ID resolver ────────────────────────────────────────────────────────────\n\nfunction getItemId(fm: Record<string, unknown>): string | null {\n // cleargate_id is the canonical identifier per EPIC-023 §2.3\n if (typeof fm['cleargate_id'] === 'string' && fm['cleargate_id']) {\n return fm['cleargate_id'] as string;\n }\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (typeof fm[key] === 'string' && fm[key]) return fm[key] as string;\n }\n return null;\n}\n\n// ── Directory walker ────────────────────────────────────────────────────────────\n\nasync function walkDeliveryDirs(projectRoot: string): Promise<WorkItemFile[]> {\n const dirs = [\n path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync'),\n path.join(projectRoot, '.cleargate', 'delivery', 'archive'),\n ];\n\n const results: WorkItemFile[] = [];\n\n for (const dir of dirs) {\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n // Directory doesn't exist — skip\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm, body } = parseFrontmatter(raw);\n\n const cleargate_id = getItemId(fm);\n if (!cleargate_id) continue; // skip files without a recognisable ID\n\n const type = getItemType(fm);\n const file_sha = computeFileSha(fm, body);\n const last_synced_body_sha =\n typeof fm['last_synced_body_sha'] === 'string'\n ? fm['last_synced_body_sha']\n : null;\n\n results.push({\n localPath: fullPath,\n fm,\n body,\n cleargate_id,\n type,\n file_sha,\n last_synced_body_sha,\n });\n } catch {\n // Defensively skip malformed files — don't abort the whole sync\n }\n }\n }\n\n return results;\n}\n\n// ── Atomic write-back ──────────────────────────────────────────────────────────\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\n// ── Main driver ─────────────────────────────────────────────────────────────────\n\n/**\n * syncWorkItems — push all changed local work items to the MCP server.\n *\n * Status-blind: every status syncs (EPIC-023 §2.1). No approved: gate.\n * Idempotent: skip if file_sha === last_synced_body_sha.\n * Batch cap: 100 items per request.\n */\nexport async function syncWorkItems(opts: SyncWorkItemsOpts): Promise<SyncWorkItemsResult> {\n const { projectRoot, mcp } = opts;\n\n // Walk all delivery dirs\n const allItems = await walkDeliveryDirs(projectRoot);\n\n // Filter to changed items (status-blind, sha-based delta)\n const changedItems = allItems.filter(\n (item) => item.file_sha !== item.last_synced_body_sha,\n );\n\n // No changes: exit immediately without any MCP call\n if (changedItems.length === 0) {\n return { accepted: 0, conflicts: 0, errors: 0, conflictItems: [], errorItems: [] };\n }\n\n // Split into batches of BATCH_SIZE\n const batches: WorkItemFile[][] = [];\n for (let i = 0; i < changedItems.length; i += BATCH_SIZE) {\n batches.push(changedItems.slice(i, i + BATCH_SIZE));\n }\n\n const result: SyncWorkItemsResult = {\n accepted: 0,\n conflicts: 0,\n errors: 0,\n conflictItems: [],\n errorItems: [],\n };\n\n for (const batch of batches) {\n // Build wire payload (EPIC-023 §2.3 — no project_id; JWT carries it)\n const items: SyncItemPayload[] = batch.map((item) => ({\n cleargate_id: item.cleargate_id,\n type: item.type,\n status: typeof item.fm['status'] === 'string' ? (item.fm['status'] as string) : '',\n frontmatter: item.fm,\n body: item.body,\n file_sha: item.file_sha,\n // FLASHCARD #cli #commander #optional-key: null not undefined for Zod nullable\n last_synced_body_sha: item.last_synced_body_sha,\n }));\n\n let response: SyncWorkItemsResponse;\n try {\n response = await mcp.call<SyncWorkItemsResponse>('cleargate_sync_work_items', { items });\n } catch (err) {\n // MCP transport error — propagate upward so the command can exit 1\n throw err;\n }\n\n result.accepted += response.accepted.length;\n result.conflicts += response.conflicts.length;\n result.errors += response.errors.length;\n result.conflictItems.push(...response.conflicts);\n result.errorItems.push(...response.errors);\n\n // Attribution write-back for accepted items (atomic, per-item)\n // FLASHCARD 2026-04-19 #cli #frontmatter #parse:\n // parseFrontmatter strips one leading blank — write back as\n // serializeFrontmatter(fm) + '\\n\\n' + body (mirrors push.ts:256)\n for (const accepted of response.accepted) {\n const workItem = batch.find((i) => i.cleargate_id === accepted.cleargate_id);\n if (!workItem) continue;\n\n const updatedFm: Record<string, unknown> = {\n ...workItem.fm,\n last_synced_body_sha: accepted.body_sha,\n server_pushed_at_version: accepted.version,\n };\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + workItem.body;\n try {\n await writeAtomic(workItem.localPath, newContent);\n } catch {\n // Non-fatal: write-back failure doesn't abort the batch\n }\n }\n }\n\n return result;\n}\n","/**\n * admin-url.ts — STORY-023-04\n *\n * Resolves the ClearGate admin UI base URL with optional project-scoped sub-path.\n *\n * Priority order:\n * 1. CLEARGATE_ADMIN_URL env var (highest)\n * 2. Default: https://admin.cleargate.soula.ge/\n *\n * Optional project suffix: if ~/.cleargate/config.json contains `project_id`,\n * appends `/projects/<project_id>` when no explicit `path` argument is given.\n *\n * NOTE: intentionally does NOT use loadConfig() from config.ts — that schema is\n * .strict() and does not include project_id. We read the raw JSON file directly\n * to pluck project_id without triggering a Zod strict-mode validation error.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\n\nconst DEFAULT_BASE = 'https://admin.cleargate.soula.ge/';\n\nexport interface AdminUrlOpts {\n /** Inject a custom env-var map (default: process.env). Useful in tests. */\n env?: NodeJS.ProcessEnv;\n /**\n * Inject a custom config reader (default: reads ~/.cleargate/config.json).\n * Must return the parsed JSON object (or throw on failure — callers catch).\n */\n configReader?: () => unknown;\n}\n\n/**\n * Returns the admin UI URL, optionally scoped to a project or an explicit sub-path.\n *\n * @param path Optional URL sub-path (e.g. \"/items\"). When provided, overrides the\n * project-scoped default. Leading slash is normalised — the return value\n * always has exactly one slash between base and path.\n * @param opts Optional injection seams for testing.\n */\nexport function adminUrl(urlPath?: string, opts?: AdminUrlOpts): string {\n const env = opts?.env ?? process.env;\n\n // Resolve base URL from env or default; normalise trailing slash.\n const rawBase = env['CLEARGATE_ADMIN_URL'] ?? DEFAULT_BASE;\n const base = rawBase.endsWith('/') ? rawBase : rawBase + '/';\n\n // Explicit path argument takes precedence over project-scoped default.\n if (urlPath !== undefined) {\n const suffix = urlPath.startsWith('/') ? urlPath.slice(1) : urlPath;\n return base + suffix;\n }\n\n // Try to read project_id from config and append project-scoped sub-path.\n try {\n const cfg = opts?.configReader ? opts.configReader() : readLocalConfig();\n const projectId =\n cfg !== null &&\n cfg !== undefined &&\n typeof cfg === 'object' &&\n 'project_id' in cfg\n ? (cfg as Record<string, unknown>)['project_id']\n : undefined;\n if (typeof projectId === 'string' && projectId.length > 0) {\n return base + 'projects/' + projectId;\n }\n } catch {\n // Config read or parse failure — fall back silently; no stderr, no throw.\n }\n\n return base;\n}\n\n/**\n * Reads ~/.cleargate/config.json directly (raw JSON, no Zod validation).\n * Returns null when the home directory is unavailable or the file does not exist.\n * Throws on JSON parse errors so the caller's catch block can fall back.\n */\nfunction readLocalConfig(): unknown {\n const home = os.homedir();\n if (!home) return null;\n\n const configPath = path.join(home, '.cleargate', 'config.json');\n const raw = fs.readFileSync(configPath, 'utf8');\n return JSON.parse(raw);\n}\n","/**\n * sync-work-items.ts — STORY-023-01\n *\n * `cleargate sync work-items` — push all local work items (every status)\n * to the MCP cleargate_sync_work_items tool without requiring a PM adapter.\n *\n * Behaviour contract (EPIC-023 §2.1):\n * - Status-blind: every item syncs (Draft, Approved, Verified, etc.)\n * - Idempotent: skip if file_sha === last_synced_body_sha\n * - Batch cap = 100 items per request\n * - Atomic write-back to local frontmatter on accepted items\n * - Prints \"→ View synced items: <url>\" on success\n * - Conflicts/errors → stderr; exit 1 only if MCP call itself threw\n * - No network traffic when 0 items changed\n *\n * Wire format: EPIC-023 §2.3. project_id NOT in the body — JWT carries it.\n *\n * MCP URL config: mirrors push.ts resolveMcp() pattern.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog } from '../lib/sync-log.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { syncWorkItems } from '../lib/sync/work-items.js';\nimport { adminUrl } from '../lib/admin-url.js';\n\n// ── Options ────────────────────────────────────────────────────────────────────\n\nexport interface SyncWorkItemsOptions {\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (prevents token-from-env requirement) */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n /** Test seam: override adminUrl() return value */\n adminUrlFn?: () => string;\n}\n\n// ── Handler ────────────────────────────────────────────────────────────────────\n\nexport async function syncWorkItemsHandler(opts: SyncWorkItemsOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n const adminUrlFn = opts.adminUrlFn ?? adminUrl;\n\n // ── Resolve MCP client (same pattern as push.ts) ──────────────────────────\n async function resolveMcp(): Promise<McpClient> {\n if (opts.mcp) return opts.mcp;\n\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n throw new Error('unreachable');\n }\n\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n throw new Error('unreachable');\n }\n\n return createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Identity + sprint (for sync-log) ──────────────────────────────────────\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // ── Resolve MCP (checks URL + token before doing any work) ────────────────\n let mcp: McpClient;\n try {\n mcp = await resolveMcp();\n } catch (err) {\n // Re-throw any __exit-tagged error (thrown by the exit seam) so the\n // test harness can observe the exit code. Also re-throw 'unreachable'\n // sentinels from resolveMcp's post-exit throw.\n const e = err as Error & { __exit?: boolean };\n if (e.__exit || e.message === 'unreachable') {\n throw err;\n }\n stderr(`Error: ${e.message}\\n`);\n exit(1);\n return;\n }\n\n // ── Run sync driver ────────────────────────────────────────────────────────\n let result;\n try {\n result = await syncWorkItems({ projectRoot, mcp, stdout, stderr, now: nowFn });\n } catch (err) {\n // MCP transport error — exit 1\n stderr(`Error: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── No changes path ────────────────────────────────────────────────────────\n if (result.accepted === 0 && result.conflicts === 0 && result.errors === 0) {\n stdout('sync: 0 items changed (nothing to push)\\n');\n return;\n }\n\n // ── Summary line ──────────────────────────────────────────────────────────\n stdout(\n `sync: ${result.accepted} accepted, ${result.conflicts} conflicts, ${result.errors} errors\\n`,\n );\n\n // ── Per-conflict details → stderr ─────────────────────────────────────────\n for (const conflict of result.conflictItems) {\n stderr(\n `conflict: ${conflict.cleargate_id} — local_sha=${conflict.local_sha.slice(0, 8)}… ` +\n `remote_sha=${conflict.remote_sha.slice(0, 8)}… divergence_path=${conflict.divergence_path}\\n`,\n );\n }\n\n // ── Per-error details → stderr ────────────────────────────────────────────\n for (const error of result.errorItems) {\n stderr(`error: ${error.cleargate_id} — code=${error.code} message=${error.message}\\n`);\n }\n\n // ── Admin URL on success (at least one accepted item) ─────────────────────\n if (result.accepted > 0) {\n stdout(`→ View synced items: ${adminUrlFn()}\\n`);\n }\n\n // ── Sync-log entry (one per invocation — per architect recommendation §5) ─\n try {\n await appendSyncLog(sprintRoot, {\n ts: nowFn(),\n actor: identity.email,\n op: 'push',\n target: 'work-items-batch',\n result: 'ok',\n detail: `accepted=${result.accepted} conflicts=${result.conflicts} errors=${result.errors}`,\n });\n } catch {\n // Sync-log write failure is non-fatal\n }\n\n // ── Exit code: 1 only if all batches errored (no accepted, has errors) ────\n // Per story §1.2: \"exit 1 only if all batches errored or the MCP call itself threw\"\n if (result.errors > 0 && result.accepted === 0 && result.conflicts === 0) {\n exit(1);\n }\n}\n","/**\n * pull.ts — STORY-010-04\n *\n * `cleargate pull <ID-or-remote_id>` — targeted single-item pull.\n *\n * --comments flag: reserved for STORY-010-06; emits a warn-level message and\n * proceeds without comment pull. Does NOT error. Does NOT accept silently.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { hashNormalized } from '../lib/sha256.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient, RemoteItem, RemoteComment } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\nimport { writeCommentCache } from '../lib/comments-cache.js';\nimport { renderCommentsSection } from '../lib/wiki-comments-render.js';\n\nexport interface PullOptions {\n comments?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\nexport async function pullHandler(idOrRemoteId: string, opts: PullOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client\n let mcp: McpClient;\n if (opts.mcp) {\n mcp = opts.mcp;\n } else {\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n return;\n }\n // Acquire token via keychain/env\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n return;\n }\n mcp = createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // Resolve the remote_id to pull\n // If idOrRemoteId looks like a remote ID (e.g. LIN-1042) use directly;\n // otherwise try to find local file with matching story_id / epic_id / etc.\n const remoteId = await resolveRemoteId(idOrRemoteId, projectRoot);\n if (!remoteId) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a remote_id. Check that the item has been pushed first.\\n`);\n exit(1);\n return;\n }\n\n // Pull from MCP\n const remoteItem = await mcp.call<RemoteItem | null>('cleargate_pull_item', { remote_id: remoteId });\n if (!remoteItem) {\n stderr(`Error: item ${remoteId} not found on MCP server.\\n`);\n exit(1);\n return;\n }\n\n // Find the local file\n const localPath = await findLocalFile(remoteId, projectRoot);\n\n if (!localPath) {\n stderr(`Error: no local file found with remote_id \"${remoteId}\".\\n`);\n exit(1);\n return;\n }\n\n // Read current state and check idempotency\n const rawContent = await fsPromises.readFile(localPath, 'utf8');\n const { fm, body } = parseFrontmatter(rawContent);\n\n const currentBodySha = hashNormalized(body);\n const remoteBodySha = hashNormalized(remoteItem.body ?? '');\n const currentStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n const lastPulledAt = typeof fm['last_pulled_at'] === 'string' ? fm['last_pulled_at'] : null;\n\n // Idempotency check: if nothing changed since last pull, skip\n const isNoOp =\n currentBodySha === remoteBodySha &&\n currentStatus === remoteItem.status &&\n typeof fm['last_synced_body_sha'] === 'string' &&\n fm['last_synced_body_sha'] === remoteBodySha &&\n lastPulledAt !== null;\n\n const now = nowFn();\n\n if (isNoOp) {\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'no-op',\n };\n await appendSyncLog(sprintRoot, entry);\n stdout(`pull: ${remoteId} no-op (no changes)\\n`);\n return;\n }\n\n // Apply the pull\n const updatedFm: Record<string, unknown> = {\n ...fm,\n status: remoteItem.status,\n last_pulled_by: identity.email,\n last_pulled_at: now,\n last_remote_update: remoteItem.updated_at,\n last_synced_status: remoteItem.status,\n last_synced_body_sha: remoteBodySha,\n };\n\n const newBody = remoteItem.body ?? '';\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + newBody;\n await writeAtomic(localPath, newContent);\n\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'pull',\n target: getItemId(fm),\n remote_id: remoteId,\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`pull: ${remoteId} applied to ${path.relative(projectRoot, localPath)}\\n`);\n\n // ── --comments: pull comment snapshot for this item ──────────────────────\n // Always pulls when flag is set (manual override; ignores active criteria).\n if (opts.comments) {\n const comments = await mcp.call<RemoteComment[]>(\n 'cleargate_pull_comments',\n { remote_id: remoteId },\n );\n await writeCommentCache(projectRoot, remoteId, comments);\n\n // Rebuild updatedFm as the local item state post-pull for wiki-render\n const localItemForRender = { fm: { ...updatedFm, remote_id: remoteId } };\n await renderCommentsSection({\n projectRoot,\n remoteId,\n comments,\n localItems: [localItemForRender],\n });\n stdout(`pull: ${remoteId} comments fetched (${comments.length})\\n`);\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveRemoteId(idOrRemoteId: string, projectRoot: string): Promise<string | null> {\n // If it looks like a remote ID pattern (e.g. LIN-NNNN, GH-NNNN, JIRA-123)\n if (/^[A-Z]+-\\d+/.test(idOrRemoteId)) {\n return idOrRemoteId;\n }\n // Try to find a local work item with matching ID, read its remote_id\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n try {\n const raw = await fsPromises.readFile(path.join(pendingSync, entry.name), 'utf8');\n const { fm } = parseFrontmatter(raw);\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId && typeof fm['remote_id'] === 'string') {\n return fm['remote_id'];\n }\n }\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function findLocalFile(remoteId: string, projectRoot: string): Promise<string | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(pendingSync, { withFileTypes: true });\n } catch {\n return null;\n }\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(pendingSync, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n if (fm['remote_id'] === remoteId) return fullPath;\n } catch {\n // skip malformed\n }\n }\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n","/**\n * push.ts — STORY-010-07\n *\n * `cleargate push <file>` — push a local work item to the MCP server.\n * `cleargate push --revert <ID-or-remote_id>` — soft-revert a pushed item.\n *\n * Pre-push gate (client-side):\n * Reads local frontmatter. If approved !== true, exits 1 with a clear message\n * BEFORE any MCP call. Zero network traffic on refusal.\n *\n * Attribution write-back:\n * On success, writes pushed_by + pushed_at from MCP response back into the\n * local frontmatter atomically (.tmp + rename). Appends sync-log entry op='push'.\n *\n * Soft revert (--revert):\n * Calls cleargate_sync_status with new_status='archived-without-shipping'.\n * Does NOT delete the remote item. Does NOT clear local remote_id.\n * Guards against reverting status='done' items unless --force is passed.\n * Appends sync-log entry op='push-revert'.\n *\n * Token safety:\n * JWT tokens (eyJ…) are NEVER written to stdout, stderr, or sync-log.\n * redactDetail in appendSyncLog covers the detail field.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { resolveIdentity } from '../lib/identity.js';\nimport { resolveActiveSprintDir, appendSyncLog, type SyncLogEntry } from '../lib/sync-log.js';\nimport { parseFrontmatter } from '../wiki/parse-frontmatter.js';\nimport { serializeFrontmatter } from '../lib/frontmatter-yaml.js';\nimport { createMcpClient } from '../lib/mcp-client.js';\nimport type { McpClient } from '../lib/mcp-client.js';\nimport { acquireAccessToken, AcquireError } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\n// ── Response shapes ─────────────────────────────────────────────────────────────\n\ninterface PushItemResult {\n version: number;\n updated_at: string;\n pushed_by: string;\n pushed_at: string;\n}\n\n// ── Options ─────────────────────────────────────────────────────────────────────\n\nexport interface PushOptions {\n /** --revert <ID-or-remote_id>: soft-revert a pushed item */\n revert?: string;\n /** --force: bypass \"done\" guard on revert */\n force?: boolean;\n projectRoot?: string;\n env?: NodeJS.ProcessEnv;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: inject McpClient directly (prevents token-from-env requirement) */\n mcp?: McpClient;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n /** Test seam: override now() for timestamps */\n now?: () => string;\n}\n\n// ── Handler ──────────────────────────────────────────────────────────────────────\n\nexport async function pushHandler(fileOrId: string, opts: PushOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n const nowFn = opts.now ?? (() => new Date().toISOString());\n\n // Identity\n const identity = resolveIdentity(projectRoot);\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // MCP client — resolved lazily and asynchronously via acquireAccessToken.\n // STORY-011-01: approved gate in handlePush runs BEFORE this, so no network\n // traffic happens on refusal (STORY-010-07 invariant preserved).\n async function resolveMcp(): Promise<McpClient> {\n if (opts.mcp) return opts.mcp;\n // Resolve base URL\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — fall through\n }\n }\n if (!baseUrl || !baseUrl.trim()) {\n stderr(\n 'Error: MCP URL not configured. Set CLEARGATE_MCP_URL env var or run `cleargate join <invite-url>`.\\n',\n );\n exit(2);\n throw new Error('unreachable');\n }\n // Acquire token\n let accessToken: string;\n try {\n accessToken = await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n env,\n });\n } catch (err) {\n if (err instanceof AcquireError) {\n stderr(`Error: ${err.message}\\n`);\n } else {\n stderr(`Error: ${String(err)}\\n`);\n }\n exit(2);\n throw new Error('unreachable');\n }\n return createMcpClient({ baseUrl: baseUrl.trim(), token: accessToken });\n }\n\n // ── Revert path ───────────────────────────────────────────────────────────────\n if (opts.revert !== undefined) {\n await handleRevert(opts.revert, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n force: opts.force ?? false,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n return;\n }\n\n // ── Push path ─────────────────────────────────────────────────────────────────\n await handlePush(fileOrId, {\n projectRoot,\n identity,\n sprintRoot,\n nowFn,\n resolveMcp,\n stdout,\n stderr,\n exit,\n });\n}\n\n// ── Push implementation ───────────────────────────────────────────────────────\n\ninterface PushCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handlePush(filePath: string, ctx: PushCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve path (absolute or relative to projectRoot)\n const resolvedPath = path.isAbsolute(filePath)\n ? filePath\n : path.resolve(projectRoot, filePath);\n\n let rawContent: string;\n try {\n rawContent = await fsPromises.readFile(resolvedPath, 'utf8');\n } catch {\n stderr(`Error: cannot read file \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n let fm: Record<string, unknown>;\n let body: string;\n try {\n ({ fm, body } = parseFrontmatter(rawContent));\n } catch (err) {\n stderr(`Error: cannot parse frontmatter in \"${resolvedPath}\": ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Client-side approved gate (BEFORE any MCP call) ─────────────────────────\n // STORY-010-07: if approved !== true, refuse and exit without network call.\n if (fm['approved'] !== true) {\n const itemId = getItemId(fm);\n stderr(\n `Error: push refused — ${itemId} has approved: false. ` +\n `Set approved: true in frontmatter after review.\\n`,\n );\n exit(1);\n return;\n }\n\n const itemId = getItemId(fm);\n const type = getItemType(fm);\n if (!type) {\n stderr(`Error: cannot determine item type from frontmatter in \"${resolvedPath}\".\\n`);\n exit(1);\n return;\n }\n\n // Derive title from body's first H1 if frontmatter lacks one.\n // ClearGate templates put the human-readable title in `# {ID}: {Name}`,\n // not in a `title:` frontmatter field. Admin UI reads payload.title for\n // item rows; without this, every row renders with an empty heading.\n const payloadForPush: Record<string, unknown> = { ...fm };\n if (typeof payloadForPush['title'] !== 'string' || payloadForPush['title'].length === 0) {\n const h1 = body.match(/^#\\s+(.+?)\\s*$/m)?.[1]?.trim();\n if (h1) payloadForPush['title'] = h1;\n }\n // Include the markdown body verbatim so the admin UI can render the full\n // work-item content (spec, Gherkin, implementation notes, DoD). Stored as\n // payload.body under the item's jsonb column — repo remains the canonical\n // source, MCP is a queryable mirror.\n payloadForPush['body'] = body;\n\n // MCP call\n const mcp = await resolveMcp();\n\n let result: PushItemResult;\n try {\n result = await mcp.call<PushItemResult>('push_item', {\n cleargate_id: itemId,\n type,\n payload: payloadForPush,\n ...(typeof fm['remote_id'] === 'string' ? { remote_id: fm['remote_id'] } : {}),\n });\n } catch (err) {\n stderr(`Error: push_item failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // ── Attribution write-back (atomic) ──────────────────────────────────────────\n const updatedFm: Record<string, unknown> = {\n ...fm,\n pushed_by: result.pushed_by,\n pushed_at: result.pushed_at,\n ...(result.version !== undefined ? { push_version: result.version } : {}),\n };\n const newContent = serializeFrontmatter(updatedFm) + '\\n\\n' + body;\n await writeAtomic(resolvedPath, newContent);\n\n // Sync-log\n const now = nowFn();\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push',\n target: itemId,\n result: 'ok',\n // Note: pushed_by and pushed_at go in frontmatter, NOT in sync-log detail\n // to prevent any accidental token leakage via the detail field.\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push: ${itemId} → version ${result.version} (pushed_by: ${result.pushed_by})\\n`);\n}\n\n// ── Revert implementation ─────────────────────────────────────────────────────\n\ninterface RevertCtx {\n projectRoot: string;\n identity: { email: string };\n sprintRoot: string;\n nowFn: () => string;\n force: boolean;\n resolveMcp: () => Promise<McpClient>;\n stdout: (s: string) => void;\n stderr: (s: string) => void;\n exit: (code: number) => never;\n}\n\nasync function handleRevert(idOrRemoteId: string, ctx: RevertCtx): Promise<void> {\n const { projectRoot, identity, sprintRoot, nowFn, force, resolveMcp, stdout, stderr, exit } = ctx;\n\n // Resolve to local file\n const resolved = await resolveLocalItem(idOrRemoteId, projectRoot);\n if (!resolved) {\n stderr(`Error: cannot resolve \"${idOrRemoteId}\" to a local work item.\\n`);\n exit(1);\n return;\n }\n\n const { localPath, fm } = resolved;\n const itemId = getItemId(fm);\n const localStatus = typeof fm['status'] === 'string' ? fm['status'] : '';\n\n // Guard: refuse to revert \"done\" items without --force\n if (localStatus === 'done' && !force) {\n stderr(`Error: refusing to revert shipped item. Pass --force to override.\\n`);\n exit(1);\n return;\n }\n\n // Call sync_status with new_status='archived-without-shipping'\n const mcp = await resolveMcp();\n try {\n await mcp.call('sync_status', {\n cleargate_id: itemId,\n new_status: 'archived-without-shipping',\n });\n } catch (err) {\n stderr(`Error: sync_status revert failed: ${(err as Error).message}\\n`);\n exit(1);\n return;\n }\n\n // DO NOT clear local remote_id — item stays traceable\n // DO NOT overwrite local status — sync will pull the server state back\n\n // Sync-log\n const now = nowFn();\n const remoteId = typeof fm['remote_id'] === 'string' ? fm['remote_id'] : undefined;\n const entry: SyncLogEntry = {\n ts: now,\n actor: identity.email,\n op: 'push-revert',\n target: itemId,\n ...(remoteId !== undefined ? { remote_id: remoteId } : {}),\n result: 'ok',\n };\n await appendSyncLog(sprintRoot, entry);\n\n stdout(`push --revert: ${itemId} → archived-without-shipping\\n`);\n void localPath; // referenced for clarity; not needed after initial read\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nasync function resolveLocalItem(\n idOrRemoteId: string,\n projectRoot: string,\n): Promise<{ localPath: string; fm: Record<string, unknown> } | null> {\n const pendingSync = path.join(projectRoot, '.cleargate', 'delivery', 'pending-sync');\n const archive = path.join(projectRoot, '.cleargate', 'delivery', 'archive');\n\n for (const dir of [pendingSync, archive]) {\n let entries: fs.Dirent[];\n try {\n entries = await fsPromises.readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.md')) continue;\n const fullPath = path.join(dir, entry.name);\n try {\n const raw = await fsPromises.readFile(fullPath, 'utf8');\n const { fm } = parseFrontmatter(raw);\n\n // Match by remote_id\n if (fm['remote_id'] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n\n // Match by primary item ID (story_id, epic_id, etc.)\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n if (fm[key] === idOrRemoteId) {\n return { localPath: fullPath, fm };\n }\n }\n } catch {\n // skip malformed\n }\n }\n }\n\n return null;\n}\n\nasync function writeAtomic(filePath: string, content: string): Promise<void> {\n await fsPromises.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp.${Date.now()}`;\n await fsPromises.writeFile(tmpPath, content, 'utf8');\n await fsPromises.rename(tmpPath, filePath);\n}\n\nfunction getItemId(fm: Record<string, unknown>): string {\n for (const key of ['story_id', 'epic_id', 'proposal_id', 'cr_id', 'bug_id']) {\n const val = fm[key];\n if (typeof val === 'string' && val) return val;\n }\n return 'unknown';\n}\n\nfunction getItemType(fm: Record<string, unknown>): string | null {\n const typeMap: Record<string, string> = {\n story_id: 'story',\n epic_id: 'epic',\n proposal_id: 'proposal',\n cr_id: 'cr',\n bug_id: 'bug',\n };\n for (const [key, type] of Object.entries(typeMap)) {\n if (typeof fm[key] === 'string' && fm[key]) return type;\n }\n return null;\n}\n","/**\n * conflicts.ts — STORY-010-04 / updated STORY-011-01\n *\n * `cleargate conflicts` — read-only command that reads .cleargate/.conflicts.json\n * and prints unresolved items with one-line resolution hints.\n *\n * Exit 0 when unresolved: [], exit 1 otherwise.\n *\n * --refresh flag: force-invalidate the acquire cache and rotate the stored\n * refresh token even if the cached access token is still valid. This is the\n * only MCP call conflicts makes.\n *\n * No mutations (besides keychain rotation on --refresh). No top-level await\n * (FLASHCARD #tsup #cjs #esm).\n */\n\nimport * as fsPromises from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { ConflictsJson, ConflictEntry } from './sync.js';\nimport { acquireAccessToken } from '../auth/acquire.js';\nimport { loadConfig } from '../config.js';\n\nexport interface ConflictsOptions {\n projectRoot?: string;\n /** --refresh: bypass single-flight cache and force a new /auth/refresh. */\n refresh?: boolean;\n /** Profile for token acquisition. Defaults to 'default'. */\n profile?: string;\n /** Test seam: override process.env lookup */\n env?: NodeJS.ProcessEnv;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n /** Test seam: override process.exit */\n exit?: (code: number) => never;\n}\n\nconst RESOLUTION_HINTS: Record<string, string> = {\n 'local-delete-remote-edit': 'remote-delete: resurrect or delete remote?',\n 'remote-delete-local-edit': 'local-edit: push your changes or accept remote deletion?',\n 'refuse': 'manual resolution required — re-run sync after resolving',\n 'halt': 'unknown conflict shape — file a ClearGate bug',\n};\n\nfunction getHint(entry: ConflictEntry): string {\n return RESOLUTION_HINTS[entry.state] ?? RESOLUTION_HINTS[entry.resolution] ?? `resolve and re-run sync`;\n}\n\nexport async function conflictsHandler(opts: ConflictsOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const env = opts.env ?? process.env;\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n // ── --refresh: force-invalidate the cache and rotate the keychain token ─────\n if (opts.refresh) {\n let baseUrl: string | undefined = env['CLEARGATE_MCP_URL'];\n if (!baseUrl || !baseUrl.trim()) {\n try {\n const cfg = loadConfig({ env });\n baseUrl = cfg.mcpUrl;\n } catch {\n // Config absent — skip refresh; command proceeds without token rotation\n }\n }\n if (baseUrl && baseUrl.trim()) {\n try {\n await acquireAccessToken({\n mcpUrl: baseUrl.trim(),\n profile: opts.profile ?? 'default',\n forceRefresh: true,\n env,\n });\n } catch {\n // Refresh errors are non-fatal for `conflicts` — proceed to print conflicts\n }\n }\n }\n\n const conflictsFile = path.join(projectRoot, '.cleargate', '.conflicts.json');\n\n let data: ConflictsJson;\n try {\n const raw = await fsPromises.readFile(conflictsFile, 'utf8');\n data = JSON.parse(raw) as ConflictsJson;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n stdout('No conflicts file found. Run `cleargate sync` first.\\n');\n exit(0);\n return;\n }\n throw err;\n }\n\n const unresolved = data.unresolved ?? [];\n\n if (unresolved.length === 0) {\n stdout('No unresolved conflicts.\\n');\n exit(0);\n return;\n }\n\n stdout(`Unresolved conflicts (${unresolved.length}):\\n`);\n stdout(`Generated: ${data.generated_at} Sprint: ${data.sprint_id}\\n\\n`);\n\n for (const item of unresolved) {\n const hint = getHint(item);\n stdout(` ${item.item_id.padEnd(20)} ${item.state.padEnd(30)} ${hint}\\n`);\n }\n\n stdout('\\nRe-run `cleargate sync` after resolving conflicts.\\n');\n exit(1);\n}\n","/**\n * sync-log.ts (command) — STORY-010-04\n *\n * `cleargate sync-log` — filter/print wrapper over readSyncLog() from M1.\n *\n * Flags: --actor, --op, --target, --limit N (default 50). Newest-first guaranteed by lib.\n *\n * No top-level await (FLASHCARD #tsup #cjs #esm).\n */\n\nimport { resolveActiveSprintDir, readSyncLog, type SyncLogOp, type SyncLogEntry } from '../lib/sync-log.js';\n\nexport interface SyncLogCommandOptions {\n actor?: string;\n op?: string;\n target?: string;\n limit?: number;\n projectRoot?: string;\n /** Test seam: stdout writer */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer */\n stderr?: (s: string) => void;\n}\n\nexport async function syncLogHandler(opts: SyncLogCommandOptions = {}): Promise<void> {\n const projectRoot = opts.projectRoot ?? process.cwd();\n const stdout = opts.stdout ?? ((s: string) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s: string) => process.stderr.write(s));\n const limit = opts.limit ?? 50;\n\n const sprintRoot = resolveActiveSprintDir(projectRoot);\n\n // Validate --op value if provided\n const validOps = new Set<SyncLogOp>([\n 'push', 'pull', 'pull-intake', 'push-revert', 'sync-status', 'conflict-remote-wins', 'conflict-refused',\n ]);\n let opFilter: SyncLogOp | undefined;\n if (opts.op !== undefined) {\n if (!validOps.has(opts.op as SyncLogOp)) {\n stderr(`Warning: unknown op \"${opts.op}\". Valid ops: ${[...validOps].join(', ')}\\n`);\n } else {\n opFilter = opts.op as SyncLogOp;\n }\n }\n\n const entries = await readSyncLog(sprintRoot, {\n actor: opts.actor,\n op: opFilter,\n target: opts.target,\n });\n\n const limited = entries.slice(0, limit);\n\n if (limited.length === 0) {\n stdout('No sync-log entries match the given filters.\\n');\n return;\n }\n\n for (const entry of limited) {\n stdout(formatEntry(entry) + '\\n');\n }\n}\n\nfunction formatEntry(entry: SyncLogEntry): string {\n const parts: string[] = [\n entry.ts,\n entry.actor,\n entry.op.padEnd(20),\n entry.target.padEnd(24),\n entry.result,\n ];\n if (entry.remote_id) parts.push(`remote=${entry.remote_id}`);\n if (entry.detail) parts.push(`detail=${entry.detail}`);\n return parts.join(' ');\n}\n","/**\n * `cleargate admin login` — GitHub OAuth device flow for admin CLI login.\n *\n * Flow:\n * 1. POST <mcp-url>/admin-api/v1/auth/device/start → gets device_code + user_code + verification_uri.\n * 2. Prints the verification URL and user code for the operator to open in a browser.\n * 3. Polls POST /admin-api/v1/auth/device/poll every `interval` seconds.\n * 4. On success: writes ~/.cleargate/admin-auth.json { version: 1, token: <admin_jwt> } at chmod 600.\n * 5. On failure: prints a clear error message and exits with appropriate exit code.\n *\n * Exit codes (per STORY-005-06 spec):\n * 0 — success\n * 3 — network error (unreachable)\n * 4 — auth rejected (non-admin GitHub user — not_admin)\n * 5 — device-flow timeout or user denied (expired_token / access_denied)\n * 6 — other device-flow error\n * 99 — unhandled\n *\n * Secrets NEVER appear on stdout/stderr: neither the GitHub access token\n * (server-side only) nor the admin JWT.\n *\n * STORY-005-06.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport { startDeviceFlow, DeviceFlowError } from '../auth/identity-flow.js';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface DeviceStartResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n expires_in: number;\n interval: number;\n}\n\nexport interface DevicePollPendingResponse {\n pending: true;\n retry_after?: number;\n}\n\nexport interface DevicePollSuccessResponse {\n pending: false;\n admin_token: string;\n expires_at: string;\n admin_user_id: string;\n}\n\nexport type DevicePollResponse = DevicePollPendingResponse | DevicePollSuccessResponse;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Options / seams\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AdminLoginOptions {\n mcpUrl?: string;\n env?: NodeJS.ProcessEnv;\n homedir?: () => string;\n fetch?: typeof globalThis.fetch;\n stdout?: (msg: string) => void;\n stderr?: (msg: string) => void;\n exit?: (code: number) => never;\n /** Override polling interval in milliseconds (used in tests to avoid real waits) */\n intervalOverrideMs?: number;\n /** Override admin-auth file path */\n authFilePath?: string;\n /** Override sleep implementation — injected in tests to capture interval values */\n sleepFn?: (ms: number) => Promise<void>;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst DEFAULT_MCP_URL = 'http://localhost:3000';\n\n/** Sleep helper — exported so tests can spy on it. */\nexport function sleep(ms: number): Promise<void> {\n return new Promise<void>((resolve) => setTimeout(resolve, ms));\n}\n\nfunction resolveMcpUrl(mcpUrlFlag?: string, env?: NodeJS.ProcessEnv): string {\n return (\n mcpUrlFlag ??\n (env ?? process.env)['CLEARGATE_MCP_URL'] ??\n DEFAULT_MCP_URL\n ).replace(/\\/$/, '');\n}\n\nfunction resolveAuthFilePath(opts: AdminLoginOptions): string {\n if (opts.authFilePath) return opts.authFilePath;\n const homedirFn = opts.homedir ?? os.homedir;\n return path.join(homedirFn(), '.cleargate', 'admin-auth.json');\n}\n\nfunction writeAdminAuth(filePath: string, token: string): void {\n const dir = path.dirname(filePath);\n fs.mkdirSync(dir, { recursive: true });\n const payload = JSON.stringify({ version: 1, token }, null, 2);\n fs.writeFileSync(filePath, payload, { encoding: 'utf8', mode: 0o600 });\n // Explicit chmod in case the file already existed with wider permissions\n fs.chmodSync(filePath, 0o600);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main handler\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport async function adminLoginHandler(opts: AdminLoginOptions = {}): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((msg: string) => process.stdout.write(msg + '\\n'));\n const stderr = opts.stderr ?? ((msg: string) => process.stderr.write(msg + '\\n'));\n const exitFn = opts.exit ?? ((code: number): never => process.exit(code));\n const mcpBase = resolveMcpUrl(opts.mcpUrl, opts.env);\n\n // ── Step 1: Start device flow ──────────────────────────────────────────────\n let startData: DeviceStartResponse;\n try {\n const startRes = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n });\n\n if (!startRes.ok) {\n const body = (await startRes.json().catch(() => ({}))) as { error?: string };\n if (startRes.status === 503) {\n stderr('cleargate: error: device flow not configured on the server (CLEARGATE_GITHUB_CLI_CLIENT_ID not set).');\n return exitFn(6);\n }\n stderr(`cleargate: error: server error ${startRes.status}: ${body.error ?? 'unknown'}`);\n return exitFn(6);\n }\n\n startData = (await startRes.json()) as DeviceStartResponse;\n } catch (err) {\n stderr(`cleargate: error: cannot reach ${mcpBase} (${err instanceof Error ? err.message : String(err)})`);\n return exitFn(3);\n }\n\n // ── Step 2: Display instructions ───────────────────────────────────────────\n stdout(`Open the following URL in your browser and enter the code:`);\n stdout(` URL: ${startData.verification_uri}`);\n stdout(` Code: ${startData.user_code}`);\n stdout(` (Code expires in ${Math.floor(startData.expires_in / 60)} minutes)`);\n stdout('Waiting for authorization...');\n\n // ── Step 3: Poll via identity-flow.startDeviceFlow with captured success body ─\n // We wrap fetchPoll to capture the full DevicePollSuccessResponse (which includes\n // expires_at + admin_user_id needed for the success stdout message) since\n // startDeviceFlow only returns { accessToken }.\n let capturedSuccessBody: DevicePollSuccessResponse | null = null;\n\n const fetchPollCapture = async (deviceCode: string) => {\n const res = await fetchFn(`${mcpBase}/admin-api/v1/auth/device/poll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ device_code: deviceCode }),\n });\n // Capture the success body before startDeviceFlow consumes it.\n // We intercept by wrapping json() to also store the body when pending=false.\n const originalJson = res.json.bind(res);\n return {\n status: res.status,\n json: async () => {\n const body = (await originalJson()) as Record<string, unknown>;\n if (body['pending'] === false) {\n capturedSuccessBody = body as unknown as DevicePollSuccessResponse;\n }\n return body;\n },\n };\n };\n\n try {\n await startDeviceFlow({\n deviceCode: startData.device_code,\n interval: startData.interval,\n expiresIn: startData.expires_in,\n fetchPoll: fetchPollCapture,\n // Only pass sleepFn if the caller explicitly injected one (test seam).\n // When sleepFn is omitted, startDeviceFlow uses its own defaultSleep.\n // This preserves the original bump-suppression logic:\n // shouldApplyBump = (sleepFn provided) || (intervalOverrideMs not set).\n ...(opts.sleepFn !== undefined ? { sleepFn: opts.sleepFn } : {}),\n ...(opts.intervalOverrideMs !== undefined ? { intervalOverrideMs: opts.intervalOverrideMs } : {}),\n deadlineGraceMs: 10_000,\n });\n } catch (err) {\n if (err instanceof DeviceFlowError) {\n switch (err.code) {\n case 'access_denied':\n stderr('cleargate: error: access denied — you declined authorization in the browser.');\n return exitFn(5);\n case 'not_admin':\n stderr('cleargate: error: your GitHub account is not authorized as an admin user.');\n return exitFn(4);\n case 'expired_token':\n stderr('cleargate: error: device code expired — please run `cleargate admin login` again.');\n return exitFn(5);\n case 'timeout':\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n case 'unreachable':\n stderr(`cleargate: error: network error while polling`);\n return exitFn(3);\n default:\n stderr(`cleargate: error: unexpected server response`);\n return exitFn(6);\n }\n }\n stderr(`cleargate: error: unexpected error during device flow`);\n return exitFn(6);\n }\n\n if (!capturedSuccessBody) {\n stderr('cleargate: error: timed out waiting for authorization. Please try again.');\n return exitFn(5);\n }\n\n // Extract fields with explicit typing to avoid TypeScript discriminated union narrowing\n const successBody = capturedSuccessBody as DevicePollSuccessResponse;\n\n // ── Step 4: Write admin-auth.json ──────────────────────────────────────────\n const authFilePath = resolveAuthFilePath(opts);\n try {\n writeAdminAuth(authFilePath, successBody.admin_token);\n } catch (err) {\n stderr(`cleargate: error: failed to write ${authFilePath}: ${err instanceof Error ? err.message : String(err)}`);\n return exitFn(99);\n }\n\n // ── Step 5: Success message (no secrets in output) ─────────────────────────\n stdout(`Logged in successfully. Token expires ${successBody.expires_at}.`);\n stdout(`Credentials saved to ${authFilePath} (chmod 600).`);\n}\n","/**\n * hotfix.ts — `cleargate hotfix new <slug>` command handler.\n *\n * STORY-022-06: Hotfix lane scaffolding.\n *\n * Creates a new HOTFIX-NNN_<slug>.md file in `.cleargate/delivery/pending-sync/`\n * from the bundled hotfix.md template, with ID auto-incremented via a scan of\n * existing HOTFIX-* files.\n *\n * Cap stub: blocks the 4th hotfix in a rolling 7-day window (pending-sync +\n * archive files modified within the last 7 days). Node fs APIs only — no\n * shell-outs (cross-OS per BUG-010 §4b).\n *\n * FLASHCARD #tsup #cjs #esm: no top-level await.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n// ─── Public CLI option types ───────────────────────────────────────────────────\n\nexport interface HotfixCliOptions {\n stdout?: (s: string) => void;\n stderr?: (s: string) => void;\n exit?: (code: number) => never;\n /** Override cwd for the repo root (test seam). */\n cwd?: string;\n /** Override the current ISO timestamp (test seam). */\n now?: string;\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────────\n\nfunction defaultExit(code: number): never {\n return process.exit(code) as never;\n}\n\nconst SLUG_RE = /^[a-z0-9-]+$/;\nconst HOTFIX_FILE_RE = /^HOTFIX-(\\d+)_.*\\.md$/;\n\n/**\n * Scan pending-sync/ for HOTFIX-NNN_*.md files; return the highest NNN found\n * (or 0 if none). Synchronous — no async needed for the tiny delivery dir.\n */\nfunction maxHotfixId(pendingDir: string): number {\n let max = 0;\n let entries: string[];\n try {\n entries = fs.readdirSync(pendingDir);\n } catch {\n return 0;\n }\n for (const entry of entries) {\n const m = HOTFIX_FILE_RE.exec(entry);\n if (m) {\n const n = parseInt(m[1]!, 10);\n if (n > max) max = n;\n }\n }\n return max;\n}\n\n/**\n * Count active hotfixes for the rolling-window cap.\n *\n * Counts:\n * - All HOTFIX-*.md files in pending-sync/ (regardless of mtime — they are\n * by definition active/in-flight).\n * - HOTFIX-*.md files in archive/ whose mtime is within the last 7 days\n * (recently merged/resolved within the window).\n */\nfunction countActiveHotfixes(repoRoot: string): number {\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const archiveDir = path.join(repoRoot, '.cleargate', 'delivery', 'archive');\n const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;\n\n let count = 0;\n\n // All in pending-sync\n let pendingEntries: string[] = [];\n try {\n pendingEntries = fs.readdirSync(pendingDir);\n } catch {\n // dir may not exist in test fixtures\n }\n for (const entry of pendingEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) count++;\n }\n\n // Recently archived (within 7-day window)\n let archiveEntries: string[] = [];\n try {\n archiveEntries = fs.readdirSync(archiveDir);\n } catch {\n // dir may not exist\n }\n for (const entry of archiveEntries) {\n if (entry.startsWith('HOTFIX-') && entry.endsWith('.md')) {\n try {\n const stat = fs.statSync(path.join(archiveDir, entry));\n if (stat.mtimeMs >= sevenDaysAgo) count++;\n } catch {\n // Skip unreadable entries\n }\n }\n }\n\n return count;\n}\n\n/**\n * Resolve the path to `.cleargate/templates/hotfix.md` relative to the repo root.\n */\nfunction resolveTemplatePath(repoRoot: string): string {\n return path.join(repoRoot, '.cleargate', 'templates', 'hotfix.md');\n}\n\n// ─── hotfixNewHandler ────────────────────────────────────────────────────────\n\n/**\n * `cleargate hotfix new <slug>`\n *\n * - Validates slug matches ^[a-z0-9-]+$.\n * - Checks rolling-window hotfix cap (≤3 per 7 days).\n * - Reads .cleargate/templates/hotfix.md and substitutes {ID}, {SLUG}, {ISO}.\n * - Writes to .cleargate/delivery/pending-sync/HOTFIX-<NNN>_<slug>.md\n * (slug with - replaced by _ in filename, matching STORY-NNN-NN_Name.md convention).\n *\n * Exits 1 on validation failure or cap exceeded; exits 0 on success.\n */\nexport function hotfixNewHandler(\n opts: { slug: string },\n cli?: HotfixCliOptions,\n): void {\n const stdoutFn = cli?.stdout ?? ((s: string) => process.stdout.write(s + '\\n'));\n const stderrFn = cli?.stderr ?? ((s: string) => process.stderr.write(s + '\\n'));\n const exitFn: (code: number) => never = cli?.exit ?? defaultExit;\n const repoRoot = cli?.cwd ?? process.cwd();\n const now = cli?.now ?? new Date().toISOString();\n\n // ── Validate slug ────────────────────────────────────────────────────────\n if (!SLUG_RE.test(opts.slug)) {\n stderrFn(`[cleargate hotfix new] slug must match ^[a-z0-9-]+$ (got: \"${opts.slug}\")`);\n return exitFn(1);\n }\n\n // ── Cap check ────────────────────────────────────────────────────────────\n const activeCount = countActiveHotfixes(repoRoot);\n if (activeCount >= 3) {\n stderrFn(\n `Hotfix cap: ≤3 per rolling 7-day window. Currently ${activeCount} active. Bundle into a sprint or downgrade one to a CR.`,\n );\n return exitFn(1);\n }\n\n // ── Next ID ──────────────────────────────────────────────────────────────\n const pendingDir = path.join(repoRoot, '.cleargate', 'delivery', 'pending-sync');\n const maxId = maxHotfixId(pendingDir);\n const nextId = maxId + 1;\n const idStr = `HOTFIX-${String(nextId).padStart(3, '0')}`;\n\n // ── Read template ────────────────────────────────────────────────────────\n const templatePath = resolveTemplatePath(repoRoot);\n let templateContent: string;\n try {\n templateContent = fs.readFileSync(templatePath, 'utf8');\n } catch {\n stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);\n return exitFn(2);\n }\n\n // ── Substitute placeholders ──────────────────────────────────────────────\n const content = templateContent\n .replace(/\\{ID\\}/g, idStr)\n .replace(/\\{SLUG\\}/g, opts.slug)\n .replace(/\\{ISO\\}/g, now);\n\n // ── Write output file ────────────────────────────────────────────────────\n // Filename convention: HOTFIX-NNN_slug_with_underscores.md\n const fileSlug = opts.slug.replace(/-/g, '_');\n const fileName = `${idStr}_${fileSlug}.md`;\n const outPath = path.join(pendingDir, fileName);\n\n try {\n fs.mkdirSync(pendingDir, { recursive: true });\n fs.writeFileSync(outPath, content, 'utf8');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n stderrFn(`[cleargate hotfix new] write failed: ${msg}`);\n return exitFn(1);\n }\n\n stdoutFn(`[cleargate hotfix new] created: ${outPath}`);\n // Explicit exit 0 to satisfy the exit-seam contract used in tests.\n return exitFn(0);\n}\n","/**\n * `cleargate mcp serve` — stdio↔HTTP MCP proxy with auto-refresh auth.\n *\n * BUG-019 fix. Claude Code spawns this as a stdio MCP server (see\n * inject-mcp-json.ts). Each line on stdin is a JSON-RPC message; the proxy\n * POSTs it to the cleargate-mcp HTTPS `/mcp` endpoint with `Authorization:\n * Bearer <access_token>`, then writes the response (single JSON OR SSE-framed\n * data events) back to stdout, one JSON-RPC message per line.\n *\n * Auth model:\n * - boot: load refresh_token from keychain → POST /auth/refresh → cache\n * access_token + persist rotated refresh_token.\n * - per-request: lazy refresh ~60s before access expiry; on 401 invalidate\n * and retry once.\n * - if refresh itself fails (no token / revoked): print actionable hint to\n * stderr and exit non-zero so Claude Code surfaces \"auth failed\".\n *\n * Notification messages (id-less JSON-RPC) get no stdout response per spec.\n *\n * Streaming: stateless server returns single JSON for cleargate's tools. SSE\n * is handled defensively for forward-compat (parses `data:` events line-wise).\n */\nimport * as readline from 'node:readline';\nimport { Readable } from 'node:stream';\nimport { loadConfig } from '../config.js';\nimport { createTokenStore } from '../auth/factory.js';\nimport { AuthFetcher, RefreshError } from '../auth/refresh.js';\n\nexport interface McpServeOptions {\n profile: string;\n /** Override baseUrl (defaults to canonical hosted server). */\n mcpUrlFlag?: string;\n /** Test seam: replaces globalThis.fetch. */\n fetch?: typeof globalThis.fetch;\n /** Test seam: replaces createTokenStore. */\n createStore?: typeof createTokenStore;\n /** Test seam: input stream (default process.stdin). */\n stdin?: NodeJS.ReadableStream;\n /** Test seam: output writer (default process.stdout.write). */\n stdout?: (s: string) => void;\n /** Test seam: stderr writer. */\n stderr?: (s: string) => void;\n /** Test seam: process.exit replacement. */\n exit?: (code: number) => never;\n /** Test seam: replaces Date.now in AuthFetcher. */\n now?: () => number;\n /** Test seam: keychain service override (forwarded to TokenStore). */\n keychainService?: string;\n /** Test seam: forced TokenStore backend (forwarded). */\n forceBackend?: 'keychain' | 'file';\n}\n\nconst DEFAULT_BASE_URL = 'https://cleargate-mcp.soula.ge';\n\nexport async function mcpServeHandler(opts: McpServeOptions): Promise<void> {\n const fetchFn = opts.fetch ?? globalThis.fetch;\n const stdout = opts.stdout ?? ((s) => process.stdout.write(s));\n const stderr = opts.stderr ?? ((s) => process.stderr.write(s));\n const exit = opts.exit ?? ((c: number): never => process.exit(c));\n\n const cfg = loadConfig({\n flags: { profile: opts.profile, mcpUrl: opts.mcpUrlFlag },\n });\n const baseUrl = cfg.mcpUrl ?? DEFAULT_BASE_URL;\n\n const store = await (opts.createStore ?? createTokenStore)({\n ...(opts.keychainService !== undefined ? { keychainService: opts.keychainService } : {}),\n ...(opts.forceBackend !== undefined ? { forceBackend: opts.forceBackend } : {}),\n });\n\n const fetcher = new AuthFetcher({\n baseUrl,\n loadRefresh: () => store.load(opts.profile),\n saveRefresh: (t) => store.save(opts.profile, t),\n ...(opts.fetch !== undefined ? { fetch: opts.fetch } : {}),\n ...(opts.now !== undefined ? { now: opts.now } : {}),\n });\n\n // Boot-time refresh so failures surface early (Claude Code shows auth-failed).\n try {\n await fetcher.getAccessToken();\n } catch (err) {\n if (err instanceof RefreshError) {\n stderr(\n `cleargate mcp serve: refresh failed (${err.status} ${err.code}). ` +\n `Run \\`cleargate join <invite-url>\\` to re-authenticate.\\n`,\n );\n } else {\n stderr(\n `cleargate mcp serve: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n return exit(1);\n }\n\n const inputStream = (opts.stdin as Readable | undefined) ?? process.stdin;\n const rl = readline.createInterface({\n input: inputStream,\n output: undefined,\n terminal: false,\n });\n\n for await (const line of rl) {\n if (!line.trim()) continue;\n try {\n await proxyOne(line, baseUrl, fetcher, fetchFn, stdout, stderr);\n } catch (err) {\n // Emit an internal-error JSON-RPC response if we have an id; otherwise\n // log and continue.\n const errMsg = err instanceof Error ? err.message : String(err);\n stderr(`cleargate mcp serve: proxy error: ${errMsg}\\n`);\n const id = extractId(line);\n if (id !== undefined) {\n stdout(\n JSON.stringify({\n jsonrpc: '2.0',\n id,\n error: { code: -32603, message: `proxy error: ${errMsg}` },\n }) + '\\n',\n );\n }\n }\n }\n}\n\nasync function proxyOne(\n line: string,\n baseUrl: string,\n fetcher: AuthFetcher,\n fetchFn: typeof globalThis.fetch,\n stdout: (s: string) => void,\n stderr: (s: string) => void,\n): Promise<void> {\n let parsed: { id?: unknown };\n try {\n parsed = JSON.parse(line) as { id?: unknown };\n } catch {\n stderr(`cleargate mcp serve: ignoring non-JSON line: ${line.slice(0, 80)}\\n`);\n return;\n }\n const isNotification = !('id' in parsed) || parsed.id === undefined || parsed.id === null;\n\n let access = await fetcher.getAccessToken();\n let res = await postFrame(baseUrl, line, access, fetchFn);\n\n if (res.status === 401) {\n fetcher.invalidate();\n access = await fetcher.getAccessToken();\n res = await postFrame(baseUrl, line, access, fetchFn);\n }\n\n if (isNotification) {\n // No response body expected. Drain to free the connection.\n await res.arrayBuffer().catch(() => undefined);\n return;\n }\n\n const ct = res.headers.get('content-type') ?? '';\n if (ct.includes('text/event-stream')) {\n await streamSse(res, stdout);\n } else {\n const text = await res.text();\n if (text.length > 0) stdout(text + '\\n');\n }\n}\n\nasync function postFrame(\n baseUrl: string,\n body: string,\n accessToken: string,\n fetchFn: typeof globalThis.fetch,\n): Promise<Response> {\n return fetchFn(`${baseUrl}/mcp`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json, text/event-stream',\n Authorization: `Bearer ${accessToken}`,\n },\n body,\n });\n}\n\nasync function streamSse(res: Response, stdout: (s: string) => void): Promise<void> {\n if (!res.body) return;\n const reader = res.body.getReader();\n const decoder = new TextDecoder('utf-8');\n let buf = '';\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let nl: number;\n while ((nl = buf.indexOf('\\n')) !== -1) {\n const ln = buf.slice(0, nl);\n buf = buf.slice(nl + 1);\n if (ln.startsWith('data:')) {\n const payload = ln.slice(5).trim();\n if (payload) stdout(payload + '\\n');\n }\n // ignore comments + event/id lines for our minimal proxy\n }\n }\n}\n\nfunction extractId(line: string): unknown {\n try {\n const obj = JSON.parse(line) as { id?: unknown };\n return 'id' in obj ? obj.id : undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * refresh.ts — token refresh exchange against the cleargate MCP server.\n *\n * `cleargate join` saves a refresh token to the OS keychain. To talk to the\n * `/mcp` HTTP endpoint Claude Code (or any client) needs a short-lived\n * access token. This module exchanges refresh→access via POST /auth/refresh,\n * persists the rotated refresh token back to the keychain, and exposes a\n * small `AuthFetcher` that lazy-refreshes before token expiry and on 401.\n *\n * BUG-019 — used by `cleargate mcp serve` (and any future surfaces).\n */\n\nexport interface RefreshExchangeResponse {\n token_type: 'Bearer';\n access_token: string;\n expires_in: number;\n refresh_token: string;\n}\n\nexport interface RefreshDeps {\n fetch?: typeof globalThis.fetch;\n /** Wall-clock for tests. Default: () => Date.now(). */\n now?: () => number;\n}\n\n/**\n * One-shot refresh. Throws on non-2xx. Caller is responsible for persisting\n * the rotated refresh token.\n */\nexport async function refreshAccessToken(\n baseUrl: string,\n refreshToken: string,\n deps: RefreshDeps = {},\n): Promise<RefreshExchangeResponse> {\n const fetchFn = deps.fetch ?? globalThis.fetch;\n const res = await fetchFn(`${baseUrl}/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'application/json' },\n body: JSON.stringify({ refresh_token: refreshToken }),\n });\n\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as { error?: string };\n throw new RefreshError(res.status, body.error ?? 'unknown_error');\n }\n\n const json = (await res.json()) as RefreshExchangeResponse;\n if (\n typeof json.access_token !== 'string' ||\n typeof json.refresh_token !== 'string' ||\n typeof json.expires_in !== 'number'\n ) {\n throw new RefreshError(500, 'malformed_response');\n }\n return json;\n}\n\nexport class RefreshError extends Error {\n constructor(public status: number, public code: string) {\n super(`refresh failed: ${status} ${code}`);\n this.name = 'RefreshError';\n }\n}\n\n/**\n * Caching wrapper. Maintains a single live access token. Refreshes when:\n * - no token cached yet\n * - cached token is within `skewSeconds` of expiry (default 60s)\n * - caller signals 401 via {@link AuthFetcher.invalidate}.\n *\n * Persists the rotated refresh token via the injected `saveRefresh` callback.\n */\nexport interface AuthFetcherOptions extends RefreshDeps {\n baseUrl: string;\n loadRefresh: () => Promise<string | null>;\n saveRefresh: (token: string) => Promise<void>;\n /** Refresh `skewSeconds` before access expiry. Default: 60. */\n skewSeconds?: number;\n}\n\nexport class AuthFetcher {\n private accessToken: string | null = null;\n private accessExpiresAt = 0;\n private inflight: Promise<string> | null = null;\n\n constructor(private readonly opts: AuthFetcherOptions) {}\n\n /** Returns a fresh access token, refreshing if needed. */\n async getAccessToken(): Promise<string> {\n const now = (this.opts.now ?? (() => Date.now()))();\n const skewMs = (this.opts.skewSeconds ?? 60) * 1000;\n if (this.accessToken && now < this.accessExpiresAt - skewMs) {\n return this.accessToken;\n }\n if (this.inflight) return this.inflight;\n\n this.inflight = this.refreshNow().finally(() => {\n this.inflight = null;\n });\n return this.inflight;\n }\n\n /** Force the next call to refresh. Used after a 401. */\n invalidate(): void {\n this.accessToken = null;\n this.accessExpiresAt = 0;\n }\n\n private async refreshNow(): Promise<string> {\n const stored = await this.opts.loadRefresh();\n if (!stored) {\n throw new RefreshError(401, 'no_refresh_token');\n }\n const exchanged = await refreshAccessToken(this.opts.baseUrl, stored, {\n ...(this.opts.fetch ? { fetch: this.opts.fetch } : {}),\n ...(this.opts.now ? { now: this.opts.now } : {}),\n });\n await this.opts.saveRefresh(exchanged.refresh_token);\n const now = (this.opts.now ?? (() => Date.now()))();\n this.accessToken = exchanged.access_token;\n this.accessExpiresAt = now + exchanged.expires_in * 1000;\n return exchanged.access_token;\n }\n}\n"],"mappings":"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","names":["resolve","body","readline","hostname","readNextLine","resolve","fs","os","path","fs","path","fs","yaml","raw","fm","stampOpts","result","fs","path","fileURLToPath","spawnSync","fs","path","fs","path","fs","path","fs","path","fs","path","state","__dirname","fs","path","fileURLToPath","compile","state","resolveDefaultTemplateDir","__dirname","fileURLToPath","fs","path","fileURLToPath","compile","state","resolveDefaultTemplateDir","__dirname","fileURLToPath","fs","path","fileURLToPath","compile","state","resolveDefaultTemplateDir","isSet","__dirname","fileURLToPath","compile","story","readFile","writeFile","existsSync","readFileSync","path","readFile","existsSync","readFileSync","readFile","state","writeFile","readline","resolve","fs","path","os","spawnSync","hostname","fs","path","fileURLToPath","spawnSync","fs","path","spawnSync","fs","path","EXCLUDED_SUFFIXES","BUCKET_ORDER","BUCKET_LABELS","rename","buildParentRef","buildChildrenRefs","buildPageBody","spawnSync","compile","path","fs","path","fs","path","spawnSync","yaml","PREFIX_MAP","basename","spawnSync","yaml","ID_PATTERN","fs","path","yaml","wiki","fs","path","fs","path","readline","resolve","fs","path","spawnSync","defaultGitRunner","spawnSync","fs","path","fileURLToPath","spawnSync","fs","path","os","cachePath","state","parseCachedGateResult","spawnSync","fileURLToPath","gate","fs","path","spawnSync","fs","path","yaml","fs","path","fs","yaml","yaml","yaml","gate","code","spawnSync","spawnSync","spawnSync","fs","path","spawnSync","execSync","yaml","fs","path","spawnSync","spawnSync","TERMINAL_STATUSES","spawnSync","yaml","spawnSync","state","TERMINAL_STATUSES","execSync","fs","path","spawnSync","defaultExit","resolveRunScript","spawnSync","state","path","spawnSync","defaultExit","resolveRunScript","spawnSync","fs","path","fs","path","basename","fs","path","BLOCK_REGEX","state","resolve","resolve","writeAtomic","state","newSha","fs","fsp","path","execSync","execSync","writeAtomic","resolve","fsPromises","path","fs","fsPromises","path","classify","fs","os","path","resolve","fs","text","fsPromises","path","fsPromises","path","mcp","proposalId","slug","num","filename","targetPath","fs","fsPromises","path","findSprintFile","fsPromises","path","fsPromises","path","writeAtomic","nowIso","mcp","classify","writeAtomic","fsPromises","path","createHash","createHash","getItemId","writeAtomic","mcp","fs","os","path","mcp","fsPromises","path","mcp","entry","getItemId","writeAtomic","fsPromises","path","itemId","getItemId","getItemType","mcp","writeAtomic","fsPromises","path","fs","path","os","fs","path","defaultExit","readline"]}