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,270 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Shared-machine hardening for `forge serve` — three cheap "ALWAYS" controls.
5
+ *
6
+ * `forge serve` binds loopback only and token-gates every read/write, but on a
7
+ * multi-user box the process still lives in a shared filesystem. These controls
8
+ * raise the floor without adding accounts, a daemon, or cloud:
9
+ *
10
+ * 1. serve.lock — a single-instance guard so a second `forge serve` for the
11
+ * SAME project can't silently start and port-squat. Stale locks (dead PID)
12
+ * are reclaimed; a live holder blocks. Released cleanly on exit.
13
+ * 2. securePath() — create the lock/journal and their directory owner-only
14
+ * (files 0o600, dirs 0o700) AT CREATION, plus a startup audit that warns
15
+ * loudly if a sensitive path is group/other-readable.
16
+ * 3. A hash-chained mutation journal — each appended record carries
17
+ * sha256(prevHash ‖ record), so an attacker cannot silently edit or delete
18
+ * a past entry without breaking the chain. verifyJournal() proves it.
19
+ *
20
+ * Windows honesty: chmod on Windows only toggles the read-only bit — it does NOT
21
+ * change NTFS ACLs — and `fs.stat().mode` does not reflect ACLs. So on Windows
22
+ * securePath() is a best-effort no-op (reported as `applied:false`) and the
23
+ * audit refuses to raise false alarms from meaningless mode bits (it flags the
24
+ * platform caveat instead). The lock + hash-chain logic is platform-independent.
25
+ *
26
+ * State lives under `.forge/serve/` (owner-only) so we never re-chmod the shared
27
+ * `.forge/` config directory out from under other tooling.
28
+ */
29
+
30
+ const fs = require('node:fs');
31
+ const path = require('node:path');
32
+ const crypto = require('node:crypto');
33
+
34
+ const SECRET_FILE_MODE = 0o600;
35
+ const SECRET_DIR_MODE = 0o700;
36
+ const GENESIS_HASH = '0'.repeat(64);
37
+ const IS_WINDOWS = process.platform === 'win32';
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Paths
41
+ // ---------------------------------------------------------------------------
42
+
43
+ function serveStateDir(projectRoot) {
44
+ return path.join(projectRoot, '.forge', 'serve');
45
+ }
46
+ function lockPath(projectRoot) {
47
+ return path.join(serveStateDir(projectRoot), 'serve.lock');
48
+ }
49
+ function journalPath(projectRoot) {
50
+ return path.join(serveStateDir(projectRoot), 'journal.jsonl');
51
+ }
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // 2. Perms — securePath / writeSecret / ensureSecureDir + startup audit
55
+ // ---------------------------------------------------------------------------
56
+
57
+ // Tighten an existing path to owner-only. POSIX: real chmod. Windows: chmod only
58
+ // flips the read-only bit (ACLs untouched), so we report applied:false — the
59
+ // caller and the security doc treat Windows perms as best-effort.
60
+ function securePath(target, { dir = false } = {}) {
61
+ const mode = dir ? SECRET_DIR_MODE : SECRET_FILE_MODE;
62
+ try {
63
+ fs.chmodSync(target, mode);
64
+ return { ok: true, applied: !IS_WINDOWS, mode };
65
+ } catch (err) {
66
+ return { ok: false, applied: false, error: err.message };
67
+ }
68
+ }
69
+
70
+ // Create/overwrite a file with owner-only perms AT CREATION. The `mode` option
71
+ // governs the perms for a NEW file; an existing file keeps its perms on write,
72
+ // so we chmod afterwards to be certain it is tight either way.
73
+ function writeSecret(target, data) {
74
+ ensureSecureDir(path.dirname(target));
75
+ fs.writeFileSync(target, data, { mode: SECRET_FILE_MODE });
76
+ securePath(target);
77
+ }
78
+
79
+ // Create a directory owner-only (recursive). Tighten it afterwards in case it
80
+ // already existed with looser perms that we own.
81
+ function ensureSecureDir(dir) {
82
+ fs.mkdirSync(dir, { recursive: true, mode: SECRET_DIR_MODE });
83
+ securePath(dir, { dir: true });
84
+ }
85
+
86
+ // Inspect one path: does it exist, and is it group/other-readable? On Windows
87
+ // the mode bits are meaningless for ACLs, so we never mark it world-readable —
88
+ // we surface `platformCaveat:true` so the caller can print an honest note.
89
+ function auditPath(target) {
90
+ let st;
91
+ try {
92
+ st = fs.statSync(target);
93
+ } catch {
94
+ return { path: target, exists: false, worldReadable: false, platformCaveat: IS_WINDOWS };
95
+ }
96
+ const mode = st.mode & 0o777;
97
+ const worldReadable = !IS_WINDOWS && (mode & 0o077) !== 0;
98
+ return { path: target, exists: true, mode, worldReadable, platformCaveat: IS_WINDOWS };
99
+ }
100
+
101
+ // Audit a set of sensitive paths. `ok:false` when any existing path is
102
+ // group/other-readable (POSIX). Callers warn loudly on the offenders.
103
+ function auditPaths(targets) {
104
+ const results = targets.map(auditPath);
105
+ const offenders = results.filter((r) => r.exists && r.worldReadable);
106
+ return { ok: offenders.length === 0, offenders, results };
107
+ }
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // 1. serve.lock — single-instance guard
111
+ // ---------------------------------------------------------------------------
112
+
113
+ function readLock(file) {
114
+ try {
115
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
116
+ } catch {
117
+ return null;
118
+ }
119
+ }
120
+
121
+ // Is `pid` a live process? `process.kill(pid, 0)` sends no signal but throws
122
+ // ESRCH when the pid is gone. EPERM means it exists but isn't ours — still live.
123
+ function pidAlive(pid) {
124
+ if (!Number.isInteger(pid) || pid <= 0) return false;
125
+ try {
126
+ process.kill(pid, 0);
127
+ return true;
128
+ } catch (err) {
129
+ return err.code === 'EPERM';
130
+ }
131
+ }
132
+
133
+ // Try to claim the single-instance lock for this project.
134
+ // -> { ok:true, file } first claim
135
+ // -> { ok:true, file, reclaimed } stale lock (dead holder) reclaimed
136
+ // -> { ok:false, held } a LIVE holder already owns it (blocked)
137
+ // `pid` / `isAlive` are injectable for testing; production uses the real pid and
138
+ // a real liveness probe.
139
+ function acquireLock(projectRoot, { pid = process.pid, port = null, isAlive = pidAlive } = {}) {
140
+ const file = lockPath(projectRoot);
141
+ ensureSecureDir(path.dirname(file));
142
+ const payload = JSON.stringify({ pid, port, startedAt: new Date().toISOString() });
143
+
144
+ try {
145
+ // Exclusive create: EEXIST if a lock is already present.
146
+ const fd = fs.openSync(file, 'wx', SECRET_FILE_MODE);
147
+ try {
148
+ fs.writeSync(fd, payload);
149
+ } finally {
150
+ fs.closeSync(fd);
151
+ }
152
+ securePath(file);
153
+ return { ok: true, file };
154
+ } catch (err) {
155
+ if (err.code !== 'EEXIST') throw err;
156
+ }
157
+
158
+ // A lock exists. If a DIFFERENT process still holds it and is alive, block.
159
+ const held = readLock(file);
160
+ if (held && held.pid !== pid && isAlive(held.pid)) {
161
+ return { ok: false, held };
162
+ }
163
+ // Otherwise it is stale (dead holder, unreadable, or already ours) — reclaim.
164
+ // NOTE: reclaim is best-effort and not itself atomic across a simultaneous
165
+ // double-reclaim race; acceptable for a cheap loopback dev-server guard.
166
+ fs.writeFileSync(file, payload, { mode: SECRET_FILE_MODE });
167
+ securePath(file);
168
+ return { ok: true, file, reclaimed: true };
169
+ }
170
+
171
+ // Release ONLY if the lock is ours (matching pid), so a reclaimed/foreign lock
172
+ // is never deleted out from under its owner.
173
+ function releaseLock(projectRoot, { pid = process.pid } = {}) {
174
+ const file = lockPath(projectRoot);
175
+ try {
176
+ const held = readLock(file);
177
+ if (held && held.pid === pid) fs.rmSync(file, { force: true });
178
+ } catch {
179
+ /* best effort — a failed release just leaves a stale lock to be reclaimed */
180
+ }
181
+ }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // 3. Hash-chained tamper-evident journal
185
+ // ---------------------------------------------------------------------------
186
+
187
+ // hash = sha256(prevHash ‖ JSON(record-without-hash)). The prevHash is embedded
188
+ // in the record too, so both a payload edit and a record deletion break verify.
189
+ function recordHash(prevHash, record) {
190
+ return crypto.createHash('sha256').update(`${prevHash}\n${JSON.stringify(record)}`).digest('hex');
191
+ }
192
+
193
+ function readLines(file) {
194
+ try {
195
+ return fs.readFileSync(file, 'utf8').split('\n').filter(Boolean);
196
+ } catch {
197
+ return null;
198
+ }
199
+ }
200
+
201
+ function lastHash(lines) {
202
+ if (!lines || !lines.length) return GENESIS_HASH;
203
+ try {
204
+ return JSON.parse(lines[lines.length - 1]).hash || GENESIS_HASH;
205
+ } catch {
206
+ return GENESIS_HASH;
207
+ }
208
+ }
209
+
210
+ // Append a tamper-evident record. `entry` is arbitrary JSON (e.g. { verb, ok,
211
+ // actor, origin }). Returns { seq, hash }. Best-effort perms on first create.
212
+ function appendJournal(projectRoot, entry) {
213
+ const file = journalPath(projectRoot);
214
+ ensureSecureDir(path.dirname(file));
215
+ const existed = fs.existsSync(file);
216
+ const lines = existed ? readLines(file) : [];
217
+ const prevHash = lastHash(lines);
218
+ const seq = lines ? lines.length : 0;
219
+ // Key order is fixed (seq, ts, prevHash, ...entry) so the stored record,
220
+ // minus its hash, re-serializes identically at verify time.
221
+ const record = { seq, ts: new Date().toISOString(), prevHash, ...entry };
222
+ const hash = recordHash(prevHash, record);
223
+ fs.appendFileSync(file, `${JSON.stringify({ ...record, hash })}\n`, { mode: SECRET_FILE_MODE });
224
+ if (!existed) securePath(file);
225
+ return { seq, hash };
226
+ }
227
+
228
+ // Re-walk the chain from genesis. Returns { ok:true, entries } or
229
+ // { ok:false, brokenAt, reason } at the first record that fails to verify.
230
+ function verifyJournal(projectRoot) {
231
+ const file = journalPath(projectRoot);
232
+ const lines = readLines(file);
233
+ if (lines === null) return { ok: true, entries: 0, empty: true };
234
+ let prevHash = GENESIS_HASH;
235
+ for (let i = 0; i < lines.length; i += 1) {
236
+ let parsed;
237
+ try {
238
+ parsed = JSON.parse(lines[i]);
239
+ } catch {
240
+ return { ok: false, brokenAt: i, reason: 'unparseable record' };
241
+ }
242
+ const { hash, ...rest } = parsed;
243
+ if (rest.prevHash !== prevHash) {
244
+ return { ok: false, brokenAt: i, reason: 'prevHash mismatch (record inserted or deleted)' };
245
+ }
246
+ if (recordHash(prevHash, rest) !== hash) {
247
+ return { ok: false, brokenAt: i, reason: 'hash mismatch (record edited)' };
248
+ }
249
+ prevHash = hash;
250
+ }
251
+ return { ok: true, entries: lines.length, head: prevHash };
252
+ }
253
+
254
+ module.exports = {
255
+ serveStateDir,
256
+ lockPath,
257
+ journalPath,
258
+ securePath,
259
+ writeSecret,
260
+ ensureSecureDir,
261
+ auditPath,
262
+ auditPaths,
263
+ acquireLock,
264
+ releaseLock,
265
+ readLock,
266
+ pidAlive,
267
+ appendJournal,
268
+ verifyJournal,
269
+ GENESIS_HASH,
270
+ };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const { handleAdapterCommand } = require('../adapter-cli');
4
+
5
+ module.exports = {
6
+ name: 'adapter',
7
+ description: 'Manage and test review adapters',
8
+ usage: 'forge adapter <test|list|enable|disable> ...',
9
+ async handler(args, _flags, projectRoot) {
10
+ return handleAdapterCommand(args, projectRoot);
11
+ },
12
+ };
@@ -0,0 +1,118 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { addLockEntry } = require('../forge-lock');
5
+
6
+ function hasArg(args, name) {
7
+ return Array.isArray(args) && args.includes(name);
8
+ }
9
+
10
+ function argValue(args, name) {
11
+ const index = args.indexOf(name);
12
+ if (index >= 0 && index + 1 < args.length && !args[index + 1].startsWith('-')) {
13
+ return args[index + 1];
14
+ }
15
+ const prefix = `${name}=`;
16
+ const match = args.find(arg => arg.startsWith(prefix));
17
+ return match ? match.slice(prefix.length) : null;
18
+ }
19
+
20
+ function positionalArgs(args) {
21
+ const positionals = [];
22
+ const flagsWithValues = new Set(['--name', '--path', '-p']);
23
+ for (let index = 0; index < args.length; index++) {
24
+ const arg = args[index];
25
+ if (arg.startsWith('--') && arg.includes('=')) {
26
+ continue;
27
+ }
28
+ if (arg.startsWith('-')) {
29
+ if (flagsWithValues.has(arg) && index + 1 < args.length && !args[index + 1].startsWith('-')) {
30
+ index++;
31
+ }
32
+ continue;
33
+ }
34
+ positionals.push(arg);
35
+ }
36
+ return positionals;
37
+ }
38
+
39
+ function stripKnownExtension(base) {
40
+ const lowerBase = base.toLowerCase();
41
+ for (const suffix of ['.plugin.json', '.tar.gz', '.json', '.tgz']) {
42
+ if (lowerBase.endsWith(suffix)) {
43
+ return base.slice(0, base.length - suffix.length);
44
+ }
45
+ }
46
+ return base;
47
+ }
48
+
49
+ function normalizeNameSegment(value) {
50
+ let normalized = '';
51
+ let previousWasSeparator = true;
52
+ for (const character of value) {
53
+ const isAllowed = (
54
+ (character >= 'A' && character <= 'Z')
55
+ || (character >= 'a' && character <= 'z')
56
+ || (character >= '0' && character <= '9')
57
+ || character === '.'
58
+ || character === '_'
59
+ || character === '-'
60
+ );
61
+ if (isAllowed) {
62
+ normalized += character;
63
+ previousWasSeparator = false;
64
+ } else if (!previousWasSeparator) {
65
+ normalized += '-';
66
+ previousWasSeparator = true;
67
+ }
68
+ }
69
+ return normalized.endsWith('-') ? normalized.slice(0, -1) : normalized;
70
+ }
71
+
72
+ function deriveName(source) {
73
+ const sourceText = String(source || '');
74
+ const base = path.basename(sourceText.endsWith('/') ? sourceText.slice(0, -1) : sourceText);
75
+ return normalizeNameSegment(stripKnownExtension(base)) || 'extension';
76
+ }
77
+
78
+ function usage() {
79
+ return 'Usage: forge add <source> [--name <id>] [--allow-untrusted]';
80
+ }
81
+
82
+ async function handler(args, _flags, projectRoot) {
83
+ const [source] = positionalArgs(args);
84
+ if (!source) {
85
+ return { success: false, error: usage() };
86
+ }
87
+
88
+ try {
89
+ const result = addLockEntry(projectRoot, {
90
+ name: argValue(args, '--name') || deriveName(source),
91
+ source,
92
+ allowUntrusted: hasArg(args, '--allow-untrusted'),
93
+ });
94
+ const trustNote = result.entry.trust.trusted
95
+ ? 'trusted source verified'
96
+ : 'untrusted source accepted by explicit policy';
97
+ return {
98
+ success: true,
99
+ output: `Added ${result.entry.name} to forge.lock (${trustNote}).`,
100
+ };
101
+ } catch (error) {
102
+ const message = error.message.includes('Untrusted source')
103
+ ? `${error.message}\nUse --allow-untrusted only for sources you intentionally trust for this project.`
104
+ : error.message;
105
+ return { success: false, error: message };
106
+ }
107
+ }
108
+
109
+ module.exports = {
110
+ name: 'add',
111
+ description: 'Record an extension source in forge.lock',
112
+ usage: usage(),
113
+ flags: {
114
+ '--name': 'Lockfile extension id to record',
115
+ '--allow-untrusted': 'Explicitly record an untrusted remote/package source',
116
+ },
117
+ handler,
118
+ };
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ const { verifyForgeLock } = require('../forge-lock');
4
+
5
+ const usage = 'Usage: forge audit verify';
6
+
7
+ function positionalArgs(args) {
8
+ const positionals = [];
9
+ const flagsWithValues = new Set(['--path', '-p']);
10
+ for (let index = 0; index < args.length; index++) {
11
+ const arg = args[index];
12
+ if (arg.startsWith('--') && arg.includes('=')) {
13
+ continue;
14
+ }
15
+ if (arg.startsWith('-')) {
16
+ if (flagsWithValues.has(arg) && index + 1 < args.length && !args[index + 1].startsWith('-')) {
17
+ index++;
18
+ }
19
+ continue;
20
+ }
21
+ positionals.push(arg);
22
+ }
23
+ return positionals;
24
+ }
25
+
26
+ function renderVerifyReport(report) {
27
+ const lines = [
28
+ 'Forge lock audit verify',
29
+ `Result: ${report.ok ? 'PASS' : 'FAIL'}`,
30
+ ];
31
+
32
+ if (report.results.length === 0) {
33
+ lines.push('[PASS] forge.lock: no extensions recorded');
34
+ } else {
35
+ for (const result of report.results) {
36
+ lines.push(`[${result.status.toUpperCase()}] ${result.name}: ${result.reason}`);
37
+ }
38
+ }
39
+
40
+ return `${lines.join('\n')}\n`;
41
+ }
42
+
43
+ async function handler(args, _flags, projectRoot) {
44
+ const [subcommand] = positionalArgs(args);
45
+ if (subcommand !== 'verify') {
46
+ return { success: false, error: usage };
47
+ }
48
+ try {
49
+ const report = verifyForgeLock(projectRoot);
50
+ const output = renderVerifyReport(report);
51
+ return {
52
+ success: report.ok,
53
+ output,
54
+ error: report.ok ? undefined : output,
55
+ };
56
+ } catch (error) {
57
+ return {
58
+ success: false,
59
+ error: error.message || String(error),
60
+ };
61
+ }
62
+ }
63
+
64
+ module.exports = {
65
+ name: 'audit',
66
+ description: 'Verify Forge lockfile and audit state',
67
+ usage,
68
+ handler,
69
+ renderVerifyReport,
70
+ };
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ const { createIssueSubcommand } = require('./_issue');
4
+
5
+ module.exports = createIssueSubcommand('blocked');
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ const { readBeadsSnapshot } = require('../status/beads-snapshot');
4
+ const { buildBoardJson, formatBoard } = require('../status/presenter');
5
+ const { detectRepoContext } = require('./status');
6
+
7
+ function hasJsonFlag(args = [], flags = {}) {
8
+ return flags.json === true || flags['--json'] === true || args.includes('--json');
9
+ }
10
+
11
+ function getFlagValue(args, dashedName, camelName) {
12
+ const inlineDashed = args.find(arg => typeof arg === 'string' && arg.startsWith(`${dashedName}=`));
13
+ if (inlineDashed) {
14
+ return inlineDashed.slice(dashedName.length + 1);
15
+ }
16
+ const inlineCamel = args.find(arg => typeof arg === 'string' && arg.startsWith(`${camelName}=`));
17
+ if (inlineCamel) {
18
+ return inlineCamel.slice(camelName.length + 1);
19
+ }
20
+ const dashedIndex = args.indexOf(dashedName);
21
+ if (dashedIndex !== -1 && dashedIndex + 1 < args.length) {
22
+ const candidate = args[dashedIndex + 1];
23
+ return typeof candidate === 'string' && candidate.startsWith('--') ? null : candidate;
24
+ }
25
+ const camelIndex = args.indexOf(camelName);
26
+ if (camelIndex !== -1 && camelIndex + 1 < args.length) {
27
+ const candidate = args[camelIndex + 1];
28
+ return typeof candidate === 'string' && candidate.startsWith('--') ? null : candidate;
29
+ }
30
+ return null;
31
+ }
32
+
33
+ async function handler(args = [], flags = {}, projectRoot = process.cwd()) {
34
+ const firstDefined = (...values) => values.find(value => value !== undefined && value !== null);
35
+ const context = detectRepoContext(projectRoot);
36
+ const snapshot = readBeadsSnapshot(projectRoot, {
37
+ now: firstDefined(flags.now, flags['--now'], getFlagValue(args, '--now', '--now')),
38
+ staleAfterDays: firstDefined(
39
+ flags.staleAfterDays,
40
+ flags['--stale-after-days'],
41
+ flags['--staleAfterDays'],
42
+ getFlagValue(args, '--stale-after-days', '--staleAfterDays')
43
+ ),
44
+ });
45
+ const payload = { context, snapshot };
46
+ const output = hasJsonFlag(args, flags)
47
+ ? JSON.stringify(buildBoardJson(payload), null, 2)
48
+ : formatBoard(payload);
49
+
50
+ return {
51
+ success: true,
52
+ context,
53
+ snapshot,
54
+ output,
55
+ };
56
+ }
57
+
58
+ module.exports = {
59
+ name: 'board',
60
+ description: 'Show a local team runtime board from kernel state',
61
+ handler,
62
+ getFlagValue,
63
+ hasJsonFlag,
64
+ };
@@ -1,5 +1,24 @@
1
1
  'use strict';
2
2
 
3
- const { makeAliasCommand } = require('./_issue');
3
+ const { runIssueSubcommand } = require('./_issue');
4
4
 
5
- module.exports = makeAliasCommand('claim');
5
+ // `forge claim <id>` claims an issue through the SHARED issue dispatch
6
+ // (runIssueSubcommand): backend resolution (Kernel via --kernel /
7
+ // --issue-backend kernel / FORGE_ISSUE_BACKEND=kernel, Beads otherwise; the
8
+ // Beads backend translates claim to `update <id> --claim`), contract
9
+ // normalization, AND the check-after-write verification loop
10
+ // (gate.issue_verify). This command previously inlined its own copy of the
11
+ // dispatch, which silently BYPASSED the boundary verify — the exact surface
12
+ // where the d71a824b phantom-claim replay lied to a losing agent — so it now
13
+ // delegates like create/update/close/comment do.
14
+ async function handler(args, _flags, projectRoot, opts = {}) {
15
+ return runIssueSubcommand('claim', args, projectRoot, opts);
16
+ }
17
+
18
+ module.exports = {
19
+ name: 'claim',
20
+ description: 'Claim an issue via Forge',
21
+ usage: 'forge claim <id> [flags]',
22
+ flags: {},
23
+ handler,
24
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ const { createIssueSubcommand } = require('./_issue');
4
+
5
+ // `forge claims` — the active-lease/claims read (issue 7dc229d4). A bare passthrough
6
+ // to `forge issue claims`, mirroring the other kernel derived reads (blocked/stale).
7
+ module.exports = createIssueSubcommand('claims');