forge-workflow 0.0.10 → 0.1.0-beta.2

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 (454) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +150 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -118
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +461 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/forge/TOOLCHAIN.md +670 -0
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +121 -0
  81. package/lib/beads-sync-scaffold.js +25 -101
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +741 -77
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +17 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +0 -1
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +838 -972
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +158 -21
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +4 -1
  142. package/lib/commands/test.js +43 -27
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +43 -18
  146. package/lib/commands/worktree.js +307 -100
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +2 -2
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +35 -9
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +382 -11
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/kernel/backing-issue.js +305 -0
  174. package/lib/kernel/broker.js +1218 -0
  175. package/lib/kernel/cli-broker-factory.js +130 -0
  176. package/lib/kernel/conflict-signal.js +82 -0
  177. package/lib/kernel/evaluators.js +195 -0
  178. package/lib/kernel/fs-class.js +495 -0
  179. package/lib/kernel/issue-command-contract.js +559 -0
  180. package/lib/kernel/issue-id-resolver.js +186 -0
  181. package/lib/kernel/lease-enforcer.js +158 -0
  182. package/lib/kernel/migrations.js +333 -0
  183. package/lib/kernel/planning-buckets-schema.js +109 -0
  184. package/lib/kernel/projection-jsonl-writer.js +450 -0
  185. package/lib/kernel/readiness-model.js +329 -0
  186. package/lib/kernel/schema.js +356 -0
  187. package/lib/kernel/sqlite-driver.js +2504 -0
  188. package/lib/kernel/taxonomy-validator.js +394 -0
  189. package/lib/lefthook-check.js +3 -2
  190. package/lib/lefthook-wiring.js +413 -0
  191. package/lib/mcp-config-renderer.js +288 -0
  192. package/lib/memory/graphiti-mcp.js +106 -0
  193. package/lib/memory/router.js +387 -0
  194. package/lib/memory/typed-api.js +102 -0
  195. package/lib/memory-digest.js +195 -0
  196. package/lib/merge-rules.js +395 -0
  197. package/lib/migrate-dry-run.js +466 -0
  198. package/lib/orientation.js +863 -0
  199. package/lib/package-manager-remediation.js +103 -0
  200. package/lib/package-root.js +381 -0
  201. package/lib/patch-intent.js +890 -0
  202. package/lib/plugin-catalog.js +3 -4
  203. package/lib/plugin-manager.js +0 -5
  204. package/lib/pr-bundle.js +186 -0
  205. package/lib/pr-monitor/differ.js +195 -0
  206. package/lib/pr-monitor/events.js +0 -0
  207. package/lib/pr-monitor/gather.js +124 -0
  208. package/lib/pr-monitor/journal.js +299 -0
  209. package/lib/pr-monitor/monitor.js +146 -0
  210. package/lib/pr-monitor/render-sticky.js +157 -0
  211. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  212. package/lib/pr-monitor/watch.js +247 -0
  213. package/lib/pr-pull.js +1273 -0
  214. package/lib/pr-shepherd.js +494 -0
  215. package/lib/pr-state-validator.js +59 -0
  216. package/lib/preflight/gates.js +237 -0
  217. package/lib/preflight/runner.js +116 -0
  218. package/lib/project-discovery.js +0 -53
  219. package/lib/project-memory.js +99 -497
  220. package/lib/protected-path-manifest.js +281 -0
  221. package/lib/protected-state-surfaces.js +387 -0
  222. package/lib/release-readiness.js +2089 -0
  223. package/lib/reset.js +59 -45
  224. package/lib/review-adapter.js +68 -0
  225. package/lib/rules-sync.js +260 -0
  226. package/lib/runtime-health.js +241 -20
  227. package/lib/safety-config-renderer.js +268 -0
  228. package/lib/setup-action-log.js +1 -7
  229. package/lib/setup.js +27 -65
  230. package/lib/shell-utils.js +76 -6
  231. package/lib/skills-sync.js +330 -0
  232. package/lib/smart-status/scoring.js +17 -3
  233. package/lib/status/beads-snapshot.js +45 -2
  234. package/lib/status/presenter.js +169 -18
  235. package/lib/status/snapshot.js +186 -0
  236. package/lib/sync-backend.js +202 -0
  237. package/lib/untrusted-content.js +52 -0
  238. package/lib/upgrade-safety.js +199 -0
  239. package/lib/workflow/enforce-stage.js +296 -47
  240. package/lib/workflow/stage-transition.js +115 -0
  241. package/lib/workflow/stages.js +30 -6
  242. package/lib/workflow/state-manager.js +11 -22
  243. package/lib/workflow/state.js +23 -1
  244. package/lib/workflow-profiles.js +17 -5
  245. package/package.json +37 -35
  246. package/rules/documentation.md +19 -0
  247. package/rules/kernel-tracking.md +26 -0
  248. package/rules/security.md +22 -0
  249. package/rules/tdd.md +20 -0
  250. package/rules/workflow.md +27 -0
  251. package/scripts/auto-backing-issue.js +47 -0
  252. package/scripts/beads-context.sh +81 -57
  253. package/scripts/beads-upgrade-smoke.sh +24 -3
  254. package/scripts/bootstrap-windows-tools.sh +78 -0
  255. package/scripts/branch-protection.js +2 -3
  256. package/scripts/check-agents.js +34 -137
  257. package/scripts/commitlint.js +3 -1
  258. package/scripts/conflict-detect.sh +3 -0
  259. package/scripts/dep-guard.sh +22 -3
  260. package/scripts/file-index.sh +3 -0
  261. package/scripts/forge-team/lib/claim.sh +34 -18
  262. package/scripts/forge-team/lib/dashboard.sh +61 -86
  263. package/scripts/forge-team/lib/epic.sh +99 -263
  264. package/scripts/forge-team/lib/hooks.sh +26 -28
  265. package/scripts/forge-team/lib/identity.sh +4 -4
  266. package/scripts/forge-team/lib/sync-github.sh +49 -84
  267. package/scripts/forge-team/lib/verify.sh +93 -83
  268. package/scripts/forge-team/lib/workload.sh +41 -65
  269. package/scripts/forge-team/tests/claim.test.sh +25 -19
  270. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  271. package/scripts/forge-team/tests/epic.test.sh +52 -71
  272. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  273. package/scripts/forge-team/tests/identity.test.sh +3 -3
  274. package/scripts/forge-team/tests/integration.test.sh +44 -66
  275. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  276. package/scripts/forge-team/tests/verify.test.sh +37 -46
  277. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  278. package/scripts/forge-team/tests/workload.test.sh +32 -66
  279. package/scripts/gen-command-manifest.js +153 -0
  280. package/scripts/gen-embedded-assets.mjs +129 -0
  281. package/scripts/install.ps1 +139 -0
  282. package/scripts/install.sh +268 -0
  283. package/scripts/lib/release-asset.mjs +84 -0
  284. package/scripts/parity-check.mjs +145 -0
  285. package/scripts/parity-check.test.mjs +58 -0
  286. package/scripts/pin-agentic-workflow-images.js +112 -0
  287. package/scripts/pr-coordinator.sh +3 -0
  288. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  289. package/scripts/preflight.sh +21 -94
  290. package/scripts/protected-state-check.js +104 -0
  291. package/scripts/smart-status.sh +60 -57
  292. package/scripts/spikes/config-race-bench.js +111 -0
  293. package/scripts/spikes/harness-capability-matrix.js +13 -0
  294. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  295. package/scripts/spikes/protected-path-manifest.js +20 -0
  296. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  297. package/scripts/sync-agent-skills.js +62 -0
  298. package/scripts/sync-utils.sh +3 -0
  299. package/scripts/test-ci-shard.js +13 -6
  300. package/scripts/test.js +95 -12
  301. package/skills/claim-safety/SKILL.md +102 -0
  302. package/skills/claim-safety/evals/evals.json +46 -0
  303. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  304. package/skills/dev/evals/evals.json +50 -0
  305. package/skills/hermes-forge/SKILL.md +185 -0
  306. package/skills/hermes-forge/evals/evals.json +46 -0
  307. package/skills/issue-basics/SKILL.md +111 -0
  308. package/skills/issue-basics/evals/evals.json +46 -0
  309. package/skills/kernel/SKILL.md +166 -0
  310. package/skills/kernel/evals/evals.json +50 -0
  311. package/skills/memory/SKILL.md +102 -0
  312. package/skills/parallel-deep-research/SKILL.md +14 -11
  313. package/skills/parallel-deep-research/evals/evals.json +11 -27
  314. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  315. package/skills/plan/evals/evals.json +42 -0
  316. package/skills/research/SKILL.md +195 -0
  317. package/skills/research/evals/evals.json +42 -0
  318. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  319. package/skills/review/evals/evals.json +42 -0
  320. package/skills/rollback/SKILL.md +110 -0
  321. package/skills/rollback/evals/evals.json +46 -0
  322. package/skills/rollback/references/methods.md +204 -0
  323. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  324. package/skills/shepherd/SKILL.md +66 -0
  325. package/skills/shepherd/evals/evals.json +42 -0
  326. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  327. package/skills/ship/evals/evals.json +42 -0
  328. package/skills/smith/SKILL.md +142 -0
  329. package/skills/smith/evals/evals.json +46 -0
  330. package/skills/smith/references/autonomy-and-gates.md +94 -0
  331. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  332. package/skills/sonarcloud/evals/evals.json +46 -0
  333. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  334. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  335. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  336. package/skills/status/evals/evals.json +50 -0
  337. package/skills/triage-ready/SKILL.md +121 -0
  338. package/skills/triage-ready/evals/evals.json +42 -0
  339. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  340. package/skills/validate/evals/evals.json +42 -0
  341. package/skills/verify/SKILL.md +299 -0
  342. package/skills/verify/evals/evals.json +50 -0
  343. package/.claude/commands/dev.md +0 -345
  344. package/.claude/commands/plan.md +0 -566
  345. package/.claude/commands/premerge.md +0 -186
  346. package/.claude/commands/research.md +0 -42
  347. package/.claude/commands/review.md +0 -451
  348. package/.claude/commands/rollback.md +0 -721
  349. package/.claude/commands/ship.md +0 -213
  350. package/.claude/commands/sonarcloud.md +0 -152
  351. package/.claude/commands/status.md +0 -90
  352. package/.claude/commands/validate.md +0 -288
  353. package/.claude/commands/verify.md +0 -269
  354. package/.claude/rules/workflow.md +0 -121
  355. package/.cline/workflows/dev.md +0 -342
  356. package/.cline/workflows/plan.md +0 -563
  357. package/.cline/workflows/premerge.md +0 -183
  358. package/.cline/workflows/research.md +0 -39
  359. package/.cline/workflows/review.md +0 -448
  360. package/.cline/workflows/rollback.md +0 -718
  361. package/.cline/workflows/ship.md +0 -210
  362. package/.cline/workflows/sonarcloud.md +0 -146
  363. package/.cline/workflows/status.md +0 -87
  364. package/.cline/workflows/validate.md +0 -285
  365. package/.cline/workflows/verify.md +0 -266
  366. package/.codex/config.toml +0 -11
  367. package/.codex/skills/dev/SKILL.md +0 -345
  368. package/.codex/skills/plan/SKILL.md +0 -566
  369. package/.codex/skills/premerge/SKILL.md +0 -186
  370. package/.codex/skills/research/SKILL.md +0 -42
  371. package/.codex/skills/review/SKILL.md +0 -451
  372. package/.codex/skills/rollback/SKILL.md +0 -721
  373. package/.codex/skills/ship/SKILL.md +0 -213
  374. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  375. package/.codex/skills/status/SKILL.md +0 -90
  376. package/.codex/skills/validate/SKILL.md +0 -288
  377. package/.codex/skills/verify/SKILL.md +0 -269
  378. package/.cursor/commands/dev.md +0 -342
  379. package/.cursor/commands/plan.md +0 -563
  380. package/.cursor/commands/premerge.md +0 -183
  381. package/.cursor/commands/research.md +0 -39
  382. package/.cursor/commands/review.md +0 -448
  383. package/.cursor/commands/ship.md +0 -210
  384. package/.cursor/commands/sonarcloud.md +0 -146
  385. package/.cursor/commands/status.md +0 -87
  386. package/.cursor/commands/validate.md +0 -285
  387. package/.cursor/commands/verify.md +0 -266
  388. package/.cursorrules +0 -149
  389. package/.github/prompts/premerge.prompt.md +0 -188
  390. package/.github/prompts/research.prompt.md +0 -44
  391. package/.github/prompts/rollback.prompt.md +0 -723
  392. package/.github/prompts/verify.prompt.md +0 -271
  393. package/.github/workflows/beads-to-github.yml +0 -89
  394. package/.github/workflows/github-to-beads.yml +0 -100
  395. package/.kilocode/workflows/dev.md +0 -346
  396. package/.kilocode/workflows/plan.md +0 -567
  397. package/.kilocode/workflows/premerge.md +0 -187
  398. package/.kilocode/workflows/research.md +0 -43
  399. package/.kilocode/workflows/review.md +0 -452
  400. package/.kilocode/workflows/rollback.md +0 -722
  401. package/.kilocode/workflows/ship.md +0 -214
  402. package/.kilocode/workflows/sonarcloud.md +0 -150
  403. package/.kilocode/workflows/status.md +0 -91
  404. package/.kilocode/workflows/validate.md +0 -289
  405. package/.kilocode/workflows/verify.md +0 -270
  406. package/.opencode/commands/dev.md +0 -345
  407. package/.opencode/commands/plan.md +0 -566
  408. package/.opencode/commands/premerge.md +0 -186
  409. package/.opencode/commands/research.md +0 -42
  410. package/.opencode/commands/review.md +0 -451
  411. package/.opencode/commands/rollback.md +0 -721
  412. package/.opencode/commands/ship.md +0 -213
  413. package/.opencode/commands/sonarcloud.md +0 -149
  414. package/.opencode/commands/status.md +0 -90
  415. package/.opencode/commands/validate.md +0 -288
  416. package/.opencode/commands/verify.md +0 -269
  417. package/.roo/commands/dev.md +0 -346
  418. package/.roo/commands/plan.md +0 -567
  419. package/.roo/commands/premerge.md +0 -187
  420. package/.roo/commands/research.md +0 -43
  421. package/.roo/commands/review.md +0 -452
  422. package/.roo/commands/rollback.md +0 -722
  423. package/.roo/commands/ship.md +0 -214
  424. package/.roo/commands/sonarcloud.md +0 -150
  425. package/.roo/commands/status.md +0 -91
  426. package/.roo/commands/validate.md +0 -289
  427. package/.roo/commands/verify.md +0 -270
  428. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  429. package/docs/GREPTILE_SETUP.md +0 -400
  430. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  431. package/docs/SETUP.md +0 -663
  432. package/docs/VALIDATION.md +0 -363
  433. package/lib/agents/cline.plugin.json +0 -29
  434. package/lib/agents/copilot.plugin.json +0 -24
  435. package/lib/agents/kilocode.plugin.json +0 -22
  436. package/lib/agents/opencode.plugin.json +0 -23
  437. package/lib/agents/roo.plugin.json +0 -30
  438. package/lib/beads-bootstrap.js +0 -225
  439. package/lib/beads-health-check.js +0 -188
  440. package/lib/commands/commands-reset.js +0 -147
  441. package/opencode.json +0 -67
  442. package/scripts/beads-context.test.js +0 -584
  443. package/scripts/github-beads-sync/comment.mjs +0 -64
  444. package/scripts/github-beads-sync/config.mjs +0 -148
  445. package/scripts/github-beads-sync/github-api.mjs +0 -131
  446. package/scripts/github-beads-sync/index.mjs +0 -356
  447. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  448. package/scripts/github-beads-sync/mapping.mjs +0 -132
  449. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  450. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  451. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  452. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  453. package/scripts/github-beads-sync.config.json +0 -26
  454. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,559 @@
1
+ 'use strict';
2
+
3
+ const ISSUE_COMMAND_SCHEMA_VERSION = 'forge.issue.v1';
4
+ const ISSUE_COMMAND_ERROR_SCHEMA_VERSION = 'forge.issue.error.v1';
5
+
6
+ const ISSUE_COMMAND_EXIT_CODES = Object.freeze({
7
+ success: 0,
8
+ internal: 1,
9
+ usage: 2,
10
+ notFound: 3,
11
+ conflict: 4,
12
+ unavailable: 5,
13
+ validation: 6,
14
+ });
15
+
16
+ // Canonical D18 taxonomy. Types and statuses are enforced at the validation layer
17
+ // (see taxonomy-validator), not as DB constraints, so label-based extensibility and
18
+ // derived readiness (ready/blocked) stay outside the stored status set.
19
+ const ISSUE_TYPES = Object.freeze(['epic', 'task', 'bug', 'decision']);
20
+ // `backlog` is a parked lifecycle state (ideas discussed but not yet scheduled): a real
21
+ // stored status, distinct from the derived readiness `backlog` state. Parked issues are
22
+ // NOT workable (excluded from WORKABLE_STATUSES) and NOT stale, but ARE counted in rollups.
23
+ const ISSUE_STATUSES = Object.freeze(['open', 'in_progress', 'review', 'done', 'cancelled', 'backlog']);
24
+ // Canonical priority labels P0..P4. The bare integers 0..4 are also accepted on input
25
+ // (the CLI documents `--priority 1`; normalizePriority maps ranks 0..4) — see
26
+ // isValidIssuePriority. P5+ / non-numeric labels are rejected at the validation layer,
27
+ // matching MAX_DISPLAY_PRIORITY_RANK in taxonomy-validator.
28
+ const ISSUE_PRIORITIES = Object.freeze(['P0', 'P1', 'P2', 'P3', 'P4']);
29
+
30
+ // True when `priority` is a canonical P0..P4 label (case-insensitive) or a bare
31
+ // integer 0..4. Used by the create/update validation gate to reject junk priorities
32
+ // (P9, BOGUS, -1) instead of persisting them verbatim — Beads rejected them too.
33
+ function isValidIssuePriority(priority) {
34
+ if (typeof priority === 'number') {
35
+ return Number.isInteger(priority) && priority >= 0 && priority <= 4;
36
+ }
37
+ if (typeof priority !== 'string') return false;
38
+ const raw = priority.trim().toUpperCase();
39
+ const match = /^P?([0-9]+)$/.exec(raw);
40
+ if (!match) return false;
41
+ const rank = Number(match[1]);
42
+ return rank >= 0 && rank <= 4;
43
+ }
44
+
45
+ // Canonicalize a priority input to its P0..P4 label. Accepts the canonical labels
46
+ // (case-insensitive) and the bare ranks 0..4 the CLI documents (`--priority 1`), and
47
+ // maps BOTH forms to the SAME label ('1' and 'P1' -> 'P1'). Out-of-range / non-priority
48
+ // input is returned UNCHANGED so the create/update validation gate (isValidIssuePriority)
49
+ // still REJECTS it rather than silently clamping (P9 must error, not become P4). The
50
+ // stored canonical form is this label; priority_rank is its numeric rank.
51
+ function normalizePriority(priority) {
52
+ if (!isValidIssuePriority(priority)) return priority;
53
+ const raw = typeof priority === 'number' ? String(priority) : priority.trim().toUpperCase();
54
+ const rank = Number(/^P?([0-9]+)$/.exec(raw)[1]);
55
+ return `P${rank}`;
56
+ }
57
+
58
+ function deepFreeze(value) {
59
+ if (!value || typeof value !== 'object') return value;
60
+ Object.freeze(value);
61
+ for (const nestedValue of Object.values(value)) {
62
+ deepFreeze(nestedValue);
63
+ }
64
+ return value;
65
+ }
66
+
67
+ const ISSUE_SUMMARY_SCHEMA = {
68
+ type: 'object',
69
+ required: ['id', 'title', 'type', 'status', 'revision'],
70
+ properties: {
71
+ id: { type: 'string' },
72
+ title: { type: 'string' },
73
+ body: { type: ['string', 'null'] },
74
+ type: { enum: [...ISSUE_TYPES] },
75
+ status: { enum: [...ISSUE_STATUSES] },
76
+ priority: { type: 'string' },
77
+ rank: { type: 'number' },
78
+ revision: { type: 'integer', minimum: 0 },
79
+ blocked: { type: 'boolean' },
80
+ claimed_by: { type: ['string', 'null'] },
81
+ // KAP-2: stored-but-previously-unprojected fields, surfaced for agents.
82
+ parent_id: { type: ['string', 'null'] },
83
+ labels: { type: 'array', items: { type: 'string' } },
84
+ dependencies: { type: 'array', items: { type: 'string' } },
85
+ // Epic/reverse-dependency exposure: `dependents` are the ids that depend on this
86
+ // issue (inverse of `dependencies`); `blocked_by` is the readiness model's live
87
+ // blocker subset. Additive superset on every read op (schema is permissive —
88
+ // no additionalProperties:false — so older consumers still validate).
89
+ dependents: { type: 'array', items: { type: 'string' } },
90
+ blocked_by: { type: 'array', items: { type: 'string' } },
91
+ created_at: { type: 'string' },
92
+ updated_at: { type: 'string' },
93
+ // KAP-10 (acceptance_criteria/design/notes) + KAP-11 (assignee): nullable
94
+ // content fields and a persistent assignee. Optional (NOT required) so summaries
95
+ // that omit them still validate.
96
+ acceptance_criteria: { type: ['string', 'null'] },
97
+ design: { type: ['string', 'null'] },
98
+ notes: { type: ['string', 'null'] },
99
+ assignee: { type: ['string', 'null'] },
100
+ // Beads full-fidelity import: author, close timestamp, raw close reason and a
101
+ // verbatim metadata JSON blob. Optional (NOT required) and nullable so summaries
102
+ // that omit them still validate.
103
+ created_by: { type: ['string', 'null'] },
104
+ closed_at: { type: ['string', 'null'] },
105
+ close_reason: { type: ['string', 'null'] },
106
+ metadata: { type: ['string', 'null'] },
107
+ // KAP-3: `show` attaches the issue's comments; other reads (list/ready/search/
108
+ // stats) omit the key. Optional (NOT in required) so comment-less summaries still
109
+ // validate. Each item carries id/body/actor/created_at (body may be null).
110
+ comments: {
111
+ type: 'array',
112
+ items: {
113
+ type: 'object',
114
+ required: ['id', 'body', 'actor', 'created_at'],
115
+ properties: {
116
+ id: { type: 'string' },
117
+ body: { type: ['string', 'null'] },
118
+ actor: { type: 'string' },
119
+ created_at: { type: 'string' },
120
+ },
121
+ },
122
+ },
123
+ // KAP-12: `lint` attaches a content-validation result to each FAILING issue;
124
+ // other reads (list/ready/search/...) omit the key. Optional (NOT in required)
125
+ // so validation-less summaries still validate. rules_failed names the broken
126
+ // content rules (e.g. 'missing_acceptance_criteria').
127
+ validation: {
128
+ type: 'object',
129
+ required: ['rules_failed'],
130
+ properties: {
131
+ rules_failed: { type: 'array', items: { type: 'string' } },
132
+ },
133
+ },
134
+ },
135
+ };
136
+
137
+ const NEXT_COMMANDS_SCHEMA = {
138
+ type: 'array',
139
+ items: { type: 'string' },
140
+ };
141
+
142
+ const ISSUE_COMMAND_RESPONSE_SCHEMAS = deepFreeze({
143
+ issue: {
144
+ type: 'object',
145
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
146
+ properties: {
147
+ ok: { const: true },
148
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
149
+ command: { type: 'string' },
150
+ data: ISSUE_SUMMARY_SCHEMA,
151
+ next_commands: NEXT_COMMANDS_SCHEMA,
152
+ },
153
+ },
154
+ issueList: {
155
+ type: 'object',
156
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
157
+ properties: {
158
+ ok: { const: true },
159
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
160
+ command: { type: 'string' },
161
+ data: {
162
+ type: 'object',
163
+ required: ['issues'],
164
+ properties: {
165
+ issues: {
166
+ type: 'array',
167
+ items: ISSUE_SUMMARY_SCHEMA,
168
+ },
169
+ count: { type: 'integer', minimum: 0 },
170
+ truncated: { type: 'boolean' },
171
+ },
172
+ },
173
+ next_commands: NEXT_COMMANDS_SCHEMA,
174
+ },
175
+ },
176
+ stats: {
177
+ type: 'object',
178
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
179
+ properties: {
180
+ ok: { const: true },
181
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
182
+ command: { type: 'string' },
183
+ data: {
184
+ type: 'object',
185
+ required: ['counts'],
186
+ properties: {
187
+ counts: { type: 'object' },
188
+ ready_count: { type: 'integer', minimum: 0 },
189
+ blocked_count: { type: 'integer', minimum: 0 },
190
+ active_claims: { type: 'integer', minimum: 0 },
191
+ },
192
+ },
193
+ next_commands: NEXT_COMMANDS_SCHEMA,
194
+ },
195
+ },
196
+ // KAP-7: derived read responses. `blocked` and `orphans` reuse the issueList
197
+ // shape above (an array of ISSUE_SUMMARY_SCHEMA + count). `stale` reuses that
198
+ // shape and additionally carries the threshold_days (in days) the staleness
199
+ // window was computed against.
200
+ staleList: {
201
+ type: 'object',
202
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
203
+ properties: {
204
+ ok: { const: true },
205
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
206
+ command: { type: 'string' },
207
+ data: {
208
+ type: 'object',
209
+ required: ['issues', 'count', 'threshold_days'],
210
+ properties: {
211
+ issues: {
212
+ type: 'array',
213
+ items: ISSUE_SUMMARY_SCHEMA,
214
+ },
215
+ count: { type: 'integer', minimum: 0 },
216
+ threshold_days: { type: 'integer' },
217
+ },
218
+ },
219
+ next_commands: NEXT_COMMANDS_SCHEMA,
220
+ },
221
+ },
222
+ // Epic support: `children` returns the epic header, its DIRECT children (each a full
223
+ // ISSUE_SUMMARY_SCHEMA), and a kernel-computed rollup. The rollup owns the status
224
+ // vocabulary so consumers never hard-code status names; `percentage` is done-only.
225
+ children: {
226
+ type: 'object',
227
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
228
+ properties: {
229
+ ok: { const: true },
230
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
231
+ command: { type: 'string' },
232
+ data: {
233
+ type: 'object',
234
+ required: ['epic', 'children', 'rollup', 'count'],
235
+ properties: {
236
+ epic: {
237
+ type: 'object',
238
+ required: ['id', 'title', 'type', 'status'],
239
+ properties: {
240
+ id: { type: 'string' },
241
+ title: { type: 'string' },
242
+ type: { enum: [...ISSUE_TYPES] },
243
+ status: { enum: [...ISSUE_STATUSES] },
244
+ },
245
+ },
246
+ children: {
247
+ type: 'array',
248
+ items: ISSUE_SUMMARY_SCHEMA,
249
+ },
250
+ rollup: {
251
+ type: 'object',
252
+ required: ['total', 'done', 'percentage', 'by_status'],
253
+ properties: {
254
+ total: { type: 'integer', minimum: 0 },
255
+ done: { type: 'integer', minimum: 0 },
256
+ in_progress: { type: 'integer', minimum: 0 },
257
+ open: { type: 'integer', minimum: 0 },
258
+ review: { type: 'integer', minimum: 0 },
259
+ cancelled: { type: 'integer', minimum: 0 },
260
+ backlog: { type: 'integer', minimum: 0 },
261
+ blocked: { type: 'integer', minimum: 0 },
262
+ percentage: { type: 'integer', minimum: 0 },
263
+ by_status: { type: 'object' },
264
+ },
265
+ },
266
+ count: { type: 'integer', minimum: 0 },
267
+ },
268
+ },
269
+ next_commands: NEXT_COMMANDS_SCHEMA,
270
+ },
271
+ },
272
+ // Issue 7dc229d4: the active-lease/claims read. `claims` is an array of live
273
+ // lease rows (state='active' AND not expired at read time). Each row carries the
274
+ // full who/what/where the dashboard live layer needs: actor + session_id +
275
+ // worktree_id + expires_at + issue_id (plus the lease id and claimed_at). The
276
+ // kernel_claims schema has no `agent` column, so none is surfaced. session_id /
277
+ // worktree_id / expires_at are nullable (a claim may carry none / never expire).
278
+ claimsList: {
279
+ type: 'object',
280
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
281
+ properties: {
282
+ ok: { const: true },
283
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
284
+ command: { type: 'string' },
285
+ data: {
286
+ type: 'object',
287
+ required: ['claims', 'count'],
288
+ properties: {
289
+ claims: {
290
+ type: 'array',
291
+ items: {
292
+ type: 'object',
293
+ required: ['id', 'issue_id', 'actor', 'session_id', 'worktree_id', 'claimed_at', 'expires_at'],
294
+ properties: {
295
+ id: { type: 'string' },
296
+ issue_id: { type: 'string' },
297
+ actor: { type: ['string', 'null'] },
298
+ session_id: { type: ['string', 'null'] },
299
+ worktree_id: { type: ['string', 'null'] },
300
+ claimed_at: { type: ['string', 'null'] },
301
+ expires_at: { type: ['string', 'null'] },
302
+ },
303
+ },
304
+ },
305
+ count: { type: 'integer', minimum: 0 },
306
+ },
307
+ },
308
+ next_commands: NEXT_COMMANDS_SCHEMA,
309
+ },
310
+ },
311
+ mutation: {
312
+ type: 'object',
313
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
314
+ properties: {
315
+ ok: { const: true },
316
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
317
+ command: { type: 'string' },
318
+ data: {
319
+ type: 'object',
320
+ required: ['id', 'revision'],
321
+ properties: {
322
+ id: { type: 'string' },
323
+ revision: { type: 'integer', minimum: 0 },
324
+ issue: ISSUE_SUMMARY_SCHEMA,
325
+ comment_id: { type: 'string' },
326
+ dependency_id: { type: 'string' },
327
+ claim_id: { type: 'string' },
328
+ // KAP-8: close only — ids that became ready now the issue is done.
329
+ // OPTIONAL (not in `required`) so every other mutation still validates.
330
+ newly_unblocked: { type: 'array', items: { type: 'string' } },
331
+ projection: {
332
+ type: 'object',
333
+ properties: {
334
+ status: { type: 'string' },
335
+ targets: { type: 'array', items: { type: 'string' } },
336
+ },
337
+ },
338
+ },
339
+ },
340
+ next_commands: NEXT_COMMANDS_SCHEMA,
341
+ },
342
+ },
343
+ // Multi-id close (`forge issue close <a> <b> ...`). The kernel close op closes a
344
+ // SINGLE id, so the CLI fans out one mutation per id and aggregates the outcomes
345
+ // into ONE envelope (never a bare array — that broke envelope parity). `ok` is a
346
+ // plain boolean here (true only when EVERY id closed); per-id outcomes live in
347
+ // `data.results`, and `data.closed` lists the ids that reached terminal state.
348
+ // Each result echoes the single-close mutation fields (revision/newly_unblocked)
349
+ // on success or the forge.issue.error.v1 `error` object on failure.
350
+ mutationBatch: {
351
+ type: 'object',
352
+ required: ['ok', 'schema_version', 'command', 'data', 'next_commands'],
353
+ properties: {
354
+ ok: { type: 'boolean' },
355
+ schema_version: { const: ISSUE_COMMAND_SCHEMA_VERSION },
356
+ command: { type: 'string' },
357
+ data: {
358
+ type: 'object',
359
+ required: ['results', 'count', 'closed'],
360
+ properties: {
361
+ results: {
362
+ type: 'array',
363
+ items: {
364
+ type: 'object',
365
+ required: ['id', 'ok'],
366
+ properties: {
367
+ id: { type: 'string' },
368
+ ok: { type: 'boolean' },
369
+ revision: { type: 'integer', minimum: 0 },
370
+ newly_unblocked: { type: 'array', items: { type: 'string' } },
371
+ error: { type: 'object' },
372
+ },
373
+ },
374
+ },
375
+ count: { type: 'integer', minimum: 0 },
376
+ closed: { type: 'array', items: { type: 'string' } },
377
+ },
378
+ },
379
+ next_commands: NEXT_COMMANDS_SCHEMA,
380
+ },
381
+ },
382
+ });
383
+
384
+ const ISSUE_COMMAND_ERROR_SCHEMA = deepFreeze({
385
+ type: 'object',
386
+ required: ['ok', 'schema_version', 'command', 'error', 'next_commands'],
387
+ properties: {
388
+ ok: { const: false },
389
+ schema_version: { const: ISSUE_COMMAND_ERROR_SCHEMA_VERSION },
390
+ command: { type: 'string' },
391
+ error: {
392
+ type: 'object',
393
+ required: ['code', 'message', 'exit_code', 'retryable'],
394
+ properties: {
395
+ code: { type: 'string' },
396
+ message: { type: 'string' },
397
+ exit_code: { type: 'integer' },
398
+ retryable: { type: 'boolean' },
399
+ details: { type: 'object' },
400
+ },
401
+ },
402
+ next_commands: NEXT_COMMANDS_SCHEMA,
403
+ },
404
+ });
405
+
406
+ function command(id, invocation, operation, mode, outputSchema, nextCommands) {
407
+ return {
408
+ id,
409
+ invocation,
410
+ operation,
411
+ mode,
412
+ schemaVersion: ISSUE_COMMAND_SCHEMA_VERSION,
413
+ outputSchema,
414
+ errorShape: ISSUE_COMMAND_ERROR_SCHEMA_VERSION,
415
+ exitCodes: ISSUE_COMMAND_EXIT_CODES,
416
+ nextCommands,
417
+ };
418
+ }
419
+
420
+ const COMMANDS = [
421
+ command('issue.ready', 'forge issue ready --json', 'ready', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.issueList, [
422
+ 'forge claim <id>',
423
+ 'forge issue show <id> --json',
424
+ ]),
425
+ command('issue.list', 'forge issue list --json', 'list', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.issueList, [
426
+ 'forge issue show <id> --json',
427
+ 'forge issue search <query> --json',
428
+ ]),
429
+ command('issue.show', 'forge issue show <id> --json', 'show', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.issue, [
430
+ 'forge claim <id>',
431
+ 'forge issue comment <id> "<note>"',
432
+ ]),
433
+ command('issue.search', 'forge issue search <query> --json', 'search', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.issueList, [
434
+ 'forge issue show <id> --json',
435
+ 'forge claim <id>',
436
+ ]),
437
+ command('issue.stats', 'forge issue stats --json', 'stats', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.stats, [
438
+ 'forge issue ready --json',
439
+ 'forge issue list --json',
440
+ ]),
441
+ command('issue.blocked', 'forge blocked --json', 'blocked', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.issueList, [
442
+ 'forge issue show <id> --json',
443
+ 'forge issue ready --json',
444
+ ]),
445
+ command('issue.stale', 'forge stale --json', 'stale', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.staleList, [
446
+ 'forge issue show <id> --json',
447
+ 'forge issue update <id>',
448
+ ]),
449
+ command('issue.orphans', 'forge orphans --json', 'orphans', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.issueList, [
450
+ 'forge issue show <id> --json',
451
+ 'forge issue dep add <issue-id> <blocks-issue-id>',
452
+ ]),
453
+ command('issue.lint', 'forge lint --json', 'lint', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.issueList, [
454
+ 'forge issue show <id> --json',
455
+ 'forge issue update <id> --acceptance "<criteria>"',
456
+ ]),
457
+ command('issue.children', 'forge issue children <id> --json', 'children', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.children, [
458
+ 'forge issue show <id> --json',
459
+ 'forge claim <id>',
460
+ ]),
461
+ command('issue.claims', 'forge claims --json', 'claims', 'read', ISSUE_COMMAND_RESPONSE_SCHEMAS.claimsList, [
462
+ 'forge issue show <id> --json',
463
+ 'forge claim <id>',
464
+ ]),
465
+ command('issue.create', 'forge issue create', 'create', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
466
+ 'forge issue show <id> --json',
467
+ 'forge claim <id>',
468
+ ]),
469
+ command('issue.update', 'forge issue update', 'update', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
470
+ 'forge issue show <id> --json',
471
+ ]),
472
+ command('issue.close', 'forge issue close', 'close', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
473
+ 'forge issue ready --json',
474
+ ]),
475
+ command('issue.comment', 'forge issue comment', 'comment', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
476
+ 'forge issue show <id> --json',
477
+ ]),
478
+ command('issue.dep.add', 'forge issue dep add', 'dep.add', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
479
+ 'forge issue show <id> --json',
480
+ 'forge issue ready --json',
481
+ ]),
482
+ command('issue.dep.remove', 'forge issue dep remove', 'dep.remove', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
483
+ 'forge issue ready --json',
484
+ ]),
485
+ command('claim', 'forge claim <id>', 'claim', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
486
+ 'forge issue show <id> --json',
487
+ 'forge release <id>',
488
+ ]),
489
+ command('release', 'forge release <id>', 'release', 'mutation', ISSUE_COMMAND_RESPONSE_SCHEMAS.mutation, [
490
+ 'forge issue ready --json',
491
+ ]),
492
+ ];
493
+
494
+ const ISSUE_COMMAND_CONTRACT = deepFreeze({
495
+ version: ISSUE_COMMAND_SCHEMA_VERSION,
496
+ errorSchemaVersion: ISSUE_COMMAND_ERROR_SCHEMA_VERSION,
497
+ commands: COMMANDS,
498
+ exitCodes: ISSUE_COMMAND_EXIT_CODES,
499
+ });
500
+
501
+ const COMMAND_BY_ID = new Map(ISSUE_COMMAND_CONTRACT.commands.map(candidate => [candidate.id, candidate]));
502
+
503
+ function getIssueCommandContract(id) {
504
+ return COMMAND_BY_ID.get(id) || null;
505
+ }
506
+
507
+ // Resolve the catalog next_commands for a command, substituting the concrete issue
508
+ // id into the `<id>` placeholder when the response carries a single id (show and the
509
+ // mutation responses). Multi-issue responses (list/ready/blocked/...) keep the
510
+ // `<id>` template since there is no single id to bind. Unknown commands → [].
511
+ function resolveNextCommands(commandId, data = {}) {
512
+ const contract = COMMAND_BY_ID.get(commandId);
513
+ const templates = contract ? contract.nextCommands : [];
514
+ // Most single-id responses store the id at `data.id`; `issue.children` instead
515
+ // anchors on the epic, so fall back to `data.epic.id` — otherwise its
516
+ // next_commands would emit literal `<id>` placeholders.
517
+ const id = data && typeof data.id === 'string'
518
+ ? data.id
519
+ : (data && data.epic && typeof data.epic.id === 'string' ? data.epic.id : null);
520
+ return id ? templates.map(entry => entry.replace(/<id>/g, id)) : [...templates];
521
+ }
522
+
523
+ function formatIssueCommandError(options = {}) {
524
+ const error = {
525
+ code: options.code || 'FORGE_ISSUE_ERROR',
526
+ message: options.message || 'Forge issue command failed',
527
+ exit_code: Number.isInteger(options.exitCode) ? options.exitCode : ISSUE_COMMAND_EXIT_CODES.internal,
528
+ retryable: Boolean(options.retryable),
529
+ };
530
+
531
+ if (options.details && typeof options.details === 'object') {
532
+ error.details = options.details;
533
+ }
534
+
535
+ return {
536
+ ok: false,
537
+ schema_version: ISSUE_COMMAND_ERROR_SCHEMA_VERSION,
538
+ command: options.command || 'forge issue',
539
+ error,
540
+ next_commands: Array.isArray(options.nextCommands) ? options.nextCommands : [],
541
+ };
542
+ }
543
+
544
+ module.exports = {
545
+ ISSUE_COMMAND_CONTRACT,
546
+ ISSUE_COMMAND_ERROR_SCHEMA,
547
+ ISSUE_COMMAND_ERROR_SCHEMA_VERSION,
548
+ ISSUE_COMMAND_EXIT_CODES,
549
+ ISSUE_COMMAND_RESPONSE_SCHEMAS,
550
+ ISSUE_COMMAND_SCHEMA_VERSION,
551
+ ISSUE_PRIORITIES,
552
+ ISSUE_STATUSES,
553
+ ISSUE_TYPES,
554
+ formatIssueCommandError,
555
+ getIssueCommandContract,
556
+ isValidIssuePriority,
557
+ normalizePriority,
558
+ resolveNextCommands,
559
+ };