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,211 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * `forge preflight` — fast (~2-4 min) blast-radius gate.
5
+ *
6
+ * Gives DETERMINISTIC-GATE PARITY with CI so an agent catches what CI catches
7
+ * WITHOUT waiting on the hanging full suite. Runs four gates in order, fast-fail:
8
+ *
9
+ * 1. lint — eslint --max-warnings 0 on changed files (--all → whole tree)
10
+ * 2. drift — skills mirror + registry sync + agentic-workflow structural asserts
11
+ * 3. sonar — eslint-plugin-sonarjs cognitive-complexity=15 (+ parity rules)
12
+ * 4. affected — only the tests mapped from the changed files
13
+ *
14
+ * Exit non-zero on any gate failure. Run this before `forge push`.
15
+ *
16
+ * @module commands/preflight
17
+ */
18
+
19
+ const { execFileSync } = require('node:child_process');
20
+ const { buildGates } = require('../preflight/gates');
21
+ const { runGates } = require('../preflight/runner');
22
+
23
+ /**
24
+ * Strictly resolve a base ref to diff HEAD against. Returns the ref name, or
25
+ * null when no base can be established (fresh repo: no origin/HEAD, no upstream,
26
+ * no main/master ref). Unlike test.js's resolver, this never falls back to a
27
+ * default name that doesn't exist — a missing base must be reported, not faked.
28
+ *
29
+ * @param {Function} exec - execFileSync-compatible
30
+ * @returns {string|null}
31
+ */
32
+ /** Run a git command and return trimmed stdout, or null on any failure. */
33
+ function gitTryOut(exec, args) {
34
+ try {
35
+ return String(exec('git', args, { encoding: 'utf8', timeout: 3000, stdio: ['ignore', 'pipe', 'ignore'] }) || '').trim();
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ /** True when `ref` resolves to a real object in this repo. */
42
+ function gitVerifyRef(exec, ref) {
43
+ try {
44
+ exec('git', ['rev-parse', '--verify', '--quiet', ref], { stdio: 'ignore', timeout: 3000 });
45
+ return true;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * True when `ref` resolves to the SAME commit as HEAD. Diffing HEAD against such
53
+ * a ref yields zero files, so it is never a usable integration base.
54
+ */
55
+ function refEqualsHead(exec, ref) {
56
+ const refSha = gitTryOut(exec, ['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]);
57
+ if (!refSha) return false;
58
+ const headSha = gitTryOut(exec, ['rev-parse', '--verify', '--quiet', 'HEAD^{commit}']);
59
+ return !!headSha && refSha === headSha;
60
+ }
61
+
62
+ function resolveBaseRef(exec) {
63
+ const originHead = gitTryOut(exec, ['rev-parse', '--abbrev-ref', 'origin/HEAD']);
64
+ if (originHead && originHead !== 'origin/HEAD') return originHead;
65
+
66
+ // `@{upstream}` is the CURRENT branch's OWN remote-tracking ref. On a fully
67
+ // pushed feature branch it points AT HEAD, so diffing against it produces 0
68
+ // changed files and every gate passes vacuously (fail-OPEN). Accept it as a
69
+ // base only when it is a real integration point ahead of us — never when it
70
+ // resolves to our own HEAD. Fall through to a real base (origin/main, …) or
71
+ // FAIL if none exists, rather than manufacture a wrong-but-'resolved' base.
72
+ const upstream = gitTryOut(exec, ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}']);
73
+ if (upstream && !refEqualsHead(exec, upstream)) return upstream;
74
+
75
+ for (const ref of ['origin/main', 'origin/master', 'main', 'master']) {
76
+ if (gitVerifyRef(exec, ref)) return ref;
77
+ }
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * Resolve the change set for preflight, distinguishing "base resolved, genuinely
83
+ * zero changes" from "no base branch could be resolved". Only the latter is a
84
+ * gate failure — a fresh repo with no base cannot have its blast radius computed,
85
+ * so preflight must NOT pass vacuously.
86
+ *
87
+ * @param {Function} [exec] - execFileSync-compatible (injectable for tests)
88
+ * @param {{ runAll?: boolean }} [opts]
89
+ * @returns {{ resolved: boolean, changedFiles: string[], reason?: string, baseRef?: string }}
90
+ */
91
+ function resolveChangeSet(exec = execFileSync, { runAll = false } = {}) {
92
+ if (runAll) {
93
+ return { resolved: true, changedFiles: [], reason: 'whole-tree scope (--all)' };
94
+ }
95
+ const baseRef = resolveBaseRef(exec);
96
+ if (!baseRef) {
97
+ return {
98
+ resolved: false,
99
+ changedFiles: [],
100
+ reason: 'could not resolve a base branch (no origin/HEAD, no upstream, no main/master ref)',
101
+ };
102
+ }
103
+ // Diff against the SAME base we just resolved. getChangedFiles() re-resolves
104
+ // its own diff ref (and can fall back to a different default branch), so use
105
+ // baseRef directly to keep the file list consistent with the resolved base.
106
+ return { resolved: true, baseRef, changedFiles: changedFilesForBase(exec, baseRef) };
107
+ }
108
+
109
+ /** Files changed between the merge-base of HEAD and `baseRef` and HEAD. */
110
+ function changedFilesForBase(exec, baseRef) {
111
+ const mergeBase = gitTryOut(exec, ['merge-base', 'HEAD', baseRef]);
112
+ const range = mergeBase ? `${mergeBase}...HEAD` : `${baseRef}...HEAD`;
113
+ const out = gitTryOut(exec, ['diff', '--name-only', range]);
114
+ if (out == null) return [];
115
+ return out.split(/\r?\n/).filter(Boolean);
116
+ }
117
+
118
+ /**
119
+ * Pick the change set: an injected resolveChangeSet wins; a legacy injected
120
+ * resolveChangedFiles vouches for resolution; otherwise resolve strictly.
121
+ */
122
+ function selectChangeSet(deps, runAll) {
123
+ if (deps.resolveChangeSet) {
124
+ return deps.resolveChangeSet({ runAll });
125
+ }
126
+ if (deps.resolveChangedFiles) {
127
+ return { resolved: true, changedFiles: deps.resolveChangedFiles() || [] };
128
+ }
129
+ return resolveChangeSet(execFileSync, { runAll });
130
+ }
131
+
132
+ /** Print the fail-closed remedy when no base branch could be resolved. */
133
+ function reportBaseUnresolved(log, reason) {
134
+ log('forge preflight — fast blast-radius gate (deterministic parity with CI)');
135
+ log('');
136
+ log(`preflight FAILED — ${reason}.`);
137
+ log(' Remedy: give preflight a base branch to diff against, e.g.');
138
+ log(' git fetch origin && git branch --set-upstream-to=origin/main # your default branch');
139
+ log(' (or ensure origin/HEAD is set: git remote set-head origin -a)');
140
+ log(' Or scan the whole tree explicitly: forge preflight --all');
141
+ }
142
+
143
+ /** Summary mark for one gate result. */
144
+ function gateMark(result) {
145
+ if (result.skipped) return 'SKIP';
146
+ return result.ok ? 'PASS' : 'FAIL';
147
+ }
148
+
149
+ module.exports = {
150
+ name: 'preflight',
151
+ description: 'Fast deterministic-gate parity with CI (lint, drift/registry, sonar, affected tests)',
152
+ usage: 'forge preflight [--all]',
153
+ flags: {
154
+ '--all': 'Lint/scan the whole tree instead of only changed files',
155
+ },
156
+
157
+ /**
158
+ * @param {string[]} _args
159
+ * @param {Object} flags
160
+ * @param {string} projectRoot
161
+ * @param {Object} [deps] - injectable { log, resolveChangedFiles, buildGates, runGates }
162
+ * @returns {Promise<{ success: boolean, results?: Array }>}
163
+ */
164
+ handler: async (_args, flags, projectRoot, deps = {}) => {
165
+ const log = deps.log || console.log;
166
+ const build = deps.buildGates || buildGates;
167
+ const run = deps.runGates || runGates;
168
+
169
+ const runAll = !!(flags && (flags['--all'] || flags.all));
170
+
171
+ // Resolve the change set AND whether a base branch could be established.
172
+ const changeSet = selectChangeSet(deps, runAll) || {};
173
+
174
+ // B2 (N1): an unresolvable base is fail-CLOSED. Computing "0 changed files"
175
+ // from a missing base and passing every gate manufactures false confidence.
176
+ // Anything not EXPLICITLY resolved (including a missing `resolved` field from
177
+ // a future override) fails closed — never fail open on an indeterminate base.
178
+ if (!runAll && !changeSet.resolved) {
179
+ const reason = changeSet.reason || 'base branch could not be resolved';
180
+ reportBaseUnresolved(log, reason);
181
+ return { success: false, results: [], reason, baseUnresolved: true };
182
+ }
183
+
184
+ const changedFiles = changeSet.changedFiles || [];
185
+
186
+ log('forge preflight — fast blast-radius gate (deterministic parity with CI)');
187
+ log(runAll
188
+ ? ' scope: whole tree (--all)'
189
+ : ` scope: ${changedFiles.length} changed file(s) vs base branch`);
190
+ log('');
191
+
192
+ const gates = build({ projectRoot, changedFiles, runAll });
193
+ const { ok, results } = await run(gates, { log: (line) => log(` ${line}`) });
194
+
195
+ log('');
196
+ log('Preflight summary:');
197
+ for (const r of results) {
198
+ const suffix = r.summary ? ` — ${r.summary}` : '';
199
+ log(` [${gateMark(r)}] ${r.name}${suffix}`);
200
+ }
201
+ log('');
202
+ log(ok
203
+ ? 'preflight passed — safe to push'
204
+ : 'preflight FAILED — fix the failing gate above before `forge push`');
205
+
206
+ return { success: ok, results };
207
+ },
208
+ };
209
+
210
+ module.exports.resolveChangeSet = resolveChangeSet;
211
+ module.exports.resolveBaseRef = resolveBaseRef;
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ buildPrime,
5
+ runOrientationCommand,
6
+ } = require('../orientation');
7
+
8
+ module.exports = {
9
+ name: 'prime',
10
+ description: 'Emit session-entry bounded orientation for agents',
11
+ usage: 'Usage: forge prime [--budget N] [--json]',
12
+ handler: (args, _flags, projectRoot) => runOrientationCommand(buildPrime, args, projectRoot),
13
+ };
@@ -54,6 +54,62 @@ function isFirstPush(branch, execFn) {
54
54
  }
55
55
  }
56
56
 
57
+ /**
58
+ * Resolve the git worktree top-level directory.
59
+ *
60
+ * The pre-push hook checks for the nonce token relative to its own cwd, which
61
+ * git sets to the worktree root during `git push`. The handler's `projectRoot`
62
+ * is derived from INIT_CWD/cwd and can point at the MAIN repo root when forge
63
+ * runs from a linked worktree, so the token must be written to the real git
64
+ * top-level instead — otherwise lefthook never finds it and re-runs the suite.
65
+ *
66
+ * @param {function} execFn - execFileSync or mock
67
+ * @param {string} fallback - projectRoot to use when git resolution fails
68
+ * @returns {string} Absolute worktree top-level path, or the fallback
69
+ */
70
+ function resolveGitToplevel(execFn, fallback) {
71
+ try {
72
+ const top = execFn('git', ['rev-parse', '--show-toplevel'], { encoding: 'utf8' }).trim();
73
+ return top || fallback;
74
+ } catch (_err) { /* intentional: fall back to projectRoot outside a git worktree */ // NOSONAR S2486
75
+ return fallback;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Auto-file rail: best-effort, NON-BLOCKING ensure that the branch being pushed has a
81
+ * backing Kernel issue (kernel issue a4b8f56f) — so started work that is being pushed
82
+ * never goes unfiled. Idempotent (deduped by branch via ensureBackingIssue); a
83
+ * main/master/detached/ignore branch is skipped, and ANY failure degrades to null and
84
+ * NEVER aborts the push. Injectable via `deps._ensureBackingIssue` for tests.
85
+ *
86
+ * @param {string} projectRoot
87
+ * @param {function} execFn - execFileSync (for branch resolution)
88
+ * @param {object} deps - DI (may inject `_ensureBackingIssue`, `_kernelDriver`, `_kernelBroker`)
89
+ * @returns {Promise<object|null>}
90
+ */
91
+ async function autoFileBackingIssueForPush(projectRoot, execFn, deps = {}) {
92
+ try {
93
+ // Lazy require INSIDE the try: a module-load failure must degrade to null and
94
+ // NEVER abort the push (mirrors the worktree helper; CodeRabbit #370).
95
+ const ensure = deps._ensureBackingIssue || require('../kernel/backing-issue').ensureBackingIssue;
96
+ const branch = getCurrentBranch(execFn);
97
+ if (!branch || branch === 'unknown') return null;
98
+ let driver = deps._kernelDriver;
99
+ let broker = deps._kernelBroker;
100
+ if (!driver || !broker) {
101
+ if (!fs.existsSync(path.join(projectRoot, '.git'))) return null;
102
+ const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
103
+ const built = await buildMigratedKernelIssueDeps({ projectRoot });
104
+ driver = driver || built.kernelDriver;
105
+ broker = broker || built.kernelBroker;
106
+ }
107
+ return await ensure({ branch, projectRoot, driver, broker });
108
+ } catch (_err) { /* non-blocking: tracking must never break a push */ // NOSONAR S2486
109
+ return null;
110
+ }
111
+ }
112
+
57
113
  /**
58
114
  * Run branch protection check as a subprocess.
59
115
  * @param {function} execFn - execFileSync or mock
@@ -150,6 +206,10 @@ module.exports = {
150
206
  return { success: false, quickMode, lintPassed: false, pushed: false };
151
207
  }
152
208
 
209
+ // Auto-file rail (non-blocking): ensure the branch being pushed has a backing
210
+ // Kernel issue so started work never goes unfiled. Never aborts the push.
211
+ await autoFileBackingIssueForPush(projectRoot, execFn, deps || {});
212
+
153
213
  // Step 2: Lint
154
214
  const lintPassed = runLint(spawnFn, pkgManager, projectRoot);
155
215
  if (!lintPassed) {
@@ -163,9 +223,11 @@ module.exports = {
163
223
  return { success: false, quickMode, lintPassed: true, testsPassed: false, pushed: false };
164
224
  }
165
225
 
166
- // Step 4: Write nonce token so lefthook skips pre-push hooks
226
+ // Step 4: Write nonce token so lefthook skips pre-push hooks.
227
+ // Write to the git worktree top-level (where the hook checks), not the
228
+ // inherited projectRoot, so the skip works from linked worktrees too.
167
229
  try {
168
- tokenWrite(projectRoot);
230
+ tokenWrite(resolveGitToplevel(execFn, projectRoot));
169
231
  } catch (_err) { /* intentional: non-fatal, lefthook will re-run hooks */ // NOSONAR S2486
170
232
  log('Warning: Could not write forge push token — lefthook hooks will run again');
171
233
  }
@@ -193,4 +255,9 @@ module.exports = {
193
255
  pushed: true,
194
256
  };
195
257
  },
258
+
259
+ // Exposed for unit tests; not part of the CLI surface.
260
+ _internal: {
261
+ autoFileBackingIssueForPush,
262
+ },
196
263
  };
@@ -1,5 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { makeAliasCommand } = require('./_issue');
3
+ const { createIssueSubcommand } = require('./_issue');
4
4
 
5
- module.exports = makeAliasCommand('ready');
5
+ module.exports = createIssueSubcommand('ready');
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+
3
+ const memoryRouter = require('../memory/router');
4
+ const { stripGlobalFlags } = require('../global-flags');
5
+ const { fenceUntrusted } = require('../untrusted-content');
6
+
7
+ const usage = 'Usage: forge recall [query] [--limit N] [--all] [--json]';
8
+
9
+ /**
10
+ * Separate the optional positional query from `--limit N` and `--json`.
11
+ * Global flags (e.g. `-p <dir>`, `--all`) are stripped first so they never
12
+ * corrupt the search query (kernel issue c1e090ff). `--all` is a GLOBAL boolean
13
+ * flag, so the handler reads it from its `flags` argument (or the raw args on a
14
+ * direct call) — not from this parsed query.
15
+ *
16
+ * @param {string[]} rawArgs - Raw command arguments.
17
+ * @returns {{ query: string, limit: (number|undefined), json: boolean }}
18
+ */
19
+ function parseArgs(rawArgs) {
20
+ const args = stripGlobalFlags(rawArgs);
21
+ const words = [];
22
+ let limit;
23
+ let json = false;
24
+
25
+ for (let index = 0; index < args.length; index += 1) {
26
+ const arg = args[index];
27
+ if (arg === '--json') {
28
+ json = true;
29
+ } else if (arg === '--limit') {
30
+ const value = Number(args[index + 1]);
31
+ if (Number.isInteger(value) && value > 0) {
32
+ limit = value;
33
+ index += 1;
34
+ }
35
+ } else if (arg.startsWith('--limit=')) {
36
+ const value = Number(arg.slice('--limit='.length));
37
+ if (Number.isInteger(value) && value > 0) {
38
+ limit = value;
39
+ }
40
+ } else {
41
+ words.push(arg);
42
+ }
43
+ }
44
+
45
+ return { query: words.join(' ').trim(), limit, json };
46
+ }
47
+
48
+ function formatEntry(entry) {
49
+ const date = entry.timestamp ? entry.timestamp.slice(0, 10) : '';
50
+ const prefix = date ? `${date} ` : '';
51
+ const tagSuffix = entry.tags.length > 0 ? ` [${entry.tags.join(', ')}]` : '';
52
+ // Machine/insights records are LABELED with their source so they are never mistaken for a
53
+ // plain human note; human `remember` notes render clean.
54
+ const marker = entry.machine && entry.sourceAgent ? `(${entry.sourceAgent}) ` : '';
55
+ // Stored note text is UNTRUSTED (a planted memory could carry injected directives),
56
+ // so the human/agent-facing render is provenance-fenced. The `--json` path above
57
+ // keeps the raw note so programmatic consumers/parsers are unaffected.
58
+ const note = fenceUntrusted(entry.note, { source: 'memory' });
59
+ return `- ${prefix}${marker}${note}${tagSuffix}`;
60
+ }
61
+
62
+ async function handler(args, flags, projectRoot) {
63
+ const { query, limit, json } = parseArgs(args);
64
+ // `--all` is a GLOBAL boolean flag: in production bin/forge.js strips it from
65
+ // args and sets flags.all; on a direct handler call it may still be in args.
66
+ const all = Boolean(flags && flags.all) || args.includes('--all');
67
+
68
+ const { notes, total, capped, scope } = memoryRouter.recall(projectRoot, { query, limit, all });
69
+
70
+ if (json) {
71
+ // Object (not a bare array) so programmatic consumers see the total and whether the
72
+ // result was truncated (raise --limit to page further).
73
+ return {
74
+ success: true,
75
+ output: `${JSON.stringify({ notes, total, capped, scope }, null, 2)}\n`,
76
+ };
77
+ }
78
+
79
+ if (notes.length === 0) {
80
+ const reason = query
81
+ ? `No notes match "${query}".`
82
+ : 'No notes remembered yet. Use "forge remember <note>" to add one.';
83
+ return { success: true, output: reason };
84
+ }
85
+
86
+ const noun = scope === 'all' && !query ? 'stored memory record(s)' : 'remembered note(s)';
87
+ let header;
88
+ if (query) {
89
+ // BM25 returns at most `limit`; when full, signal it is the TOP-N, not the whole set.
90
+ header = capped
91
+ ? `Top ${notes.length} note(s) matching "${query}" (raise --limit for more):`
92
+ : `${notes.length} note(s) matching "${query}":`;
93
+ } else if (capped) {
94
+ // Never a bare full dump: show the newest N and the true total.
95
+ header = `Showing ${notes.length} of ${total} ${noun} (newest first):`;
96
+ } else {
97
+ header = `${total} ${noun}:`;
98
+ }
99
+ const lines = notes.map(formatEntry);
100
+ return {
101
+ success: true,
102
+ output: [header, ...lines].join('\n'),
103
+ };
104
+ }
105
+
106
+ module.exports = {
107
+ name: 'recall',
108
+ description: 'Retrieve project-memory notes from the kernel-backed memory store',
109
+ usage,
110
+ flags: {
111
+ '--limit': 'Cap the number of notes returned',
112
+ '--all': 'Include machine/insights records in the no-query listing (query already searches all)',
113
+ '--json': 'Emit machine-readable JSON output',
114
+ },
115
+ handler,
116
+ };
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ buildIssueRecap,
5
+ formatOrientationText,
6
+ } = require('../orientation');
7
+
8
+ const usage = 'Usage: forge recap <issue> [--budget N] [--json]';
9
+
10
+ // Options that consume the following token as their value. The issue-id scan
11
+ // must skip these values so `forge recap --budget 220 <issue>` is not misread.
12
+ const OPTIONS_WITH_VALUES = new Set(['--budget']);
13
+
14
+ function readOption(args, name, fallback) {
15
+ const equals = args.find(arg => arg.startsWith(`${name}=`));
16
+ if (equals) return equals.slice(name.length + 1);
17
+ const index = args.indexOf(name);
18
+ if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('-')) return args[index + 1];
19
+ return fallback;
20
+ }
21
+
22
+ function readIssueArg(args) {
23
+ for (let index = 0; index < args.length; index += 1) {
24
+ const arg = args[index];
25
+ if (!arg) continue;
26
+ if (OPTIONS_WITH_VALUES.has(arg)) {
27
+ index += 1;
28
+ continue;
29
+ }
30
+ if (arg.startsWith('-')) continue;
31
+ return arg;
32
+ }
33
+ return null;
34
+ }
35
+
36
+ // `forge recap <issue>` summarizes a single issue from the deterministic
37
+ // orientation source assembly — it is issue-scoped, not a project-wide recap.
38
+ async function handler(args, _flags, projectRoot) {
39
+ const issueId = readIssueArg(args);
40
+ if (!issueId) {
41
+ // Use `error` (not `output`) so the CLI dispatcher prints the usage line
42
+ // once via console.error instead of also appending a redundant bare
43
+ // "Command failed" (see bin/forge.js registry dispatch).
44
+ return { success: false, error: usage };
45
+ }
46
+
47
+ const recap = buildIssueRecap(projectRoot, issueId, {
48
+ budgetTokens: readOption(args, '--budget', undefined),
49
+ });
50
+ return {
51
+ success: true,
52
+ output: args.includes('--json') ? `${JSON.stringify(recap, null, 2)}\n` : formatOrientationText(recap),
53
+ };
54
+ }
55
+
56
+ module.exports = {
57
+ name: 'recap',
58
+ description: 'Summarize a single issue from deterministic orientation source files',
59
+ usage,
60
+ handler,
61
+ };
@@ -23,7 +23,6 @@ const STAGE_NAMES = {
23
23
  validate: 'Validate',
24
24
  ship: 'Ship',
25
25
  review: 'Review',
26
- premerge: 'Premerge',
27
26
  };
28
27
 
29
28
  /**
@@ -116,4 +115,25 @@ function handleRecommend(flags, projectPath) {
116
115
  return { budgetMode, recommendations };
117
116
  }
118
117
 
119
- module.exports = { formatRecommendations, handleRecommend };
118
+ async function handler(_args = [], flags = {}, projectRoot) {
119
+ const result = handleRecommend(flags, projectRoot);
120
+ if (result.error) {
121
+ return { success: false, error: result.error };
122
+ }
123
+
124
+ const output = [
125
+ '',
126
+ `🔧 Forge Tool Recommendations (Budget: ${result.budgetMode})`,
127
+ formatRecommendations(result.recommendations),
128
+ ].join('\n');
129
+
130
+ return { success: true, output };
131
+ }
132
+
133
+ module.exports = {
134
+ name: 'recommend',
135
+ description: 'Show tool recommendations for the current project',
136
+ handler,
137
+ formatRecommendations,
138
+ handleRecommend
139
+ };
@@ -0,0 +1,91 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ SUPPORTED_TARGET,
5
+ buildReadinessReport,
6
+ renderReadinessReport,
7
+ } = require('../release-readiness');
8
+ const { runIssueOperation: defaultRunIssueOperation } = require('../forge-issues');
9
+ const { normalizeArgs, normalizeIssueResult, withResolvedIssueBackend } = require('./_issue');
10
+
11
+ // `forge release <id>` releases a claimed issue; `forge release check` runs the
12
+ // release-readiness gate. The two share the top-level verb, so this command
13
+ // dispatches `check` to the gate and routes everything else through the shared
14
+ // issue dispatch (resolve backend → runIssueOperation('release') → normalize). The
15
+ // Beads backend has no release op and returns the Kernel-only contract error.
16
+ const usage = 'Usage: forge release <id> | forge release check --target 0.1.0 [--json]';
17
+
18
+ async function runReleaseIssue(args, projectRoot, opts = {}) {
19
+ const resolved = withResolvedIssueBackend(projectRoot, opts);
20
+ const runIssueOperation = resolved.runIssueOperation || defaultRunIssueOperation;
21
+ const normalizedArgs = normalizeArgs(args);
22
+ const result = await runIssueOperation('release', normalizedArgs, projectRoot,
23
+ { ...resolved, kernelBroker: resolved.kernelBroker });
24
+ return normalizeIssueResult(result, 'release', { json: normalizedArgs.includes('--json') });
25
+ }
26
+
27
+ function readOption(args, name, fallback) {
28
+ const equals = args.find(arg => arg.startsWith(`${name}=`));
29
+ if (equals) {
30
+ return equals.slice(name.length + 1);
31
+ }
32
+
33
+ const index = args.indexOf(name);
34
+ if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('-')) {
35
+ return args[index + 1];
36
+ }
37
+
38
+ return fallback;
39
+ }
40
+
41
+ function parseReleaseArgs(args = []) {
42
+ const subcommand = args.find((arg, index) => {
43
+ if (arg.startsWith('-')) {
44
+ return false;
45
+ }
46
+ const previous = args[index - 1];
47
+ return previous !== '--target';
48
+ });
49
+
50
+ return {
51
+ subcommand,
52
+ target: readOption(args, '--target', SUPPORTED_TARGET),
53
+ json: args.includes('--json'),
54
+ };
55
+ }
56
+
57
+ async function handler(args, _flags, projectRoot, opts = {}) {
58
+ const parsed = parseReleaseArgs(args);
59
+
60
+ if (parsed.subcommand !== 'check') {
61
+ // forge release <id> — release a claimed issue via the shared issue dispatch.
62
+ return runReleaseIssue(args, projectRoot, opts);
63
+ }
64
+
65
+ const report = buildReadinessReport(projectRoot, { target: parsed.target });
66
+ const output = parsed.json
67
+ ? `${JSON.stringify(report, null, 2)}\n`
68
+ : renderReadinessReport(report);
69
+ const error = parsed.json
70
+ ? `Forge release readiness check failed for ${report.target}`
71
+ : output;
72
+
73
+ return {
74
+ success: report.success,
75
+ report,
76
+ output,
77
+ error: report.success ? undefined : error,
78
+ };
79
+ }
80
+
81
+ module.exports = {
82
+ name: 'release',
83
+ description: 'Release a claimed issue, or run Forge release readiness gates (forge release check)',
84
+ usage,
85
+ flags: {
86
+ '--target <version>': 'Release target to check',
87
+ '--json': 'Emit the readiness report as JSON',
88
+ },
89
+ handler,
90
+ parseReleaseArgs,
91
+ };