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,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');