forge-workflow 0.0.10 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +150 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -118
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +461 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/forge/TOOLCHAIN.md +670 -0
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +121 -0
  81. package/lib/beads-sync-scaffold.js +25 -101
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +741 -77
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +17 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +0 -1
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +838 -972
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +158 -21
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +4 -1
  142. package/lib/commands/test.js +43 -27
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +43 -18
  146. package/lib/commands/worktree.js +307 -100
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +2 -2
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +35 -9
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +382 -11
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/kernel/backing-issue.js +305 -0
  174. package/lib/kernel/broker.js +1218 -0
  175. package/lib/kernel/cli-broker-factory.js +130 -0
  176. package/lib/kernel/conflict-signal.js +82 -0
  177. package/lib/kernel/evaluators.js +195 -0
  178. package/lib/kernel/fs-class.js +495 -0
  179. package/lib/kernel/issue-command-contract.js +559 -0
  180. package/lib/kernel/issue-id-resolver.js +186 -0
  181. package/lib/kernel/lease-enforcer.js +158 -0
  182. package/lib/kernel/migrations.js +333 -0
  183. package/lib/kernel/planning-buckets-schema.js +109 -0
  184. package/lib/kernel/projection-jsonl-writer.js +450 -0
  185. package/lib/kernel/readiness-model.js +329 -0
  186. package/lib/kernel/schema.js +356 -0
  187. package/lib/kernel/sqlite-driver.js +2504 -0
  188. package/lib/kernel/taxonomy-validator.js +394 -0
  189. package/lib/lefthook-check.js +3 -2
  190. package/lib/lefthook-wiring.js +413 -0
  191. package/lib/mcp-config-renderer.js +288 -0
  192. package/lib/memory/graphiti-mcp.js +106 -0
  193. package/lib/memory/router.js +387 -0
  194. package/lib/memory/typed-api.js +102 -0
  195. package/lib/memory-digest.js +195 -0
  196. package/lib/merge-rules.js +395 -0
  197. package/lib/migrate-dry-run.js +466 -0
  198. package/lib/orientation.js +863 -0
  199. package/lib/package-manager-remediation.js +103 -0
  200. package/lib/package-root.js +381 -0
  201. package/lib/patch-intent.js +890 -0
  202. package/lib/plugin-catalog.js +3 -4
  203. package/lib/plugin-manager.js +0 -5
  204. package/lib/pr-bundle.js +186 -0
  205. package/lib/pr-monitor/differ.js +195 -0
  206. package/lib/pr-monitor/events.js +0 -0
  207. package/lib/pr-monitor/gather.js +124 -0
  208. package/lib/pr-monitor/journal.js +299 -0
  209. package/lib/pr-monitor/monitor.js +146 -0
  210. package/lib/pr-monitor/render-sticky.js +157 -0
  211. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  212. package/lib/pr-monitor/watch.js +247 -0
  213. package/lib/pr-pull.js +1273 -0
  214. package/lib/pr-shepherd.js +494 -0
  215. package/lib/pr-state-validator.js +59 -0
  216. package/lib/preflight/gates.js +237 -0
  217. package/lib/preflight/runner.js +116 -0
  218. package/lib/project-discovery.js +0 -53
  219. package/lib/project-memory.js +99 -497
  220. package/lib/protected-path-manifest.js +281 -0
  221. package/lib/protected-state-surfaces.js +387 -0
  222. package/lib/release-readiness.js +2089 -0
  223. package/lib/reset.js +59 -45
  224. package/lib/review-adapter.js +68 -0
  225. package/lib/rules-sync.js +260 -0
  226. package/lib/runtime-health.js +241 -20
  227. package/lib/safety-config-renderer.js +268 -0
  228. package/lib/setup-action-log.js +1 -7
  229. package/lib/setup.js +27 -65
  230. package/lib/shell-utils.js +76 -6
  231. package/lib/skills-sync.js +330 -0
  232. package/lib/smart-status/scoring.js +17 -3
  233. package/lib/status/beads-snapshot.js +45 -2
  234. package/lib/status/presenter.js +169 -18
  235. package/lib/status/snapshot.js +186 -0
  236. package/lib/sync-backend.js +202 -0
  237. package/lib/untrusted-content.js +52 -0
  238. package/lib/upgrade-safety.js +199 -0
  239. package/lib/workflow/enforce-stage.js +296 -47
  240. package/lib/workflow/stage-transition.js +115 -0
  241. package/lib/workflow/stages.js +30 -6
  242. package/lib/workflow/state-manager.js +11 -22
  243. package/lib/workflow/state.js +23 -1
  244. package/lib/workflow-profiles.js +17 -5
  245. package/package.json +37 -35
  246. package/rules/documentation.md +19 -0
  247. package/rules/kernel-tracking.md +26 -0
  248. package/rules/security.md +22 -0
  249. package/rules/tdd.md +20 -0
  250. package/rules/workflow.md +27 -0
  251. package/scripts/auto-backing-issue.js +47 -0
  252. package/scripts/beads-context.sh +81 -57
  253. package/scripts/beads-upgrade-smoke.sh +24 -3
  254. package/scripts/bootstrap-windows-tools.sh +78 -0
  255. package/scripts/branch-protection.js +2 -3
  256. package/scripts/check-agents.js +34 -137
  257. package/scripts/commitlint.js +3 -1
  258. package/scripts/conflict-detect.sh +3 -0
  259. package/scripts/dep-guard.sh +22 -3
  260. package/scripts/file-index.sh +3 -0
  261. package/scripts/forge-team/lib/claim.sh +34 -18
  262. package/scripts/forge-team/lib/dashboard.sh +61 -86
  263. package/scripts/forge-team/lib/epic.sh +99 -263
  264. package/scripts/forge-team/lib/hooks.sh +26 -28
  265. package/scripts/forge-team/lib/identity.sh +4 -4
  266. package/scripts/forge-team/lib/sync-github.sh +49 -84
  267. package/scripts/forge-team/lib/verify.sh +93 -83
  268. package/scripts/forge-team/lib/workload.sh +41 -65
  269. package/scripts/forge-team/tests/claim.test.sh +25 -19
  270. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  271. package/scripts/forge-team/tests/epic.test.sh +52 -71
  272. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  273. package/scripts/forge-team/tests/identity.test.sh +3 -3
  274. package/scripts/forge-team/tests/integration.test.sh +44 -66
  275. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  276. package/scripts/forge-team/tests/verify.test.sh +37 -46
  277. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  278. package/scripts/forge-team/tests/workload.test.sh +32 -66
  279. package/scripts/gen-command-manifest.js +153 -0
  280. package/scripts/gen-embedded-assets.mjs +129 -0
  281. package/scripts/install.ps1 +139 -0
  282. package/scripts/install.sh +268 -0
  283. package/scripts/lib/release-asset.mjs +84 -0
  284. package/scripts/parity-check.mjs +145 -0
  285. package/scripts/parity-check.test.mjs +58 -0
  286. package/scripts/pin-agentic-workflow-images.js +112 -0
  287. package/scripts/pr-coordinator.sh +3 -0
  288. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  289. package/scripts/preflight.sh +21 -94
  290. package/scripts/protected-state-check.js +104 -0
  291. package/scripts/smart-status.sh +60 -57
  292. package/scripts/spikes/config-race-bench.js +111 -0
  293. package/scripts/spikes/harness-capability-matrix.js +13 -0
  294. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  295. package/scripts/spikes/protected-path-manifest.js +20 -0
  296. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  297. package/scripts/sync-agent-skills.js +62 -0
  298. package/scripts/sync-utils.sh +3 -0
  299. package/scripts/test-ci-shard.js +13 -6
  300. package/scripts/test.js +95 -12
  301. package/skills/claim-safety/SKILL.md +102 -0
  302. package/skills/claim-safety/evals/evals.json +46 -0
  303. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  304. package/skills/dev/evals/evals.json +50 -0
  305. package/skills/hermes-forge/SKILL.md +185 -0
  306. package/skills/hermes-forge/evals/evals.json +46 -0
  307. package/skills/issue-basics/SKILL.md +111 -0
  308. package/skills/issue-basics/evals/evals.json +46 -0
  309. package/skills/kernel/SKILL.md +166 -0
  310. package/skills/kernel/evals/evals.json +50 -0
  311. package/skills/memory/SKILL.md +102 -0
  312. package/skills/parallel-deep-research/SKILL.md +14 -11
  313. package/skills/parallel-deep-research/evals/evals.json +11 -27
  314. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  315. package/skills/plan/evals/evals.json +42 -0
  316. package/skills/research/SKILL.md +195 -0
  317. package/skills/research/evals/evals.json +42 -0
  318. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  319. package/skills/review/evals/evals.json +42 -0
  320. package/skills/rollback/SKILL.md +110 -0
  321. package/skills/rollback/evals/evals.json +46 -0
  322. package/skills/rollback/references/methods.md +204 -0
  323. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  324. package/skills/shepherd/SKILL.md +66 -0
  325. package/skills/shepherd/evals/evals.json +42 -0
  326. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  327. package/skills/ship/evals/evals.json +42 -0
  328. package/skills/smith/SKILL.md +142 -0
  329. package/skills/smith/evals/evals.json +46 -0
  330. package/skills/smith/references/autonomy-and-gates.md +94 -0
  331. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  332. package/skills/sonarcloud/evals/evals.json +46 -0
  333. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  334. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  335. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  336. package/skills/status/evals/evals.json +50 -0
  337. package/skills/triage-ready/SKILL.md +121 -0
  338. package/skills/triage-ready/evals/evals.json +42 -0
  339. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  340. package/skills/validate/evals/evals.json +42 -0
  341. package/skills/verify/SKILL.md +299 -0
  342. package/skills/verify/evals/evals.json +50 -0
  343. package/.claude/commands/dev.md +0 -345
  344. package/.claude/commands/plan.md +0 -566
  345. package/.claude/commands/premerge.md +0 -186
  346. package/.claude/commands/research.md +0 -42
  347. package/.claude/commands/review.md +0 -451
  348. package/.claude/commands/rollback.md +0 -721
  349. package/.claude/commands/ship.md +0 -213
  350. package/.claude/commands/sonarcloud.md +0 -152
  351. package/.claude/commands/status.md +0 -90
  352. package/.claude/commands/validate.md +0 -288
  353. package/.claude/commands/verify.md +0 -269
  354. package/.claude/rules/workflow.md +0 -121
  355. package/.cline/workflows/dev.md +0 -342
  356. package/.cline/workflows/plan.md +0 -563
  357. package/.cline/workflows/premerge.md +0 -183
  358. package/.cline/workflows/research.md +0 -39
  359. package/.cline/workflows/review.md +0 -448
  360. package/.cline/workflows/rollback.md +0 -718
  361. package/.cline/workflows/ship.md +0 -210
  362. package/.cline/workflows/sonarcloud.md +0 -146
  363. package/.cline/workflows/status.md +0 -87
  364. package/.cline/workflows/validate.md +0 -285
  365. package/.cline/workflows/verify.md +0 -266
  366. package/.codex/config.toml +0 -11
  367. package/.codex/skills/dev/SKILL.md +0 -345
  368. package/.codex/skills/plan/SKILL.md +0 -566
  369. package/.codex/skills/premerge/SKILL.md +0 -186
  370. package/.codex/skills/research/SKILL.md +0 -42
  371. package/.codex/skills/review/SKILL.md +0 -451
  372. package/.codex/skills/rollback/SKILL.md +0 -721
  373. package/.codex/skills/ship/SKILL.md +0 -213
  374. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  375. package/.codex/skills/status/SKILL.md +0 -90
  376. package/.codex/skills/validate/SKILL.md +0 -288
  377. package/.codex/skills/verify/SKILL.md +0 -269
  378. package/.cursor/commands/dev.md +0 -342
  379. package/.cursor/commands/plan.md +0 -563
  380. package/.cursor/commands/premerge.md +0 -183
  381. package/.cursor/commands/research.md +0 -39
  382. package/.cursor/commands/review.md +0 -448
  383. package/.cursor/commands/ship.md +0 -210
  384. package/.cursor/commands/sonarcloud.md +0 -146
  385. package/.cursor/commands/status.md +0 -87
  386. package/.cursor/commands/validate.md +0 -285
  387. package/.cursor/commands/verify.md +0 -266
  388. package/.cursorrules +0 -149
  389. package/.github/prompts/premerge.prompt.md +0 -188
  390. package/.github/prompts/research.prompt.md +0 -44
  391. package/.github/prompts/rollback.prompt.md +0 -723
  392. package/.github/prompts/verify.prompt.md +0 -271
  393. package/.github/workflows/beads-to-github.yml +0 -89
  394. package/.github/workflows/github-to-beads.yml +0 -100
  395. package/.kilocode/workflows/dev.md +0 -346
  396. package/.kilocode/workflows/plan.md +0 -567
  397. package/.kilocode/workflows/premerge.md +0 -187
  398. package/.kilocode/workflows/research.md +0 -43
  399. package/.kilocode/workflows/review.md +0 -452
  400. package/.kilocode/workflows/rollback.md +0 -722
  401. package/.kilocode/workflows/ship.md +0 -214
  402. package/.kilocode/workflows/sonarcloud.md +0 -150
  403. package/.kilocode/workflows/status.md +0 -91
  404. package/.kilocode/workflows/validate.md +0 -289
  405. package/.kilocode/workflows/verify.md +0 -270
  406. package/.opencode/commands/dev.md +0 -345
  407. package/.opencode/commands/plan.md +0 -566
  408. package/.opencode/commands/premerge.md +0 -186
  409. package/.opencode/commands/research.md +0 -42
  410. package/.opencode/commands/review.md +0 -451
  411. package/.opencode/commands/rollback.md +0 -721
  412. package/.opencode/commands/ship.md +0 -213
  413. package/.opencode/commands/sonarcloud.md +0 -149
  414. package/.opencode/commands/status.md +0 -90
  415. package/.opencode/commands/validate.md +0 -288
  416. package/.opencode/commands/verify.md +0 -269
  417. package/.roo/commands/dev.md +0 -346
  418. package/.roo/commands/plan.md +0 -567
  419. package/.roo/commands/premerge.md +0 -187
  420. package/.roo/commands/research.md +0 -43
  421. package/.roo/commands/review.md +0 -452
  422. package/.roo/commands/rollback.md +0 -722
  423. package/.roo/commands/ship.md +0 -214
  424. package/.roo/commands/sonarcloud.md +0 -150
  425. package/.roo/commands/status.md +0 -91
  426. package/.roo/commands/validate.md +0 -289
  427. package/.roo/commands/verify.md +0 -270
  428. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  429. package/docs/GREPTILE_SETUP.md +0 -400
  430. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  431. package/docs/SETUP.md +0 -663
  432. package/docs/VALIDATION.md +0 -363
  433. package/lib/agents/cline.plugin.json +0 -29
  434. package/lib/agents/copilot.plugin.json +0 -24
  435. package/lib/agents/kilocode.plugin.json +0 -22
  436. package/lib/agents/opencode.plugin.json +0 -23
  437. package/lib/agents/roo.plugin.json +0 -30
  438. package/lib/beads-bootstrap.js +0 -225
  439. package/lib/beads-health-check.js +0 -188
  440. package/lib/commands/commands-reset.js +0 -147
  441. package/opencode.json +0 -67
  442. package/scripts/beads-context.test.js +0 -584
  443. package/scripts/github-beads-sync/comment.mjs +0 -64
  444. package/scripts/github-beads-sync/config.mjs +0 -148
  445. package/scripts/github-beads-sync/github-api.mjs +0 -131
  446. package/scripts/github-beads-sync/index.mjs +0 -356
  447. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  448. package/scripts/github-beads-sync/mapping.mjs +0 -132
  449. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  450. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  451. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  452. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  453. package/scripts/github-beads-sync.config.json +0 -26
  454. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,598 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const readline = require('node:readline');
4
+ const YAML = require('yaml');
5
+
6
+ const {
7
+ ADOPTION_PROFILE_NAMES,
8
+ buildAdoptionConfig,
9
+ renderAdoptionConfigYaml,
10
+ } = require('../adoption-profiles');
11
+
12
+ const PROFILE_REQUIRED_ERROR = '--profile requires a non-empty value: minimal, standard, or full.';
13
+ const CLASSIFICATION_REQUIRED_ERROR = '--classification requires a non-empty value: critical, standard, or refactor.';
14
+ const PROFILE_SHORTCUTS = Object.freeze({
15
+ '--minimal': 'minimal',
16
+ '--standard': 'standard',
17
+ '--full': 'full',
18
+ });
19
+ const CLASSIFICATIONS = Object.freeze(['critical', 'standard', 'refactor']);
20
+ const DETECTABLE_HARNESS_TARGETS = Object.freeze(['claude', 'cursor', 'codex']);
21
+ const HARNESS_TARGETS = Object.freeze(['claude', 'cursor', 'codex']);
22
+ const HARNESS_REQUIRED_ERROR = `--harness requires a non-empty value: ${HARNESS_TARGETS.join(', ')}.`;
23
+
24
+ /** Return the command help text shown by the registry. */
25
+ function usage() {
26
+ return [
27
+ 'Usage: forge init [--profile minimal|standard|full] [--classification critical|standard|refactor] [--harness claude,cursor,codex] [--yes] [--force] [--dry-run]',
28
+ '',
29
+ 'Initialize the day-one .forge skeleton for a fresh repository.',
30
+ ].join('\n');
31
+ }
32
+
33
+ /** Normalize a profile flag value while preserving empty-string validation. */
34
+ function normalizeProfile(value) {
35
+ if (typeof value !== 'string') return value ?? null;
36
+ const trimmed = value.trim();
37
+ return trimmed.length > 0 ? trimmed : '';
38
+ }
39
+
40
+ /** Apply a parsed adoption profile value to the mutable parse result. */
41
+ function setProfile(parsed, value) {
42
+ const profile = normalizeProfile(value);
43
+ if (profile === '') {
44
+ parsed.error = PROFILE_REQUIRED_ERROR;
45
+ parsed.profile = null;
46
+ return;
47
+ }
48
+ parsed.profile = profile;
49
+ }
50
+
51
+ /** Normalize a classification flag or wizard answer. */
52
+ function normalizeClassification(value) {
53
+ if (typeof value !== 'string') return value ?? null;
54
+ const trimmed = value.trim().toLowerCase();
55
+ return trimmed.length > 0 ? trimmed : '';
56
+ }
57
+
58
+ /** Validate and apply a default workflow classification. */
59
+ function setClassification(parsed, value) {
60
+ const classification = normalizeClassification(value);
61
+ if (classification === '') {
62
+ parsed.error = CLASSIFICATION_REQUIRED_ERROR;
63
+ parsed.classification = null;
64
+ return;
65
+ }
66
+ if (!CLASSIFICATIONS.includes(classification)) {
67
+ parsed.error = `Unknown classification '${classification}'. Choose one of: ${CLASSIFICATIONS.join(', ')}.`;
68
+ parsed.classification = null;
69
+ return;
70
+ }
71
+ parsed.classification = classification;
72
+ }
73
+
74
+ /** Convert comma-separated or array harness input into normalized target names. */
75
+ function normalizeHarnessTargets(value) {
76
+ if (Array.isArray(value)) {
77
+ return value.flatMap(item => normalizeHarnessTargets(item));
78
+ }
79
+ if (typeof value !== 'string') return [];
80
+ return value
81
+ .split(',')
82
+ .map(item => item.trim().toLowerCase())
83
+ .filter(Boolean);
84
+ }
85
+
86
+ /** Validate and apply selected harness targets. */
87
+ function setHarnessTargets(parsed, value) {
88
+ const targets = [...new Set(normalizeHarnessTargets(value))];
89
+ if (targets.length === 0) {
90
+ parsed.error = HARNESS_REQUIRED_ERROR;
91
+ parsed.harnessTargets = null;
92
+ return;
93
+ }
94
+ const unknown = targets.find(target => !HARNESS_TARGETS.includes(target));
95
+ if (unknown) {
96
+ parsed.error = `Unknown harness target '${unknown}'. Choose from: ${HARNESS_TARGETS.join(', ')}.`;
97
+ parsed.harnessTargets = null;
98
+ return;
99
+ }
100
+ parsed.harnessTargets = targets;
101
+ }
102
+
103
+ /** Consume --profile syntax from argv and return the consumed index. */
104
+ function consumeProfileArg(args, index, parsed) {
105
+ const arg = args[index];
106
+ if (arg === '--profile') {
107
+ const value = args[index + 1];
108
+ if (typeof value === 'string' && !value.startsWith('--')) {
109
+ setProfile(parsed, value);
110
+ return index + 1;
111
+ }
112
+ parsed.error = PROFILE_REQUIRED_ERROR;
113
+ return index;
114
+ }
115
+
116
+ setProfile(parsed, arg.slice('--profile='.length));
117
+ return index;
118
+ }
119
+
120
+ /** Consume --classification syntax from argv and return the consumed index. */
121
+ function consumeClassificationArg(args, index, parsed) {
122
+ const arg = args[index];
123
+ if (arg === '--classification') {
124
+ const value = args[index + 1];
125
+ if (typeof value === 'string' && !value.startsWith('--')) {
126
+ setClassification(parsed, value);
127
+ return index + 1;
128
+ }
129
+ parsed.error = CLASSIFICATION_REQUIRED_ERROR;
130
+ return index;
131
+ }
132
+
133
+ setClassification(parsed, arg.slice('--classification='.length));
134
+ return index;
135
+ }
136
+
137
+ /** Consume --harness syntax from argv and return the consumed index. */
138
+ function consumeHarnessArg(args, index, parsed) {
139
+ const arg = args[index];
140
+ if (arg === '--harness') {
141
+ const value = args[index + 1];
142
+ if (typeof value === 'string' && !value.startsWith('--')) {
143
+ setHarnessTargets(parsed, value);
144
+ return index + 1;
145
+ }
146
+ parsed.error = HARNESS_REQUIRED_ERROR;
147
+ return index;
148
+ }
149
+
150
+ setHarnessTargets(parsed, arg.slice('--harness='.length));
151
+ return index;
152
+ }
153
+
154
+ /** Apply profile shortcut flags such as --minimal and --full. */
155
+ function applyProfileShortcut(arg, parsed) {
156
+ const profile = PROFILE_SHORTCUTS[arg];
157
+ if (profile) {
158
+ parsed.profile = profile;
159
+ parsed.yes = true;
160
+ }
161
+ return Boolean(profile);
162
+ }
163
+
164
+ /** Parse init-specific flags after global CLI flag handling. */
165
+ function parseInitFlags(args = [], flags = {}) {
166
+ const parsed = {
167
+ profile: null,
168
+ error: null,
169
+ yes: Boolean(flags.yes || flags.nonInteractive),
170
+ force: Boolean(flags.force),
171
+ dryRun: Boolean(flags.dryRun),
172
+ classification: null,
173
+ harnessTargets: null,
174
+ railsConfirmed: Boolean(flags.yes || flags.nonInteractive),
175
+ };
176
+
177
+ if (Object.hasOwn(flags, 'profile')) {
178
+ setProfile(parsed, flags.profile);
179
+ }
180
+ if (Object.hasOwn(flags, 'classification')) {
181
+ setClassification(parsed, flags.classification);
182
+ }
183
+ if (Object.hasOwn(flags, 'harness')) {
184
+ setHarnessTargets(parsed, flags.harness);
185
+ }
186
+
187
+ let index = 0;
188
+ while (index < args.length) {
189
+ const arg = args[index];
190
+ if (arg === '--profile' || arg.startsWith('--profile=')) {
191
+ index = consumeProfileArg(args, index, parsed) + 1;
192
+ continue;
193
+ } else if (arg === '--classification' || arg.startsWith('--classification=')) {
194
+ index = consumeClassificationArg(args, index, parsed) + 1;
195
+ continue;
196
+ } else if (arg === '--harness' || arg.startsWith('--harness=')) {
197
+ index = consumeHarnessArg(args, index, parsed) + 1;
198
+ continue;
199
+ } else if (applyProfileShortcut(arg, parsed)) {
200
+ index += 1;
201
+ continue;
202
+ } else if (arg === '--yes' || arg === '-y') {
203
+ parsed.yes = true;
204
+ parsed.railsConfirmed = true;
205
+ } else if (arg === '--force') {
206
+ parsed.force = true;
207
+ } else if (arg === '--dry-run') {
208
+ parsed.dryRun = true;
209
+ }
210
+ index += 1;
211
+ }
212
+
213
+ return parsed;
214
+ }
215
+
216
+ /** Create one readline prompt interface for a multi-question wizard. */
217
+ function makePromptInterface() {
218
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
219
+ return {
220
+ ask(question) {
221
+ return new Promise((resolve) => {
222
+ rl.question(question, (answer) => {
223
+ resolve(answer);
224
+ });
225
+ });
226
+ },
227
+ close() {
228
+ rl.close();
229
+ },
230
+ };
231
+ }
232
+
233
+ /** Resolve the adoption profile while preserving non-interactive defaults. */
234
+ async function resolveProfile(parsed) {
235
+ if (parsed.profile) {
236
+ return parsed.profile;
237
+ }
238
+ return 'standard';
239
+ }
240
+
241
+ /** Validate a resolved adoption profile name before prompting the user. */
242
+ function validateProfile(profile) {
243
+ if (ADOPTION_PROFILE_NAMES.includes(profile)) {
244
+ return null;
245
+ }
246
+ return `Unknown adoption profile '${profile}'. Available profiles: ${ADOPTION_PROFILE_NAMES.join(', ')}`;
247
+ }
248
+
249
+ /** Return true only when a path exists and is a directory. */
250
+ function isDirectory(filePath) {
251
+ try {
252
+ return fs.statSync(filePath).isDirectory();
253
+ } catch (_err) {
254
+ return false;
255
+ }
256
+ }
257
+
258
+ /** Detect active harnesses from day-one filesystem marker directories. */
259
+ function detectHarnessTargets(projectRoot = process.cwd()) {
260
+ return DETECTABLE_HARNESS_TARGETS.filter(target => isDirectory(path.join(projectRoot, `.${target}`)));
261
+ }
262
+
263
+ /** Read profile-defined harness targets for explicit profile selections. */
264
+ function getProfileHarnessTargets(profile) {
265
+ const targets = buildAdoptionConfig(profile).adapters?.harness?.targets;
266
+ return Array.isArray(targets) ? targets : [];
267
+ }
268
+
269
+ /** Render the empty patch intent scaffold. */
270
+ function renderPatchMd() {
271
+ return [
272
+ '# Forge Patch Intent',
273
+ '',
274
+ '<!--',
275
+ 'This file is intentionally empty for day-one setup.',
276
+ 'Use it later to document local Forge overrides before applying them.',
277
+ '-->',
278
+ '',
279
+ ].join('\n');
280
+ }
281
+
282
+ /** Append an existing file or directory glob to the protected-path manifest. */
283
+ function addExistingPath(entries, projectRoot, protectedPath, reason) {
284
+ const absolutePath = projectRoot ? path.join(projectRoot, protectedPath) : null;
285
+ if (!absolutePath || !fs.existsSync(absolutePath)) {
286
+ return;
287
+ }
288
+ const manifestPath = isDirectory(absolutePath) ? `${protectedPath}/**` : protectedPath;
289
+ entries.push({ path: manifestPath.replace(/\\/g, '/'), reason });
290
+ }
291
+
292
+ /** Add only selected and existing harness paths to the manifest. */
293
+ function addSelectedHarnessPaths(entries, { projectRoot, harnessTargets }) {
294
+ if (!projectRoot) return;
295
+ if (fs.existsSync(path.join(projectRoot, 'AGENTS.md'))) {
296
+ entries.push({ path: 'AGENTS.md', reason: 'Agent workflow instructions' });
297
+ }
298
+
299
+ if (harnessTargets.includes('claude')) {
300
+ addExistingPath(entries, projectRoot, 'CLAUDE.md', 'Claude harness instructions');
301
+ addExistingPath(entries, projectRoot, '.claude', 'Claude harness directory');
302
+ }
303
+ if (harnessTargets.includes('cursor')) {
304
+ addExistingPath(entries, projectRoot, '.cursor', 'Cursor harness directory');
305
+ }
306
+ if (harnessTargets.includes('codex')) {
307
+ addExistingPath(entries, projectRoot, '.codex', 'Codex harness directory');
308
+ }
309
+ }
310
+
311
+ /** Render the protected-path manifest scaffold without enforcing it. */
312
+ function renderProtectedPathsYaml({ classification, harnessTargets, projectRoot = null }) {
313
+ const paths = [
314
+ { path: '.forge/config.yaml', reason: 'Forge runtime configuration' },
315
+ { path: '.forge/patch.md', reason: 'Local patch intent scaffold' },
316
+ { path: '.forge/protected-paths.yaml', reason: 'Protected path manifest scaffold' },
317
+ ];
318
+ addSelectedHarnessPaths(paths, { projectRoot, harnessTargets });
319
+
320
+ return YAML.stringify({
321
+ kind: 'forge.protectedPaths',
322
+ version: 1,
323
+ classification,
324
+ harness: {
325
+ targets: harnessTargets,
326
+ },
327
+ paths,
328
+ });
329
+ }
330
+
331
+ /** Render config.yaml by layering day-one choices over an adoption profile. */
332
+ function renderDayOneConfigYaml({ profile, classification, harnessTargets, railsConfirmed }) {
333
+ const config = YAML.parse(renderAdoptionConfigYaml(profile));
334
+ config.workflow = config.workflow || {};
335
+ config.workflow.classification = { default: classification };
336
+ config.layer1Rails = {
337
+ confirmed: railsConfirmed,
338
+ rails: [
339
+ 'tdd_intent',
340
+ 'secret_scan',
341
+ 'branch_protection',
342
+ 'signed_commits',
343
+ 'schema_integrity',
344
+ ],
345
+ };
346
+ config.adapters = config.adapters || {};
347
+ config.adapters.harness = {
348
+ enabled: harnessTargets.length > 0,
349
+ targets: harnessTargets,
350
+ };
351
+ config.protectedPaths = [
352
+ ...new Set([
353
+ ...(Array.isArray(config.protectedPaths) ? config.protectedPaths : []),
354
+ '.forge/config.yaml',
355
+ '.forge/patch.md',
356
+ '.forge/protected-paths.yaml',
357
+ ]),
358
+ ];
359
+ return YAML.stringify(config);
360
+ }
361
+
362
+ /** Format generated file paths relative to the target project root. */
363
+ function relativeForgePath(projectRoot, filePath) {
364
+ return path.relative(projectRoot, filePath).replace(/\\/g, '/');
365
+ }
366
+
367
+ /** Build the complete set of day-one generated files. */
368
+ function buildGeneratedFiles(projectRoot, choices) {
369
+ const forgeDir = path.join(projectRoot, '.forge');
370
+ return [
371
+ {
372
+ path: path.join(forgeDir, 'config.yaml'),
373
+ content: renderDayOneConfigYaml(choices),
374
+ },
375
+ {
376
+ path: path.join(forgeDir, 'patch.md'),
377
+ content: renderPatchMd(),
378
+ },
379
+ {
380
+ path: path.join(forgeDir, 'protected-paths.yaml'),
381
+ content: renderProtectedPathsYaml({ ...choices, projectRoot }),
382
+ },
383
+ ];
384
+ }
385
+
386
+ /** Filter generated files down to paths that already exist. */
387
+ function existingGeneratedFiles(files) {
388
+ return files.filter(file => fs.existsSync(file.path));
389
+ }
390
+
391
+ /** Build a no-clobber repair message for existing generated files. */
392
+ function formatNoClobberError(projectRoot, files) {
393
+ const names = files.map(file => relativeForgePath(projectRoot, file.path)).join(', ');
394
+ const plural = files.length !== 1;
395
+ return `${names} already ${plural ? 'exist' : 'exists'}. Re-run with --force to overwrite ${plural ? 'them' : 'it'}, or move the existing ${plural ? 'files' : 'file'} and run forge init again.`;
396
+ }
397
+
398
+ /** Resolve the workflow classification from flags, wizard input, or defaults. */
399
+ async function resolveClassificationChoice(parsed, interactive, prompt) {
400
+ if (parsed.classification) {
401
+ return { classification: parsed.classification };
402
+ }
403
+ if (!interactive) {
404
+ return { classification: 'standard' };
405
+ }
406
+
407
+ const answer = await prompt(`Select default classification (${CLASSIFICATIONS.join('/')}), default standard: `);
408
+ setClassification(parsed, answer.trim() || 'standard');
409
+ if (parsed.error) {
410
+ return { error: parsed.error };
411
+ }
412
+ return { classification: parsed.classification };
413
+ }
414
+
415
+ /** Resolve Layer 1 rail confirmation from flags, wizard input, or defaults. */
416
+ async function resolveRailsConfirmation(parsed, interactive, prompt) {
417
+ if (parsed.railsConfirmed) {
418
+ return { railsConfirmed: true };
419
+ }
420
+ if (!interactive) {
421
+ return { railsConfirmed: true };
422
+ }
423
+
424
+ const answer = await prompt('Confirm Layer 1 rails are required for this repo? (Y/n): ');
425
+ const normalized = answer.trim().toLowerCase();
426
+ const railsConfirmed = normalized === '' || normalized === 'y' || normalized === 'yes';
427
+ if (railsConfirmed) {
428
+ return { railsConfirmed: true };
429
+ }
430
+ return {
431
+ error: 'Layer 1 rails must be confirmed before initialization. Re-run forge init and answer yes to continue.',
432
+ };
433
+ }
434
+
435
+ /** Resolve harness targets from flags, filesystem detection, wizard input, or defaults. */
436
+ async function resolveHarnessTargetChoice(parsed, interactive, prompt, projectRoot, profile) {
437
+ if (parsed.harnessTargets) {
438
+ return { harnessTargets: parsed.harnessTargets };
439
+ }
440
+
441
+ const detectedHarnessTargets = detectHarnessTargets(projectRoot);
442
+ const profileHarnessTargets = parsed.profile ? getProfileHarnessTargets(profile) : [];
443
+ const fallbackHarnessTargets = profileHarnessTargets.length > 0 || parsed.profile ? profileHarnessTargets : ['codex'];
444
+ const defaultHarnessTargets = detectedHarnessTargets.length > 0
445
+ ? detectedHarnessTargets
446
+ : fallbackHarnessTargets;
447
+ if (!interactive) {
448
+ return { harnessTargets: defaultHarnessTargets };
449
+ }
450
+
451
+ const defaultValue = defaultHarnessTargets.join(',');
452
+ const answer = await prompt(`Select harness targets (${HARNESS_TARGETS.join(',')}), default ${defaultValue}: `);
453
+ if (answer.trim().length === 0) {
454
+ return { harnessTargets: defaultHarnessTargets };
455
+ }
456
+
457
+ setHarnessTargets(parsed, answer.trim());
458
+ if (parsed.error) {
459
+ return { error: parsed.error };
460
+ }
461
+ return { harnessTargets: parsed.harnessTargets };
462
+ }
463
+
464
+ /** Resolve profile, classification, L1 confirmation, and harness targets. */
465
+ async function resolveChoices(parsed, projectRoot, deps = {}, profile = null) {
466
+ const stdinIsTTY = deps.stdinIsTTY ?? process.stdin.isTTY;
467
+ const interactive = !parsed.yes && stdinIsTTY;
468
+ const promptInterface = !deps.prompt && interactive ? makePromptInterface() : null;
469
+ const prompt = deps.prompt || (promptInterface ? promptInterface.ask : null);
470
+ const resolvedProfile = profile || await resolveProfile(parsed);
471
+
472
+ try {
473
+ const classificationChoice = await resolveClassificationChoice(parsed, interactive, prompt);
474
+ if (classificationChoice.error) {
475
+ return { error: classificationChoice.error };
476
+ }
477
+
478
+ const railsChoice = await resolveRailsConfirmation(parsed, interactive, prompt);
479
+ if (railsChoice.error) {
480
+ return { error: railsChoice.error };
481
+ }
482
+
483
+ const harnessChoice = await resolveHarnessTargetChoice(parsed, interactive, prompt, projectRoot, resolvedProfile);
484
+ if (harnessChoice.error) {
485
+ return { error: harnessChoice.error };
486
+ }
487
+
488
+ return {
489
+ profile: resolvedProfile,
490
+ classification: classificationChoice.classification,
491
+ railsConfirmed: railsChoice.railsConfirmed,
492
+ harnessTargets: harnessChoice.harnessTargets,
493
+ };
494
+ } finally {
495
+ if (promptInterface) {
496
+ promptInterface.close();
497
+ }
498
+ }
499
+ }
500
+
501
+ /** Execute forge init for the target project root. */
502
+ async function handler(args, flags, projectRoot = process.cwd(), deps = {}) {
503
+ const parsed = parseInitFlags(args, flags);
504
+ if (parsed.error) {
505
+ return { success: false, error: parsed.error };
506
+ }
507
+ const profile = await resolveProfile(parsed);
508
+ const profileError = validateProfile(profile);
509
+ if (profileError) {
510
+ return {
511
+ success: false,
512
+ error: profileError,
513
+ };
514
+ }
515
+ const choices = await resolveChoices(parsed, projectRoot, deps, profile);
516
+ if (choices.error) {
517
+ return { success: false, error: choices.error };
518
+ }
519
+ const forgeDir = path.join(projectRoot, '.forge');
520
+ const files = buildGeneratedFiles(projectRoot, choices);
521
+
522
+ if (parsed.dryRun) {
523
+ const previewFile = files[0];
524
+ return {
525
+ success: true,
526
+ ...choices,
527
+ files: [relativeForgePath(projectRoot, previewFile.path)],
528
+ output: previewFile.content,
529
+ };
530
+ }
531
+
532
+ const existingFiles = existingGeneratedFiles(files);
533
+ if (existingFiles.length > 0 && !parsed.force) {
534
+ return {
535
+ success: false,
536
+ error: formatNoClobberError(projectRoot, existingFiles),
537
+ };
538
+ }
539
+
540
+ fs.mkdirSync(forgeDir, { recursive: true });
541
+ for (const file of files) {
542
+ fs.writeFileSync(file.path, file.content, 'utf8');
543
+ }
544
+
545
+ // Close the onboarding loop that a bare `forge init` used to leave open:
546
+ // 1. install git hooks so stage commands are not HOOKS_NOT_ACTIVE-blocked, and
547
+ // 2. import any existing Beads store into the Kernel.
548
+ // Both reuse setup's real implementations (overridable via deps for tests) and
549
+ // degrade to a warning rather than failing init.
550
+ const installHooks = deps.installHooks
551
+ || ((root) => require('./setup').ensureGitHooksInstalled(root));
552
+ const autoMigrateBeads = deps.autoMigrateBeads
553
+ || ((root) => require('./migrate').autoMigrateBeadsIfPresent(root));
554
+ try {
555
+ await installHooks(projectRoot);
556
+ } catch (err) {
557
+ console.warn(`Warning: git hook installation skipped: ${err.message}`);
558
+ }
559
+ try {
560
+ await autoMigrateBeads(projectRoot);
561
+ } catch (err) {
562
+ console.warn(`Warning: Beads → Kernel migration skipped: ${err.message}`);
563
+ }
564
+
565
+ console.log(`Initialized Forge adoption profile '${profile}' at .forge/`);
566
+ console.log(`Classification: ${choices.classification}`);
567
+ console.log(`Harness targets: ${choices.harnessTargets.join(', ')}`);
568
+ console.log('Inspect with: forge options lint && forge options diff');
569
+
570
+ return {
571
+ success: true,
572
+ ...choices,
573
+ files: files.map(file => relativeForgePath(projectRoot, file.path)),
574
+ };
575
+ }
576
+
577
+ module.exports = {
578
+ name: 'init',
579
+ description: 'Initialize Forge adoption config in a fresh repository',
580
+ usage: usage(),
581
+ flags: {
582
+ '--profile <name>': 'Adoption profile: minimal, standard, or full',
583
+ '--minimal': 'Shortcut for --profile minimal --yes',
584
+ '--standard': 'Shortcut for --profile standard --yes',
585
+ '--full': 'Shortcut for --profile full --yes',
586
+ '--classification <name>': 'Default workflow classification: critical, standard, or refactor',
587
+ '--harness <targets>': 'Comma-separated harness targets: claude, cursor, codex',
588
+ '--yes': 'Use the standard profile when no profile is provided',
589
+ '--force': 'Overwrite existing day-one .forge files',
590
+ '--dry-run': 'Print the generated config YAML without writing files',
591
+ },
592
+ handler,
593
+ detectHarnessTargets,
594
+ parseInitFlags,
595
+ renderDayOneConfigYaml,
596
+ renderPatchMd,
597
+ renderProtectedPathsYaml,
598
+ };
@@ -0,0 +1,79 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ analyzeInsights,
5
+ formatInsightsText,
6
+ recordInsightDecision,
7
+ } = require('../insights');
8
+
9
+ const usage = [
10
+ 'Usage: forge insights [--limit N] [--min-count N] [--since YYYY-MM-DD] [--json]',
11
+ ' forge insights accept <candidate-id> [--note text]',
12
+ ' forge insights reject <candidate-id> [--note text]',
13
+ ].join('\n');
14
+
15
+ function readOption(args, name, fallback) {
16
+ const equals = args.find(arg => arg.startsWith(`${name}=`));
17
+ if (equals) return equals.slice(name.length + 1);
18
+ const index = args.indexOf(name);
19
+ if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('-')) return args[index + 1];
20
+ return fallback;
21
+ }
22
+
23
+ function positionals(args) {
24
+ const values = [];
25
+ const flagsWithValues = new Set(['--limit', '--min-count', '--since', '--note', '--path', '-p']);
26
+ for (let index = 0; index < args.length; index++) {
27
+ const arg = args[index];
28
+ if (arg.startsWith('--') && arg.includes('=')) continue;
29
+ if (flagsWithValues.has(arg)) {
30
+ index += 1;
31
+ continue;
32
+ }
33
+ if (arg.startsWith('-')) continue;
34
+ values.push(arg);
35
+ }
36
+ return values;
37
+ }
38
+
39
+ async function handler(args, flags, projectRoot) {
40
+ const commandFlags = flags ?? {};
41
+ const [subcommand, candidateId] = positionals(args);
42
+ if (subcommand === 'accept' || subcommand === 'reject') {
43
+ if (!candidateId) {
44
+ return { success: false, error: usage };
45
+ }
46
+ const status = subcommand === 'accept' ? 'accepted' : 'rejected';
47
+ const decision = recordInsightDecision(projectRoot, candidateId, status, {
48
+ note: readOption(args, '--note', ''),
49
+ memory: commandFlags.memory,
50
+ });
51
+ return {
52
+ success: true,
53
+ output: `Insight ${candidateId} ${status}. Decision recorded at ${decision.key}.\n`,
54
+ };
55
+ }
56
+
57
+ const result = analyzeInsights(projectRoot, {
58
+ limit: readOption(args, '--limit', undefined),
59
+ minCount: readOption(args, '--min-count', undefined),
60
+ since: readOption(args, '--since', undefined),
61
+ });
62
+ if (args.includes('--review-feedback')) {
63
+ result.limitations = [
64
+ 'Compatibility note: --review-feedback now reads Beads interactions and issue evidence; external review-provider comments are not inferred.',
65
+ ...result.limitations,
66
+ ];
67
+ }
68
+ return {
69
+ success: true,
70
+ output: args.includes('--json') ? `${JSON.stringify(result, null, 2)}\n` : formatInsightsText(result),
71
+ };
72
+ }
73
+
74
+ module.exports = {
75
+ name: 'insights',
76
+ description: 'Detect recurring evidence patterns and suggest conservative workflow follow-ups',
77
+ usage,
78
+ handler,
79
+ };
@@ -2,4 +2,15 @@
2
2
 
3
3
  const { createIssueCommand } = require('./_issue');
4
4
 
5
- module.exports = createIssueCommand();
5
+ // Static registry export for `forge issue`. The handler delegates to the issue
6
+ // surface built by createIssueCommand() in _issue.js (called inline so the
7
+ // delegation is visible to the release-readiness surface check), which dispatches
8
+ // each subcommand through the shared backend abstraction.
9
+ module.exports = {
10
+ name: 'issue',
11
+ description: 'Manage issues through the Forge command surface',
12
+ usage: 'forge issue <create|update|claim|release|close|show|list|ready|search|stats|dep> [...]',
13
+ flags: {},
14
+ handler: (args, flags, projectRoot, opts = {}) =>
15
+ createIssueCommand().handler(args, flags, projectRoot, opts),
16
+ };