forge-workflow 0.0.10 → 0.1.0-beta.3

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 (468) 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 +82 -5
  5. package/.forge/hooks/forge-native-hook.js +431 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +709 -0
  9. package/CLAUDE.md +9 -118
  10. package/QUICKSTART.md +175 -0
  11. package/README.md +275 -365
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +532 -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 +121 -0
  29. package/docs/guides/SUPPORT.md +190 -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 +214 -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 +155 -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/activation/ensure-forge-home.js +135 -0
  67. package/lib/adapter-cli.js +307 -0
  68. package/lib/adapters/beads-issue-adapter.js +127 -0
  69. package/lib/adapters/beads-kernel-compat.js +1109 -0
  70. package/lib/adapters/greptile-review-adapter.js +141 -0
  71. package/lib/adapters/kernel-issue-adapter.js +101 -0
  72. package/lib/adapters/pr-state-adapter.js +484 -0
  73. package/lib/adoption-profiles.js +139 -0
  74. package/lib/agents/README.md +2 -6
  75. package/lib/agents/claude.plugin.json +3 -8
  76. package/lib/agents/codex.plugin.json +9 -1
  77. package/lib/agents/cursor.plugin.json +2 -6
  78. package/lib/agents/hermes.plugin.json +22 -0
  79. package/lib/agents-config.js +39 -1236
  80. package/lib/audit-evidence.js +282 -0
  81. package/lib/beads-detect.js +60 -0
  82. package/lib/beads-nudge.js +91 -0
  83. package/lib/beads-setup.js +121 -0
  84. package/lib/beads-sync-scaffold.js +25 -101
  85. package/lib/codex-skills.js +51 -1
  86. package/lib/commands/_aliases.js +248 -0
  87. package/lib/commands/_issue.js +780 -77
  88. package/lib/commands/_manifest.js +93 -0
  89. package/lib/commands/_registry.js +99 -34
  90. package/lib/commands/_resolve-command-opts.js +230 -0
  91. package/lib/commands/_serve-security.js +270 -0
  92. package/lib/commands/adapter.js +12 -0
  93. package/lib/commands/add.js +118 -0
  94. package/lib/commands/audit.js +70 -0
  95. package/lib/commands/blocked.js +5 -0
  96. package/lib/commands/board.js +64 -0
  97. package/lib/commands/claim.js +21 -2
  98. package/lib/commands/claims.js +7 -0
  99. package/lib/commands/clean.js +485 -75
  100. package/lib/commands/close.js +2 -2
  101. package/lib/commands/comment.js +5 -0
  102. package/lib/commands/control.js +148 -0
  103. package/lib/commands/create.js +2 -2
  104. package/lib/commands/dev.js +185 -7
  105. package/lib/commands/doc-gate.js +336 -0
  106. package/lib/commands/doctor.js +156 -0
  107. package/lib/commands/explain.js +15 -0
  108. package/lib/commands/export.js +237 -0
  109. package/lib/commands/gate.js +209 -0
  110. package/lib/commands/hooks.js +377 -0
  111. package/lib/commands/inbox.js +118 -0
  112. package/lib/commands/init.js +604 -0
  113. package/lib/commands/insights.js +79 -0
  114. package/lib/commands/issue.js +12 -1
  115. package/lib/commands/issues.js +17 -0
  116. package/lib/commands/lint.js +5 -0
  117. package/lib/commands/list.js +2 -2
  118. package/lib/commands/memory.js +81 -0
  119. package/lib/commands/merge.js +312 -0
  120. package/lib/commands/migrate.js +362 -0
  121. package/lib/commands/new.js +12 -0
  122. package/lib/commands/options.js +241 -0
  123. package/lib/commands/orient.js +13 -0
  124. package/lib/commands/orphans.js +5 -0
  125. package/lib/commands/patch.js +67 -0
  126. package/lib/commands/plan.js +481 -29
  127. package/lib/commands/pr.js +88 -0
  128. package/lib/commands/preflight.js +211 -0
  129. package/lib/commands/prime.js +13 -0
  130. package/lib/commands/push.js +135 -2
  131. package/lib/commands/ready.js +2 -2
  132. package/lib/commands/recall.js +171 -0
  133. package/lib/commands/recap.js +75 -0
  134. package/lib/commands/recommend.js +0 -1
  135. package/lib/commands/release.js +104 -0
  136. package/lib/commands/remember.js +140 -0
  137. package/lib/commands/role.js +99 -0
  138. package/lib/commands/serve.js +581 -0
  139. package/lib/commands/setup.js +900 -971
  140. package/lib/commands/shepherd.js +501 -0
  141. package/lib/commands/ship.js +59 -1
  142. package/lib/commands/show.js +2 -2
  143. package/lib/commands/stage.js +192 -0
  144. package/lib/commands/stale.js +5 -0
  145. package/lib/commands/status.js +158 -21
  146. package/lib/commands/sync.js +34 -46
  147. package/lib/commands/team.js +4 -1
  148. package/lib/commands/test.js +43 -27
  149. package/lib/commands/update.js +2 -2
  150. package/lib/commands/upgrade.js +47 -0
  151. package/lib/commands/validate.js +43 -18
  152. package/lib/commands/worktree.js +362 -99
  153. package/lib/config-writer.js +202 -0
  154. package/lib/control-plane.js +236 -0
  155. package/lib/core/runtime-graph.js +977 -0
  156. package/lib/dep-guard/keyword-ripple.js +2 -2
  157. package/lib/deprecated-sync-cleanup.js +362 -0
  158. package/lib/detect-agent.js +2 -28
  159. package/lib/detect-worktree.js +35 -9
  160. package/lib/doc-gate/declaration.js +177 -0
  161. package/lib/doc-gate/detect.js +289 -0
  162. package/lib/doc-gate/gate.js +375 -0
  163. package/lib/doc-gate/okf-config.js +128 -0
  164. package/lib/doc-gate/okf.js +429 -0
  165. package/lib/docs-command.js +1161 -6
  166. package/lib/forge-issues.js +382 -11
  167. package/lib/forge-lock.js +262 -0
  168. package/lib/gate-events.js +192 -0
  169. package/lib/global-flags.js +104 -0
  170. package/lib/greptile-match.js +7 -63
  171. package/lib/grounding/context-events.js +230 -0
  172. package/lib/grounding/read-first.js +112 -0
  173. package/lib/harness-capability-matrix.js +380 -0
  174. package/lib/hook-global-installer.js +347 -0
  175. package/lib/hook-renderer.js +541 -0
  176. package/lib/inbox.js +391 -0
  177. package/lib/insights.js +397 -0
  178. package/lib/issue-adapter.js +156 -0
  179. package/lib/issue-backend.js +145 -0
  180. package/lib/issue-render.js +220 -0
  181. package/lib/kernel/backing-issue.js +311 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/owned-kernel.js +43 -0
  192. package/lib/kernel/planning-buckets-schema.js +109 -0
  193. package/lib/kernel/projection-jsonl-writer.js +450 -0
  194. package/lib/kernel/readiness-model.js +329 -0
  195. package/lib/kernel/schema.js +356 -0
  196. package/lib/kernel/sqlite-driver.js +2540 -0
  197. package/lib/kernel/taxonomy-validator.js +394 -0
  198. package/lib/lefthook-check.js +3 -2
  199. package/lib/lefthook-wiring.js +413 -0
  200. package/lib/mcp-config-renderer.js +288 -0
  201. package/lib/memory/graphiti-mcp.js +106 -0
  202. package/lib/memory/router.js +387 -0
  203. package/lib/memory/typed-api.js +102 -0
  204. package/lib/memory-digest.js +195 -0
  205. package/lib/merge-rules.js +395 -0
  206. package/lib/migrate-dry-run.js +466 -0
  207. package/lib/orientation.js +863 -0
  208. package/lib/package-manager-remediation.js +103 -0
  209. package/lib/package-root.js +381 -0
  210. package/lib/patch-intent.js +890 -0
  211. package/lib/plugin-catalog.js +3 -4
  212. package/lib/plugin-manager.js +0 -5
  213. package/lib/pr-bundle.js +186 -0
  214. package/lib/pr-monitor/auto-actions.js +175 -0
  215. package/lib/pr-monitor/differ.js +195 -0
  216. package/lib/pr-monitor/digest.js +206 -0
  217. package/lib/pr-monitor/events.js +0 -0
  218. package/lib/pr-monitor/gather.js +124 -0
  219. package/lib/pr-monitor/journal.js +299 -0
  220. package/lib/pr-monitor/monitor.js +146 -0
  221. package/lib/pr-monitor/render-sticky.js +192 -0
  222. package/lib/pr-monitor/upsert-sticky.js +169 -0
  223. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  224. package/lib/pr-monitor/watch.js +247 -0
  225. package/lib/pr-pull.js +1314 -0
  226. package/lib/pr-shepherd.js +494 -0
  227. package/lib/pr-state-validator.js +59 -0
  228. package/lib/preflight/gates.js +237 -0
  229. package/lib/preflight/runner.js +116 -0
  230. package/lib/project-discovery.js +0 -53
  231. package/lib/project-memory.js +99 -497
  232. package/lib/protected-path-manifest.js +281 -0
  233. package/lib/protected-state-surfaces.js +387 -0
  234. package/lib/release-readiness.js +2105 -0
  235. package/lib/reset.js +59 -45
  236. package/lib/review-adapter.js +68 -0
  237. package/lib/rules-sync.js +260 -0
  238. package/lib/runtime-health.js +241 -20
  239. package/lib/safety-config-renderer.js +268 -0
  240. package/lib/setup-action-log.js +1 -7
  241. package/lib/setup.js +27 -65
  242. package/lib/shell-utils.js +76 -6
  243. package/lib/skills-sync.js +330 -0
  244. package/lib/smart-status/scoring.js +17 -3
  245. package/lib/status/beads-snapshot.js +45 -2
  246. package/lib/status/presenter.js +169 -18
  247. package/lib/status/snapshot.js +186 -0
  248. package/lib/sync-backend.js +202 -0
  249. package/lib/untrusted-content.js +52 -0
  250. package/lib/upgrade-safety.js +251 -0
  251. package/lib/workflow/enforce-stage.js +351 -45
  252. package/lib/workflow/stage-transition.js +115 -0
  253. package/lib/workflow/stages.js +30 -6
  254. package/lib/workflow/state-manager.js +11 -22
  255. package/lib/workflow/state.js +23 -1
  256. package/lib/workflow-profiles.js +17 -5
  257. package/package.json +37 -35
  258. package/rules/documentation.md +19 -0
  259. package/rules/kernel-tracking.md +26 -0
  260. package/rules/security.md +22 -0
  261. package/rules/tdd.md +20 -0
  262. package/rules/workflow.md +27 -0
  263. package/scripts/auto-backing-issue.js +47 -0
  264. package/scripts/beads-context.sh +81 -57
  265. package/scripts/beads-upgrade-smoke.sh +24 -3
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard.sh +22 -3
  272. package/scripts/file-index.sh +3 -0
  273. package/scripts/forge-team/lib/claim.sh +34 -18
  274. package/scripts/forge-team/lib/dashboard.sh +61 -86
  275. package/scripts/forge-team/lib/epic.sh +99 -263
  276. package/scripts/forge-team/lib/hooks.sh +26 -28
  277. package/scripts/forge-team/lib/identity.sh +4 -4
  278. package/scripts/forge-team/lib/sync-github.sh +49 -84
  279. package/scripts/forge-team/lib/verify.sh +93 -83
  280. package/scripts/forge-team/lib/workload.sh +41 -65
  281. package/scripts/forge-team/tests/claim.test.sh +25 -19
  282. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  283. package/scripts/forge-team/tests/epic.test.sh +52 -71
  284. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  285. package/scripts/forge-team/tests/identity.test.sh +3 -3
  286. package/scripts/forge-team/tests/integration.test.sh +44 -66
  287. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  288. package/scripts/forge-team/tests/verify.test.sh +37 -46
  289. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  290. package/scripts/forge-team/tests/workload.test.sh +32 -66
  291. package/scripts/gen-command-manifest.js +153 -0
  292. package/scripts/gen-embedded-assets.mjs +129 -0
  293. package/scripts/install.ps1 +139 -0
  294. package/scripts/install.sh +268 -0
  295. package/scripts/lib/release-asset.mjs +84 -0
  296. package/scripts/parity-check.mjs +145 -0
  297. package/scripts/parity-check.test.mjs +58 -0
  298. package/scripts/pin-agentic-workflow-images.js +112 -0
  299. package/scripts/pr-auto-actions.js +93 -0
  300. package/scripts/pr-coordinator.sh +3 -0
  301. package/scripts/pr-verdict-label.js +50 -0
  302. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  303. package/scripts/preflight.sh +21 -94
  304. package/scripts/protected-state-check.js +104 -0
  305. package/scripts/smart-status.sh +60 -57
  306. package/scripts/spikes/config-race-bench.js +111 -0
  307. package/scripts/spikes/harness-capability-matrix.js +13 -0
  308. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  309. package/scripts/spikes/protected-path-manifest.js +20 -0
  310. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  311. package/scripts/sync-agent-skills.js +62 -0
  312. package/scripts/sync-utils.sh +3 -0
  313. package/scripts/test-ci-shard.js +13 -6
  314. package/scripts/test.js +95 -12
  315. package/skills/claim-safety/SKILL.md +102 -0
  316. package/skills/claim-safety/evals/evals.json +46 -0
  317. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  318. package/skills/dev/evals/evals.json +50 -0
  319. package/skills/hermes-forge/SKILL.md +185 -0
  320. package/skills/hermes-forge/evals/evals.json +46 -0
  321. package/skills/issue-basics/SKILL.md +111 -0
  322. package/skills/issue-basics/evals/evals.json +46 -0
  323. package/skills/kernel/SKILL.md +166 -0
  324. package/skills/kernel/evals/evals.json +50 -0
  325. package/skills/memory/SKILL.md +102 -0
  326. package/skills/parallel-deep-research/SKILL.md +14 -11
  327. package/skills/parallel-deep-research/evals/evals.json +11 -27
  328. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  329. package/skills/plan/evals/evals.json +42 -0
  330. package/skills/research/SKILL.md +195 -0
  331. package/skills/research/evals/evals.json +42 -0
  332. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  333. package/skills/review/evals/evals.json +42 -0
  334. package/skills/rollback/SKILL.md +110 -0
  335. package/skills/rollback/evals/evals.json +46 -0
  336. package/skills/rollback/references/methods.md +204 -0
  337. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  338. package/skills/shepherd/SKILL.md +66 -0
  339. package/skills/shepherd/evals/evals.json +42 -0
  340. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  341. package/skills/ship/evals/evals.json +42 -0
  342. package/skills/smith/SKILL.md +142 -0
  343. package/skills/smith/evals/evals.json +46 -0
  344. package/skills/smith/references/autonomy-and-gates.md +94 -0
  345. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  346. package/skills/sonarcloud/evals/evals.json +46 -0
  347. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  348. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  349. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  350. package/skills/status/evals/evals.json +50 -0
  351. package/skills/triage-ready/SKILL.md +121 -0
  352. package/skills/triage-ready/evals/evals.json +42 -0
  353. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  354. package/skills/validate/evals/evals.json +42 -0
  355. package/skills/verify/SKILL.md +299 -0
  356. package/skills/verify/evals/evals.json +50 -0
  357. package/.claude/commands/dev.md +0 -345
  358. package/.claude/commands/plan.md +0 -566
  359. package/.claude/commands/premerge.md +0 -186
  360. package/.claude/commands/research.md +0 -42
  361. package/.claude/commands/review.md +0 -451
  362. package/.claude/commands/rollback.md +0 -721
  363. package/.claude/commands/ship.md +0 -213
  364. package/.claude/commands/sonarcloud.md +0 -152
  365. package/.claude/commands/status.md +0 -90
  366. package/.claude/commands/validate.md +0 -288
  367. package/.claude/commands/verify.md +0 -269
  368. package/.claude/rules/workflow.md +0 -121
  369. package/.cline/workflows/dev.md +0 -342
  370. package/.cline/workflows/plan.md +0 -563
  371. package/.cline/workflows/premerge.md +0 -183
  372. package/.cline/workflows/research.md +0 -39
  373. package/.cline/workflows/review.md +0 -448
  374. package/.cline/workflows/rollback.md +0 -718
  375. package/.cline/workflows/ship.md +0 -210
  376. package/.cline/workflows/sonarcloud.md +0 -146
  377. package/.cline/workflows/status.md +0 -87
  378. package/.cline/workflows/validate.md +0 -285
  379. package/.cline/workflows/verify.md +0 -266
  380. package/.codex/config.toml +0 -11
  381. package/.codex/skills/dev/SKILL.md +0 -345
  382. package/.codex/skills/plan/SKILL.md +0 -566
  383. package/.codex/skills/premerge/SKILL.md +0 -186
  384. package/.codex/skills/research/SKILL.md +0 -42
  385. package/.codex/skills/review/SKILL.md +0 -451
  386. package/.codex/skills/rollback/SKILL.md +0 -721
  387. package/.codex/skills/ship/SKILL.md +0 -213
  388. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  389. package/.codex/skills/status/SKILL.md +0 -90
  390. package/.codex/skills/validate/SKILL.md +0 -288
  391. package/.codex/skills/verify/SKILL.md +0 -269
  392. package/.cursor/commands/dev.md +0 -342
  393. package/.cursor/commands/plan.md +0 -563
  394. package/.cursor/commands/premerge.md +0 -183
  395. package/.cursor/commands/research.md +0 -39
  396. package/.cursor/commands/review.md +0 -448
  397. package/.cursor/commands/ship.md +0 -210
  398. package/.cursor/commands/sonarcloud.md +0 -146
  399. package/.cursor/commands/status.md +0 -87
  400. package/.cursor/commands/validate.md +0 -285
  401. package/.cursor/commands/verify.md +0 -266
  402. package/.cursorrules +0 -149
  403. package/.github/prompts/premerge.prompt.md +0 -188
  404. package/.github/prompts/research.prompt.md +0 -44
  405. package/.github/prompts/rollback.prompt.md +0 -723
  406. package/.github/prompts/verify.prompt.md +0 -271
  407. package/.github/workflows/beads-to-github.yml +0 -89
  408. package/.github/workflows/github-to-beads.yml +0 -100
  409. package/.kilocode/workflows/dev.md +0 -346
  410. package/.kilocode/workflows/plan.md +0 -567
  411. package/.kilocode/workflows/premerge.md +0 -187
  412. package/.kilocode/workflows/research.md +0 -43
  413. package/.kilocode/workflows/review.md +0 -452
  414. package/.kilocode/workflows/rollback.md +0 -722
  415. package/.kilocode/workflows/ship.md +0 -214
  416. package/.kilocode/workflows/sonarcloud.md +0 -150
  417. package/.kilocode/workflows/status.md +0 -91
  418. package/.kilocode/workflows/validate.md +0 -289
  419. package/.kilocode/workflows/verify.md +0 -270
  420. package/.opencode/commands/dev.md +0 -345
  421. package/.opencode/commands/plan.md +0 -566
  422. package/.opencode/commands/premerge.md +0 -186
  423. package/.opencode/commands/research.md +0 -42
  424. package/.opencode/commands/review.md +0 -451
  425. package/.opencode/commands/rollback.md +0 -721
  426. package/.opencode/commands/ship.md +0 -213
  427. package/.opencode/commands/sonarcloud.md +0 -149
  428. package/.opencode/commands/status.md +0 -90
  429. package/.opencode/commands/validate.md +0 -288
  430. package/.opencode/commands/verify.md +0 -269
  431. package/.roo/commands/dev.md +0 -346
  432. package/.roo/commands/plan.md +0 -567
  433. package/.roo/commands/premerge.md +0 -187
  434. package/.roo/commands/research.md +0 -43
  435. package/.roo/commands/review.md +0 -452
  436. package/.roo/commands/rollback.md +0 -722
  437. package/.roo/commands/ship.md +0 -214
  438. package/.roo/commands/sonarcloud.md +0 -150
  439. package/.roo/commands/status.md +0 -91
  440. package/.roo/commands/validate.md +0 -289
  441. package/.roo/commands/verify.md +0 -270
  442. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  443. package/docs/GREPTILE_SETUP.md +0 -400
  444. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  445. package/docs/SETUP.md +0 -663
  446. package/docs/VALIDATION.md +0 -363
  447. package/lib/agents/cline.plugin.json +0 -29
  448. package/lib/agents/copilot.plugin.json +0 -24
  449. package/lib/agents/kilocode.plugin.json +0 -22
  450. package/lib/agents/opencode.plugin.json +0 -23
  451. package/lib/agents/roo.plugin.json +0 -30
  452. package/lib/beads-bootstrap.js +0 -225
  453. package/lib/beads-health-check.js +0 -188
  454. package/lib/commands/commands-reset.js +0 -147
  455. package/opencode.json +0 -67
  456. package/scripts/beads-context.test.js +0 -584
  457. package/scripts/github-beads-sync/comment.mjs +0 -64
  458. package/scripts/github-beads-sync/config.mjs +0 -148
  459. package/scripts/github-beads-sync/github-api.mjs +0 -131
  460. package/scripts/github-beads-sync/index.mjs +0 -356
  461. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  462. package/scripts/github-beads-sync/mapping.mjs +0 -132
  463. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  464. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  465. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  466. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  467. package/scripts/github-beads-sync.config.json +0 -26
  468. 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
+ };