forge-workflow 0.0.9 → 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 (479) 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 +151 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -106
  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 +466 -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/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
  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/reference/TOOLCHAIN.md +658 -0
  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 +225 -28
  81. package/lib/beads-sync-scaffold.js +36 -107
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +744 -70
  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 +66 -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 +22 -2
  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 +851 -979
  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 +329 -11
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +15 -2
  142. package/lib/commands/test.js +58 -7
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +56 -25
  146. package/lib/commands/worktree.js +308 -128
  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 +184 -0
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +42 -17
  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 +697 -0
  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/issue-sync/authority.js +100 -0
  174. package/lib/issue-sync/github-pull.js +184 -0
  175. package/lib/issue-sync/import-primitives.js +98 -0
  176. package/lib/issue-sync/legacy-link-bridge.js +436 -0
  177. package/lib/issue-sync/link-store.js +292 -0
  178. package/lib/issue-sync/project-github.js +123 -0
  179. package/lib/issue-sync/reconcile.js +195 -0
  180. package/lib/issue-sync/schema.js +126 -0
  181. package/lib/kernel/backing-issue.js +305 -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/planning-buckets-schema.js +109 -0
  192. package/lib/kernel/projection-jsonl-writer.js +450 -0
  193. package/lib/kernel/readiness-model.js +329 -0
  194. package/lib/kernel/schema.js +356 -0
  195. package/lib/kernel/sqlite-driver.js +2504 -0
  196. package/lib/kernel/taxonomy-validator.js +394 -0
  197. package/lib/lefthook-check.js +8 -4
  198. package/lib/lefthook-wiring.js +413 -0
  199. package/lib/mcp-config-renderer.js +288 -0
  200. package/lib/memory/graphiti-mcp.js +106 -0
  201. package/lib/memory/router.js +387 -0
  202. package/lib/memory/typed-api.js +102 -0
  203. package/lib/memory-digest.js +195 -0
  204. package/lib/merge-rules.js +395 -0
  205. package/lib/migrate-dry-run.js +466 -0
  206. package/lib/orientation.js +863 -0
  207. package/lib/package-manager-remediation.js +103 -0
  208. package/lib/package-root.js +381 -0
  209. package/lib/patch-intent.js +890 -0
  210. package/lib/plugin-catalog.js +3 -4
  211. package/lib/plugin-manager.js +0 -5
  212. package/lib/pr-bundle.js +186 -0
  213. package/lib/pr-monitor/differ.js +195 -0
  214. package/lib/pr-monitor/events.js +0 -0
  215. package/lib/pr-monitor/gather.js +124 -0
  216. package/lib/pr-monitor/journal.js +299 -0
  217. package/lib/pr-monitor/monitor.js +146 -0
  218. package/lib/pr-monitor/render-sticky.js +157 -0
  219. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  220. package/lib/pr-monitor/watch.js +247 -0
  221. package/lib/pr-pull.js +1273 -0
  222. package/lib/pr-shepherd.js +494 -0
  223. package/lib/pr-state-validator.js +59 -0
  224. package/lib/preflight/gates.js +237 -0
  225. package/lib/preflight/runner.js +116 -0
  226. package/lib/project-discovery.js +0 -53
  227. package/lib/project-memory.js +166 -0
  228. package/lib/protected-path-manifest.js +281 -0
  229. package/lib/protected-state-surfaces.js +387 -0
  230. package/lib/release-readiness.js +2089 -0
  231. package/lib/reset.js +59 -45
  232. package/lib/review-adapter.js +68 -0
  233. package/lib/rules-sync.js +260 -0
  234. package/lib/runtime-health.js +332 -23
  235. package/lib/safety-config-renderer.js +268 -0
  236. package/lib/setup-action-log.js +1 -7
  237. package/lib/setup.js +27 -65
  238. package/lib/shell-utils.js +76 -6
  239. package/lib/skills-sync.js +330 -0
  240. package/lib/smart-status/conflicts.js +205 -0
  241. package/lib/smart-status/scoring.js +191 -0
  242. package/lib/status/beads-snapshot.js +145 -0
  243. package/lib/status/presenter.js +216 -0
  244. package/lib/status/snapshot.js +186 -0
  245. package/lib/sync-backend.js +202 -0
  246. package/lib/untrusted-content.js +52 -0
  247. package/lib/upgrade-safety.js +199 -0
  248. package/lib/workflow/enforce-stage.js +298 -47
  249. package/lib/workflow/stage-transition.js +115 -0
  250. package/lib/workflow/stages.js +30 -6
  251. package/lib/workflow/state-manager.js +159 -14
  252. package/lib/workflow/state.js +23 -1
  253. package/lib/workflow-profiles.js +17 -5
  254. package/package.json +46 -36
  255. package/rules/documentation.md +19 -0
  256. package/rules/kernel-tracking.md +26 -0
  257. package/rules/security.md +22 -0
  258. package/rules/tdd.md +20 -0
  259. package/rules/workflow.md +27 -0
  260. package/scripts/auto-backing-issue.js +47 -0
  261. package/scripts/beads-context.sh +165 -22
  262. package/scripts/beads-migrate-to-dolt.sh +7 -0
  263. package/scripts/beads-upgrade-smoke.sh +284 -0
  264. package/scripts/behavioral-judge.sh +115 -11
  265. package/scripts/benchmark.js +349 -63
  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-analyze.js +52 -17
  272. package/scripts/dep-guard-keyword-ripple.js +29 -0
  273. package/scripts/dep-guard-render-review.js +86 -0
  274. package/scripts/dep-guard.sh +64 -232
  275. package/scripts/file-index.sh +3 -0
  276. package/scripts/forge-team/lib/claim.sh +34 -18
  277. package/scripts/forge-team/lib/dashboard.sh +61 -86
  278. package/scripts/forge-team/lib/epic.sh +99 -263
  279. package/scripts/forge-team/lib/hooks.sh +26 -28
  280. package/scripts/forge-team/lib/identity.sh +4 -4
  281. package/scripts/forge-team/lib/sync-github.sh +144 -47
  282. package/scripts/forge-team/lib/verify.sh +93 -83
  283. package/scripts/forge-team/lib/workload.sh +41 -65
  284. package/scripts/forge-team/tests/claim.test.sh +25 -19
  285. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  286. package/scripts/forge-team/tests/epic.test.sh +52 -71
  287. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  288. package/scripts/forge-team/tests/identity.test.sh +3 -3
  289. package/scripts/forge-team/tests/integration.test.sh +44 -66
  290. package/scripts/forge-team/tests/sync-github.test.sh +183 -79
  291. package/scripts/forge-team/tests/verify.test.sh +37 -46
  292. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  293. package/scripts/forge-team/tests/workload.test.sh +32 -66
  294. package/scripts/gen-command-manifest.js +153 -0
  295. package/scripts/gen-embedded-assets.mjs +129 -0
  296. package/scripts/install.ps1 +139 -0
  297. package/scripts/install.sh +268 -0
  298. package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
  299. package/scripts/lib/release-asset.mjs +84 -0
  300. package/scripts/parity-check.mjs +145 -0
  301. package/scripts/parity-check.test.mjs +58 -0
  302. package/scripts/pin-agentic-workflow-images.js +112 -0
  303. package/scripts/pr-coordinator.sh +3 -0
  304. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  305. package/scripts/preflight.sh +108 -0
  306. package/scripts/protected-state-check.js +104 -0
  307. package/scripts/smart-status-score.js +31 -0
  308. package/scripts/smart-status-sessions.js +51 -0
  309. package/scripts/smart-status.sh +117 -369
  310. package/scripts/spikes/config-race-bench.js +111 -0
  311. package/scripts/spikes/harness-capability-matrix.js +13 -0
  312. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  313. package/scripts/spikes/protected-path-manifest.js +20 -0
  314. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  315. package/scripts/sync-agent-skills.js +62 -0
  316. package/scripts/sync-agentic-workflow.js +48 -0
  317. package/scripts/sync-utils.sh +3 -0
  318. package/scripts/test-ci-shard.js +251 -0
  319. package/scripts/test-dashboard.js +188 -52
  320. package/scripts/test-full-suite.js +186 -0
  321. package/scripts/test-profile.js +278 -0
  322. package/scripts/test.js +302 -28
  323. package/scripts/validate.js +143 -0
  324. package/scripts/validate.sh +18 -1
  325. package/skills/claim-safety/SKILL.md +102 -0
  326. package/skills/claim-safety/evals/evals.json +46 -0
  327. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
  328. package/skills/dev/evals/evals.json +50 -0
  329. package/skills/hermes-forge/SKILL.md +185 -0
  330. package/skills/hermes-forge/evals/evals.json +46 -0
  331. package/skills/issue-basics/SKILL.md +111 -0
  332. package/skills/issue-basics/evals/evals.json +46 -0
  333. package/skills/kernel/SKILL.md +166 -0
  334. package/skills/kernel/evals/evals.json +50 -0
  335. package/skills/memory/SKILL.md +102 -0
  336. package/skills/parallel-deep-research/SKILL.md +14 -11
  337. package/skills/parallel-deep-research/evals/evals.json +11 -27
  338. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
  339. package/skills/plan/evals/evals.json +42 -0
  340. package/skills/research/SKILL.md +195 -0
  341. package/skills/research/evals/evals.json +42 -0
  342. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  343. package/skills/review/evals/evals.json +42 -0
  344. package/skills/rollback/SKILL.md +110 -0
  345. package/skills/rollback/evals/evals.json +46 -0
  346. package/skills/rollback/references/methods.md +204 -0
  347. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  348. package/skills/shepherd/SKILL.md +66 -0
  349. package/skills/shepherd/evals/evals.json +42 -0
  350. package/skills/ship/SKILL.md +251 -0
  351. package/skills/ship/evals/evals.json +42 -0
  352. package/skills/smith/SKILL.md +142 -0
  353. package/skills/smith/evals/evals.json +46 -0
  354. package/skills/smith/references/autonomy-and-gates.md +94 -0
  355. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  356. package/skills/sonarcloud/evals/evals.json +46 -0
  357. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  358. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  359. package/skills/status/SKILL.md +102 -0
  360. package/skills/status/evals/evals.json +50 -0
  361. package/skills/triage-ready/SKILL.md +121 -0
  362. package/skills/triage-ready/evals/evals.json +42 -0
  363. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  364. package/skills/validate/evals/evals.json +42 -0
  365. package/skills/verify/SKILL.md +299 -0
  366. package/skills/verify/evals/evals.json +50 -0
  367. package/.claude/commands/dev.md +0 -345
  368. package/.claude/commands/plan.md +0 -566
  369. package/.claude/commands/premerge.md +0 -186
  370. package/.claude/commands/research.md +0 -42
  371. package/.claude/commands/review.md +0 -451
  372. package/.claude/commands/rollback.md +0 -721
  373. package/.claude/commands/ship.md +0 -213
  374. package/.claude/commands/sonarcloud.md +0 -152
  375. package/.claude/commands/status.md +0 -90
  376. package/.claude/commands/validate.md +0 -288
  377. package/.claude/commands/verify.md +0 -269
  378. package/.claude/rules/workflow.md +0 -121
  379. package/.cline/workflows/dev.md +0 -342
  380. package/.cline/workflows/plan.md +0 -563
  381. package/.cline/workflows/premerge.md +0 -183
  382. package/.cline/workflows/research.md +0 -39
  383. package/.cline/workflows/review.md +0 -448
  384. package/.cline/workflows/rollback.md +0 -718
  385. package/.cline/workflows/ship.md +0 -210
  386. package/.cline/workflows/sonarcloud.md +0 -146
  387. package/.cline/workflows/status.md +0 -87
  388. package/.cline/workflows/validate.md +0 -285
  389. package/.cline/workflows/verify.md +0 -266
  390. package/.codex/config.toml +0 -11
  391. package/.codex/skills/dev/SKILL.md +0 -345
  392. package/.codex/skills/plan/SKILL.md +0 -566
  393. package/.codex/skills/premerge/SKILL.md +0 -186
  394. package/.codex/skills/research/SKILL.md +0 -42
  395. package/.codex/skills/review/SKILL.md +0 -451
  396. package/.codex/skills/rollback/SKILL.md +0 -721
  397. package/.codex/skills/ship/SKILL.md +0 -213
  398. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  399. package/.codex/skills/status/SKILL.md +0 -90
  400. package/.codex/skills/validate/SKILL.md +0 -288
  401. package/.codex/skills/verify/SKILL.md +0 -269
  402. package/.cursor/commands/dev.md +0 -342
  403. package/.cursor/commands/plan.md +0 -563
  404. package/.cursor/commands/premerge.md +0 -183
  405. package/.cursor/commands/research.md +0 -39
  406. package/.cursor/commands/review.md +0 -448
  407. package/.cursor/commands/ship.md +0 -210
  408. package/.cursor/commands/sonarcloud.md +0 -146
  409. package/.cursor/commands/status.md +0 -87
  410. package/.cursor/commands/validate.md +0 -285
  411. package/.cursor/commands/verify.md +0 -266
  412. package/.cursorrules +0 -149
  413. package/.github/prompts/premerge.prompt.md +0 -188
  414. package/.github/prompts/research.prompt.md +0 -44
  415. package/.github/prompts/rollback.prompt.md +0 -723
  416. package/.github/prompts/ship.prompt.md +0 -215
  417. package/.github/prompts/status.prompt.md +0 -92
  418. package/.github/prompts/verify.prompt.md +0 -271
  419. package/.github/workflows/beads-to-github.yml +0 -56
  420. package/.github/workflows/github-to-beads.yml +0 -97
  421. package/.kilocode/workflows/dev.md +0 -346
  422. package/.kilocode/workflows/plan.md +0 -567
  423. package/.kilocode/workflows/premerge.md +0 -187
  424. package/.kilocode/workflows/research.md +0 -43
  425. package/.kilocode/workflows/review.md +0 -452
  426. package/.kilocode/workflows/rollback.md +0 -722
  427. package/.kilocode/workflows/ship.md +0 -214
  428. package/.kilocode/workflows/sonarcloud.md +0 -150
  429. package/.kilocode/workflows/status.md +0 -91
  430. package/.kilocode/workflows/validate.md +0 -289
  431. package/.kilocode/workflows/verify.md +0 -270
  432. package/.opencode/commands/dev.md +0 -345
  433. package/.opencode/commands/plan.md +0 -566
  434. package/.opencode/commands/premerge.md +0 -186
  435. package/.opencode/commands/research.md +0 -42
  436. package/.opencode/commands/review.md +0 -451
  437. package/.opencode/commands/rollback.md +0 -721
  438. package/.opencode/commands/ship.md +0 -213
  439. package/.opencode/commands/sonarcloud.md +0 -149
  440. package/.opencode/commands/status.md +0 -90
  441. package/.opencode/commands/validate.md +0 -288
  442. package/.opencode/commands/verify.md +0 -269
  443. package/.roo/commands/dev.md +0 -346
  444. package/.roo/commands/plan.md +0 -567
  445. package/.roo/commands/premerge.md +0 -187
  446. package/.roo/commands/research.md +0 -43
  447. package/.roo/commands/review.md +0 -452
  448. package/.roo/commands/rollback.md +0 -722
  449. package/.roo/commands/ship.md +0 -214
  450. package/.roo/commands/sonarcloud.md +0 -150
  451. package/.roo/commands/status.md +0 -91
  452. package/.roo/commands/validate.md +0 -289
  453. package/.roo/commands/verify.md +0 -270
  454. package/docs/BEADS_GITHUB_SYNC.md +0 -255
  455. package/docs/GREPTILE_SETUP.md +0 -400
  456. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  457. package/docs/SETUP.md +0 -663
  458. package/docs/VALIDATION.md +0 -363
  459. package/lib/agents/cline.plugin.json +0 -29
  460. package/lib/agents/copilot.plugin.json +0 -24
  461. package/lib/agents/kilocode.plugin.json +0 -22
  462. package/lib/agents/opencode.plugin.json +0 -23
  463. package/lib/agents/roo.plugin.json +0 -30
  464. package/lib/beads-health-check.js +0 -143
  465. package/lib/commands/commands-reset.js +0 -147
  466. package/opencode.json +0 -67
  467. package/scripts/beads-context.test.js +0 -567
  468. package/scripts/github-beads-sync/comment.mjs +0 -64
  469. package/scripts/github-beads-sync/config.mjs +0 -148
  470. package/scripts/github-beads-sync/github-api.mjs +0 -131
  471. package/scripts/github-beads-sync/index.mjs +0 -332
  472. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  473. package/scripts/github-beads-sync/mapping.mjs +0 -78
  474. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  475. package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
  476. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  477. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  478. package/scripts/github-beads-sync.config.json +0 -26
  479. 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');