forge-workflow 0.0.10 → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (468) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +82 -5
  5. package/.forge/hooks/forge-native-hook.js +431 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +709 -0
  9. package/CLAUDE.md +9 -118
  10. package/QUICKSTART.md +175 -0
  11. package/README.md +275 -365
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +532 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/forge/TOOLCHAIN.md +670 -0
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +121 -0
  29. package/docs/guides/SUPPORT.md +190 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +214 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +155 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/activation/ensure-forge-home.js +135 -0
  67. package/lib/adapter-cli.js +307 -0
  68. package/lib/adapters/beads-issue-adapter.js +127 -0
  69. package/lib/adapters/beads-kernel-compat.js +1109 -0
  70. package/lib/adapters/greptile-review-adapter.js +141 -0
  71. package/lib/adapters/kernel-issue-adapter.js +101 -0
  72. package/lib/adapters/pr-state-adapter.js +484 -0
  73. package/lib/adoption-profiles.js +139 -0
  74. package/lib/agents/README.md +2 -6
  75. package/lib/agents/claude.plugin.json +3 -8
  76. package/lib/agents/codex.plugin.json +9 -1
  77. package/lib/agents/cursor.plugin.json +2 -6
  78. package/lib/agents/hermes.plugin.json +22 -0
  79. package/lib/agents-config.js +39 -1236
  80. package/lib/audit-evidence.js +282 -0
  81. package/lib/beads-detect.js +60 -0
  82. package/lib/beads-nudge.js +91 -0
  83. package/lib/beads-setup.js +121 -0
  84. package/lib/beads-sync-scaffold.js +25 -101
  85. package/lib/codex-skills.js +51 -1
  86. package/lib/commands/_aliases.js +248 -0
  87. package/lib/commands/_issue.js +780 -77
  88. package/lib/commands/_manifest.js +93 -0
  89. package/lib/commands/_registry.js +99 -34
  90. package/lib/commands/_resolve-command-opts.js +230 -0
  91. package/lib/commands/_serve-security.js +270 -0
  92. package/lib/commands/adapter.js +12 -0
  93. package/lib/commands/add.js +118 -0
  94. package/lib/commands/audit.js +70 -0
  95. package/lib/commands/blocked.js +5 -0
  96. package/lib/commands/board.js +64 -0
  97. package/lib/commands/claim.js +21 -2
  98. package/lib/commands/claims.js +7 -0
  99. package/lib/commands/clean.js +485 -75
  100. package/lib/commands/close.js +2 -2
  101. package/lib/commands/comment.js +5 -0
  102. package/lib/commands/control.js +148 -0
  103. package/lib/commands/create.js +2 -2
  104. package/lib/commands/dev.js +185 -7
  105. package/lib/commands/doc-gate.js +336 -0
  106. package/lib/commands/doctor.js +156 -0
  107. package/lib/commands/explain.js +15 -0
  108. package/lib/commands/export.js +237 -0
  109. package/lib/commands/gate.js +209 -0
  110. package/lib/commands/hooks.js +377 -0
  111. package/lib/commands/inbox.js +118 -0
  112. package/lib/commands/init.js +604 -0
  113. package/lib/commands/insights.js +79 -0
  114. package/lib/commands/issue.js +12 -1
  115. package/lib/commands/issues.js +17 -0
  116. package/lib/commands/lint.js +5 -0
  117. package/lib/commands/list.js +2 -2
  118. package/lib/commands/memory.js +81 -0
  119. package/lib/commands/merge.js +312 -0
  120. package/lib/commands/migrate.js +362 -0
  121. package/lib/commands/new.js +12 -0
  122. package/lib/commands/options.js +241 -0
  123. package/lib/commands/orient.js +13 -0
  124. package/lib/commands/orphans.js +5 -0
  125. package/lib/commands/patch.js +67 -0
  126. package/lib/commands/plan.js +481 -29
  127. package/lib/commands/pr.js +88 -0
  128. package/lib/commands/preflight.js +211 -0
  129. package/lib/commands/prime.js +13 -0
  130. package/lib/commands/push.js +135 -2
  131. package/lib/commands/ready.js +2 -2
  132. package/lib/commands/recall.js +171 -0
  133. package/lib/commands/recap.js +75 -0
  134. package/lib/commands/recommend.js +0 -1
  135. package/lib/commands/release.js +104 -0
  136. package/lib/commands/remember.js +140 -0
  137. package/lib/commands/role.js +99 -0
  138. package/lib/commands/serve.js +581 -0
  139. package/lib/commands/setup.js +900 -971
  140. package/lib/commands/shepherd.js +501 -0
  141. package/lib/commands/ship.js +59 -1
  142. package/lib/commands/show.js +2 -2
  143. package/lib/commands/stage.js +192 -0
  144. package/lib/commands/stale.js +5 -0
  145. package/lib/commands/status.js +158 -21
  146. package/lib/commands/sync.js +34 -46
  147. package/lib/commands/team.js +4 -1
  148. package/lib/commands/test.js +43 -27
  149. package/lib/commands/update.js +2 -2
  150. package/lib/commands/upgrade.js +47 -0
  151. package/lib/commands/validate.js +43 -18
  152. package/lib/commands/worktree.js +362 -99
  153. package/lib/config-writer.js +202 -0
  154. package/lib/control-plane.js +236 -0
  155. package/lib/core/runtime-graph.js +977 -0
  156. package/lib/dep-guard/keyword-ripple.js +2 -2
  157. package/lib/deprecated-sync-cleanup.js +362 -0
  158. package/lib/detect-agent.js +2 -28
  159. package/lib/detect-worktree.js +35 -9
  160. package/lib/doc-gate/declaration.js +177 -0
  161. package/lib/doc-gate/detect.js +289 -0
  162. package/lib/doc-gate/gate.js +375 -0
  163. package/lib/doc-gate/okf-config.js +128 -0
  164. package/lib/doc-gate/okf.js +429 -0
  165. package/lib/docs-command.js +1161 -6
  166. package/lib/forge-issues.js +382 -11
  167. package/lib/forge-lock.js +262 -0
  168. package/lib/gate-events.js +192 -0
  169. package/lib/global-flags.js +104 -0
  170. package/lib/greptile-match.js +7 -63
  171. package/lib/grounding/context-events.js +230 -0
  172. package/lib/grounding/read-first.js +112 -0
  173. package/lib/harness-capability-matrix.js +380 -0
  174. package/lib/hook-global-installer.js +347 -0
  175. package/lib/hook-renderer.js +541 -0
  176. package/lib/inbox.js +391 -0
  177. package/lib/insights.js +397 -0
  178. package/lib/issue-adapter.js +156 -0
  179. package/lib/issue-backend.js +145 -0
  180. package/lib/issue-render.js +220 -0
  181. package/lib/kernel/backing-issue.js +311 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/owned-kernel.js +43 -0
  192. package/lib/kernel/planning-buckets-schema.js +109 -0
  193. package/lib/kernel/projection-jsonl-writer.js +450 -0
  194. package/lib/kernel/readiness-model.js +329 -0
  195. package/lib/kernel/schema.js +356 -0
  196. package/lib/kernel/sqlite-driver.js +2540 -0
  197. package/lib/kernel/taxonomy-validator.js +394 -0
  198. package/lib/lefthook-check.js +3 -2
  199. package/lib/lefthook-wiring.js +413 -0
  200. package/lib/mcp-config-renderer.js +288 -0
  201. package/lib/memory/graphiti-mcp.js +106 -0
  202. package/lib/memory/router.js +387 -0
  203. package/lib/memory/typed-api.js +102 -0
  204. package/lib/memory-digest.js +195 -0
  205. package/lib/merge-rules.js +395 -0
  206. package/lib/migrate-dry-run.js +466 -0
  207. package/lib/orientation.js +863 -0
  208. package/lib/package-manager-remediation.js +103 -0
  209. package/lib/package-root.js +381 -0
  210. package/lib/patch-intent.js +890 -0
  211. package/lib/plugin-catalog.js +3 -4
  212. package/lib/plugin-manager.js +0 -5
  213. package/lib/pr-bundle.js +186 -0
  214. package/lib/pr-monitor/auto-actions.js +175 -0
  215. package/lib/pr-monitor/differ.js +195 -0
  216. package/lib/pr-monitor/digest.js +206 -0
  217. package/lib/pr-monitor/events.js +0 -0
  218. package/lib/pr-monitor/gather.js +124 -0
  219. package/lib/pr-monitor/journal.js +299 -0
  220. package/lib/pr-monitor/monitor.js +146 -0
  221. package/lib/pr-monitor/render-sticky.js +192 -0
  222. package/lib/pr-monitor/upsert-sticky.js +169 -0
  223. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  224. package/lib/pr-monitor/watch.js +247 -0
  225. package/lib/pr-pull.js +1314 -0
  226. package/lib/pr-shepherd.js +494 -0
  227. package/lib/pr-state-validator.js +59 -0
  228. package/lib/preflight/gates.js +237 -0
  229. package/lib/preflight/runner.js +116 -0
  230. package/lib/project-discovery.js +0 -53
  231. package/lib/project-memory.js +99 -497
  232. package/lib/protected-path-manifest.js +281 -0
  233. package/lib/protected-state-surfaces.js +387 -0
  234. package/lib/release-readiness.js +2105 -0
  235. package/lib/reset.js +59 -45
  236. package/lib/review-adapter.js +68 -0
  237. package/lib/rules-sync.js +260 -0
  238. package/lib/runtime-health.js +241 -20
  239. package/lib/safety-config-renderer.js +268 -0
  240. package/lib/setup-action-log.js +1 -7
  241. package/lib/setup.js +27 -65
  242. package/lib/shell-utils.js +76 -6
  243. package/lib/skills-sync.js +330 -0
  244. package/lib/smart-status/scoring.js +17 -3
  245. package/lib/status/beads-snapshot.js +45 -2
  246. package/lib/status/presenter.js +169 -18
  247. package/lib/status/snapshot.js +186 -0
  248. package/lib/sync-backend.js +202 -0
  249. package/lib/untrusted-content.js +52 -0
  250. package/lib/upgrade-safety.js +251 -0
  251. package/lib/workflow/enforce-stage.js +351 -45
  252. package/lib/workflow/stage-transition.js +115 -0
  253. package/lib/workflow/stages.js +30 -6
  254. package/lib/workflow/state-manager.js +11 -22
  255. package/lib/workflow/state.js +23 -1
  256. package/lib/workflow-profiles.js +17 -5
  257. package/package.json +37 -35
  258. package/rules/documentation.md +19 -0
  259. package/rules/kernel-tracking.md +26 -0
  260. package/rules/security.md +22 -0
  261. package/rules/tdd.md +20 -0
  262. package/rules/workflow.md +27 -0
  263. package/scripts/auto-backing-issue.js +47 -0
  264. package/scripts/beads-context.sh +81 -57
  265. package/scripts/beads-upgrade-smoke.sh +24 -3
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard.sh +22 -3
  272. package/scripts/file-index.sh +3 -0
  273. package/scripts/forge-team/lib/claim.sh +34 -18
  274. package/scripts/forge-team/lib/dashboard.sh +61 -86
  275. package/scripts/forge-team/lib/epic.sh +99 -263
  276. package/scripts/forge-team/lib/hooks.sh +26 -28
  277. package/scripts/forge-team/lib/identity.sh +4 -4
  278. package/scripts/forge-team/lib/sync-github.sh +49 -84
  279. package/scripts/forge-team/lib/verify.sh +93 -83
  280. package/scripts/forge-team/lib/workload.sh +41 -65
  281. package/scripts/forge-team/tests/claim.test.sh +25 -19
  282. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  283. package/scripts/forge-team/tests/epic.test.sh +52 -71
  284. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  285. package/scripts/forge-team/tests/identity.test.sh +3 -3
  286. package/scripts/forge-team/tests/integration.test.sh +44 -66
  287. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  288. package/scripts/forge-team/tests/verify.test.sh +37 -46
  289. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  290. package/scripts/forge-team/tests/workload.test.sh +32 -66
  291. package/scripts/gen-command-manifest.js +153 -0
  292. package/scripts/gen-embedded-assets.mjs +129 -0
  293. package/scripts/install.ps1 +139 -0
  294. package/scripts/install.sh +268 -0
  295. package/scripts/lib/release-asset.mjs +84 -0
  296. package/scripts/parity-check.mjs +145 -0
  297. package/scripts/parity-check.test.mjs +58 -0
  298. package/scripts/pin-agentic-workflow-images.js +112 -0
  299. package/scripts/pr-auto-actions.js +93 -0
  300. package/scripts/pr-coordinator.sh +3 -0
  301. package/scripts/pr-verdict-label.js +50 -0
  302. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  303. package/scripts/preflight.sh +21 -94
  304. package/scripts/protected-state-check.js +104 -0
  305. package/scripts/smart-status.sh +60 -57
  306. package/scripts/spikes/config-race-bench.js +111 -0
  307. package/scripts/spikes/harness-capability-matrix.js +13 -0
  308. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  309. package/scripts/spikes/protected-path-manifest.js +20 -0
  310. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  311. package/scripts/sync-agent-skills.js +62 -0
  312. package/scripts/sync-utils.sh +3 -0
  313. package/scripts/test-ci-shard.js +13 -6
  314. package/scripts/test.js +95 -12
  315. package/skills/claim-safety/SKILL.md +102 -0
  316. package/skills/claim-safety/evals/evals.json +46 -0
  317. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  318. package/skills/dev/evals/evals.json +50 -0
  319. package/skills/hermes-forge/SKILL.md +185 -0
  320. package/skills/hermes-forge/evals/evals.json +46 -0
  321. package/skills/issue-basics/SKILL.md +111 -0
  322. package/skills/issue-basics/evals/evals.json +46 -0
  323. package/skills/kernel/SKILL.md +166 -0
  324. package/skills/kernel/evals/evals.json +50 -0
  325. package/skills/memory/SKILL.md +102 -0
  326. package/skills/parallel-deep-research/SKILL.md +14 -11
  327. package/skills/parallel-deep-research/evals/evals.json +11 -27
  328. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  329. package/skills/plan/evals/evals.json +42 -0
  330. package/skills/research/SKILL.md +195 -0
  331. package/skills/research/evals/evals.json +42 -0
  332. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  333. package/skills/review/evals/evals.json +42 -0
  334. package/skills/rollback/SKILL.md +110 -0
  335. package/skills/rollback/evals/evals.json +46 -0
  336. package/skills/rollback/references/methods.md +204 -0
  337. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  338. package/skills/shepherd/SKILL.md +66 -0
  339. package/skills/shepherd/evals/evals.json +42 -0
  340. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  341. package/skills/ship/evals/evals.json +42 -0
  342. package/skills/smith/SKILL.md +142 -0
  343. package/skills/smith/evals/evals.json +46 -0
  344. package/skills/smith/references/autonomy-and-gates.md +94 -0
  345. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  346. package/skills/sonarcloud/evals/evals.json +46 -0
  347. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  348. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  349. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  350. package/skills/status/evals/evals.json +50 -0
  351. package/skills/triage-ready/SKILL.md +121 -0
  352. package/skills/triage-ready/evals/evals.json +42 -0
  353. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  354. package/skills/validate/evals/evals.json +42 -0
  355. package/skills/verify/SKILL.md +299 -0
  356. package/skills/verify/evals/evals.json +50 -0
  357. package/.claude/commands/dev.md +0 -345
  358. package/.claude/commands/plan.md +0 -566
  359. package/.claude/commands/premerge.md +0 -186
  360. package/.claude/commands/research.md +0 -42
  361. package/.claude/commands/review.md +0 -451
  362. package/.claude/commands/rollback.md +0 -721
  363. package/.claude/commands/ship.md +0 -213
  364. package/.claude/commands/sonarcloud.md +0 -152
  365. package/.claude/commands/status.md +0 -90
  366. package/.claude/commands/validate.md +0 -288
  367. package/.claude/commands/verify.md +0 -269
  368. package/.claude/rules/workflow.md +0 -121
  369. package/.cline/workflows/dev.md +0 -342
  370. package/.cline/workflows/plan.md +0 -563
  371. package/.cline/workflows/premerge.md +0 -183
  372. package/.cline/workflows/research.md +0 -39
  373. package/.cline/workflows/review.md +0 -448
  374. package/.cline/workflows/rollback.md +0 -718
  375. package/.cline/workflows/ship.md +0 -210
  376. package/.cline/workflows/sonarcloud.md +0 -146
  377. package/.cline/workflows/status.md +0 -87
  378. package/.cline/workflows/validate.md +0 -285
  379. package/.cline/workflows/verify.md +0 -266
  380. package/.codex/config.toml +0 -11
  381. package/.codex/skills/dev/SKILL.md +0 -345
  382. package/.codex/skills/plan/SKILL.md +0 -566
  383. package/.codex/skills/premerge/SKILL.md +0 -186
  384. package/.codex/skills/research/SKILL.md +0 -42
  385. package/.codex/skills/review/SKILL.md +0 -451
  386. package/.codex/skills/rollback/SKILL.md +0 -721
  387. package/.codex/skills/ship/SKILL.md +0 -213
  388. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  389. package/.codex/skills/status/SKILL.md +0 -90
  390. package/.codex/skills/validate/SKILL.md +0 -288
  391. package/.codex/skills/verify/SKILL.md +0 -269
  392. package/.cursor/commands/dev.md +0 -342
  393. package/.cursor/commands/plan.md +0 -563
  394. package/.cursor/commands/premerge.md +0 -183
  395. package/.cursor/commands/research.md +0 -39
  396. package/.cursor/commands/review.md +0 -448
  397. package/.cursor/commands/ship.md +0 -210
  398. package/.cursor/commands/sonarcloud.md +0 -146
  399. package/.cursor/commands/status.md +0 -87
  400. package/.cursor/commands/validate.md +0 -285
  401. package/.cursor/commands/verify.md +0 -266
  402. package/.cursorrules +0 -149
  403. package/.github/prompts/premerge.prompt.md +0 -188
  404. package/.github/prompts/research.prompt.md +0 -44
  405. package/.github/prompts/rollback.prompt.md +0 -723
  406. package/.github/prompts/verify.prompt.md +0 -271
  407. package/.github/workflows/beads-to-github.yml +0 -89
  408. package/.github/workflows/github-to-beads.yml +0 -100
  409. package/.kilocode/workflows/dev.md +0 -346
  410. package/.kilocode/workflows/plan.md +0 -567
  411. package/.kilocode/workflows/premerge.md +0 -187
  412. package/.kilocode/workflows/research.md +0 -43
  413. package/.kilocode/workflows/review.md +0 -452
  414. package/.kilocode/workflows/rollback.md +0 -722
  415. package/.kilocode/workflows/ship.md +0 -214
  416. package/.kilocode/workflows/sonarcloud.md +0 -150
  417. package/.kilocode/workflows/status.md +0 -91
  418. package/.kilocode/workflows/validate.md +0 -289
  419. package/.kilocode/workflows/verify.md +0 -270
  420. package/.opencode/commands/dev.md +0 -345
  421. package/.opencode/commands/plan.md +0 -566
  422. package/.opencode/commands/premerge.md +0 -186
  423. package/.opencode/commands/research.md +0 -42
  424. package/.opencode/commands/review.md +0 -451
  425. package/.opencode/commands/rollback.md +0 -721
  426. package/.opencode/commands/ship.md +0 -213
  427. package/.opencode/commands/sonarcloud.md +0 -149
  428. package/.opencode/commands/status.md +0 -90
  429. package/.opencode/commands/validate.md +0 -288
  430. package/.opencode/commands/verify.md +0 -269
  431. package/.roo/commands/dev.md +0 -346
  432. package/.roo/commands/plan.md +0 -567
  433. package/.roo/commands/premerge.md +0 -187
  434. package/.roo/commands/research.md +0 -43
  435. package/.roo/commands/review.md +0 -452
  436. package/.roo/commands/rollback.md +0 -722
  437. package/.roo/commands/ship.md +0 -214
  438. package/.roo/commands/sonarcloud.md +0 -150
  439. package/.roo/commands/status.md +0 -91
  440. package/.roo/commands/validate.md +0 -289
  441. package/.roo/commands/verify.md +0 -270
  442. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  443. package/docs/GREPTILE_SETUP.md +0 -400
  444. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  445. package/docs/SETUP.md +0 -663
  446. package/docs/VALIDATION.md +0 -363
  447. package/lib/agents/cline.plugin.json +0 -29
  448. package/lib/agents/copilot.plugin.json +0 -24
  449. package/lib/agents/kilocode.plugin.json +0 -22
  450. package/lib/agents/opencode.plugin.json +0 -23
  451. package/lib/agents/roo.plugin.json +0 -30
  452. package/lib/beads-bootstrap.js +0 -225
  453. package/lib/beads-health-check.js +0 -188
  454. package/lib/commands/commands-reset.js +0 -147
  455. package/opencode.json +0 -67
  456. package/scripts/beads-context.test.js +0 -584
  457. package/scripts/github-beads-sync/comment.mjs +0 -64
  458. package/scripts/github-beads-sync/config.mjs +0 -148
  459. package/scripts/github-beads-sync/github-api.mjs +0 -131
  460. package/scripts/github-beads-sync/index.mjs +0 -356
  461. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  462. package/scripts/github-beads-sync/mapping.mjs +0 -132
  463. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  464. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  465. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  466. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  467. package/scripts/github-beads-sync.config.json +0 -26
  468. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,604 @@
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
+ // The immutable Layer 1 floor: locked rails that cannot be toggled off.
339
+ rails: [
340
+ 'secret_scan',
341
+ 'branch_protection',
342
+ 'signed_commits',
343
+ 'schema_integrity',
344
+ ],
345
+ // tdd_intent is a strong DEFAULT rail, not an immutable L1 floor: it is unlocked
346
+ // (locked:false) and honestly toggleable via `forge gate disable rail.tdd_intent`
347
+ // (the minimal profile ships it off). Listed separately so this display never
348
+ // implies it is a non-negotiable Layer 1 rail (issue eda6d866).
349
+ toggleableRails: ['tdd_intent'],
350
+ };
351
+ config.adapters = config.adapters || {};
352
+ config.adapters.harness = {
353
+ enabled: harnessTargets.length > 0,
354
+ targets: harnessTargets,
355
+ };
356
+ // The adoption profile is authoritative for whether protected-path enforcement runs
357
+ // at all. When it resolves EMPTY (the `minimal` profile = zero active enforcement),
358
+ // the protected-path guard must stay genuinely inert — do NOT re-add the day-one
359
+ // paths, or `init --minimal` would silently reactivate the guard the profile just
360
+ // disabled (issue eda6d866). Standard/full keep their profile paths + the additions.
361
+ const profilePaths = Array.isArray(config.protectedPaths) ? config.protectedPaths : [];
362
+ config.protectedPaths = profilePaths.length === 0
363
+ ? []
364
+ : [
365
+ ...new Set([
366
+ ...profilePaths,
367
+ '.forge/config.yaml',
368
+ '.forge/patch.md',
369
+ '.forge/protected-paths.yaml',
370
+ ]),
371
+ ];
372
+ return YAML.stringify(config);
373
+ }
374
+
375
+ /** Format generated file paths relative to the target project root. */
376
+ function relativeForgePath(projectRoot, filePath) {
377
+ return path.relative(projectRoot, filePath).replace(/\\/g, '/');
378
+ }
379
+
380
+ /** Build the complete set of day-one generated files. */
381
+ function buildGeneratedFiles(projectRoot, choices) {
382
+ const forgeDir = path.join(projectRoot, '.forge');
383
+ return [
384
+ {
385
+ path: path.join(forgeDir, 'config.yaml'),
386
+ content: renderDayOneConfigYaml(choices),
387
+ },
388
+ {
389
+ path: path.join(forgeDir, 'patch.md'),
390
+ content: renderPatchMd(),
391
+ },
392
+ {
393
+ path: path.join(forgeDir, 'protected-paths.yaml'),
394
+ content: renderProtectedPathsYaml({ ...choices, projectRoot }),
395
+ },
396
+ ];
397
+ }
398
+
399
+ /** Filter generated files down to paths that already exist. */
400
+ function existingGeneratedFiles(files) {
401
+ return files.filter(file => fs.existsSync(file.path));
402
+ }
403
+
404
+ /** Build a no-clobber repair message for existing generated files. */
405
+ function formatNoClobberError(projectRoot, files) {
406
+ const names = files.map(file => relativeForgePath(projectRoot, file.path)).join(', ');
407
+ const plural = files.length !== 1;
408
+ 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.`;
409
+ }
410
+
411
+ /** Resolve the workflow classification from flags, wizard input, or defaults. */
412
+ async function resolveClassificationChoice(parsed, interactive, prompt) {
413
+ if (parsed.classification) {
414
+ return { classification: parsed.classification };
415
+ }
416
+ if (!interactive) {
417
+ return { classification: 'standard' };
418
+ }
419
+
420
+ const answer = await prompt(`Select default classification (${CLASSIFICATIONS.join('/')}), default standard: `);
421
+ setClassification(parsed, answer.trim() || 'standard');
422
+ if (parsed.error) {
423
+ return { error: parsed.error };
424
+ }
425
+ return { classification: parsed.classification };
426
+ }
427
+
428
+ /** Resolve Layer 1 rail confirmation from flags, wizard input, or defaults. */
429
+ async function resolveRailsConfirmation(parsed, interactive, prompt) {
430
+ if (parsed.railsConfirmed) {
431
+ return { railsConfirmed: true };
432
+ }
433
+ if (!interactive) {
434
+ return { railsConfirmed: true };
435
+ }
436
+
437
+ const answer = await prompt('Confirm Layer 1 rails are required for this repo? (Y/n): ');
438
+ const normalized = answer.trim().toLowerCase();
439
+ const railsConfirmed = normalized === '' || normalized === 'y' || normalized === 'yes';
440
+ if (railsConfirmed) {
441
+ return { railsConfirmed: true };
442
+ }
443
+ return {
444
+ error: 'Layer 1 rails must be confirmed before initialization. Re-run forge init and answer yes to continue.',
445
+ };
446
+ }
447
+
448
+ /** Resolve harness targets from flags, filesystem detection, wizard input, or defaults. */
449
+ async function resolveHarnessTargetChoice(parsed, interactive, prompt, projectRoot, profile) {
450
+ if (parsed.harnessTargets) {
451
+ return { harnessTargets: parsed.harnessTargets };
452
+ }
453
+
454
+ const detectedHarnessTargets = detectHarnessTargets(projectRoot);
455
+ const profileHarnessTargets = parsed.profile ? getProfileHarnessTargets(profile) : [];
456
+ const fallbackHarnessTargets = profileHarnessTargets.length > 0 || parsed.profile ? profileHarnessTargets : ['codex'];
457
+ const defaultHarnessTargets = detectedHarnessTargets.length > 0
458
+ ? detectedHarnessTargets
459
+ : fallbackHarnessTargets;
460
+ if (!interactive) {
461
+ return { harnessTargets: defaultHarnessTargets };
462
+ }
463
+
464
+ const defaultValue = defaultHarnessTargets.join(',');
465
+ const answer = await prompt(`Select harness targets (${HARNESS_TARGETS.join(',')}), default ${defaultValue}: `);
466
+ if (answer.trim().length === 0) {
467
+ return { harnessTargets: defaultHarnessTargets };
468
+ }
469
+
470
+ setHarnessTargets(parsed, answer.trim());
471
+ if (parsed.error) {
472
+ return { error: parsed.error };
473
+ }
474
+ return { harnessTargets: parsed.harnessTargets };
475
+ }
476
+
477
+ /** Resolve profile, classification, L1 confirmation, and harness targets. */
478
+ async function resolveChoices(parsed, projectRoot, deps = {}, profile = null) {
479
+ const stdinIsTTY = deps.stdinIsTTY ?? process.stdin.isTTY;
480
+ const interactive = !parsed.yes && stdinIsTTY;
481
+ const promptInterface = !deps.prompt && interactive ? makePromptInterface() : null;
482
+ const prompt = deps.prompt || (promptInterface ? promptInterface.ask : null);
483
+ const resolvedProfile = profile || await resolveProfile(parsed);
484
+
485
+ try {
486
+ const classificationChoice = await resolveClassificationChoice(parsed, interactive, prompt);
487
+ if (classificationChoice.error) {
488
+ return { error: classificationChoice.error };
489
+ }
490
+
491
+ const railsChoice = await resolveRailsConfirmation(parsed, interactive, prompt);
492
+ if (railsChoice.error) {
493
+ return { error: railsChoice.error };
494
+ }
495
+
496
+ const harnessChoice = await resolveHarnessTargetChoice(parsed, interactive, prompt, projectRoot, resolvedProfile);
497
+ if (harnessChoice.error) {
498
+ return { error: harnessChoice.error };
499
+ }
500
+
501
+ return {
502
+ profile: resolvedProfile,
503
+ classification: classificationChoice.classification,
504
+ railsConfirmed: railsChoice.railsConfirmed,
505
+ harnessTargets: harnessChoice.harnessTargets,
506
+ };
507
+ } finally {
508
+ if (promptInterface) {
509
+ promptInterface.close();
510
+ }
511
+ }
512
+ }
513
+
514
+ /** Execute forge init for the target project root. */
515
+ async function handler(args, flags, projectRoot = process.cwd(), deps = {}) {
516
+ const parsed = parseInitFlags(args, flags);
517
+ if (parsed.error) {
518
+ return { success: false, error: parsed.error };
519
+ }
520
+ const profile = await resolveProfile(parsed);
521
+ const profileError = validateProfile(profile);
522
+ if (profileError) {
523
+ return {
524
+ success: false,
525
+ error: profileError,
526
+ };
527
+ }
528
+ const choices = await resolveChoices(parsed, projectRoot, deps, profile);
529
+ if (choices.error) {
530
+ return { success: false, error: choices.error };
531
+ }
532
+ const forgeDir = path.join(projectRoot, '.forge');
533
+ const files = buildGeneratedFiles(projectRoot, choices);
534
+
535
+ if (parsed.dryRun) {
536
+ const previewFile = files[0];
537
+ return {
538
+ success: true,
539
+ ...choices,
540
+ files: [relativeForgePath(projectRoot, previewFile.path)],
541
+ output: previewFile.content,
542
+ };
543
+ }
544
+
545
+ const existingFiles = existingGeneratedFiles(files);
546
+ if (existingFiles.length > 0 && !parsed.force) {
547
+ return {
548
+ success: false,
549
+ error: formatNoClobberError(projectRoot, existingFiles),
550
+ };
551
+ }
552
+
553
+ fs.mkdirSync(forgeDir, { recursive: true });
554
+ for (const file of files) {
555
+ fs.writeFileSync(file.path, file.content, 'utf8');
556
+ }
557
+
558
+ // Close the onboarding loop that a bare `forge init` used to leave open:
559
+ // install git hooks so stage commands are not HOOKS_NOT_ACTIVE-blocked. Reuses
560
+ // setup's real implementation (overridable via deps for tests) and degrades to a
561
+ // warning rather than failing init. Beads → Kernel transfer is NOT done here: it
562
+ // is explicit-only via `forge migrate --from beads` (a7e1443c), never implicit.
563
+ const installHooks = deps.installHooks
564
+ || ((root) => require('./setup').ensureGitHooksInstalled(root));
565
+ try {
566
+ await installHooks(projectRoot);
567
+ } catch (err) {
568
+ console.warn(`Warning: git hook installation skipped: ${err.message}`);
569
+ }
570
+
571
+ console.log(`Initialized Forge adoption profile '${profile}' at .forge/`);
572
+ console.log(`Classification: ${choices.classification}`);
573
+ console.log(`Harness targets: ${choices.harnessTargets.join(', ')}`);
574
+ console.log('Inspect with: forge options lint && forge options diff');
575
+
576
+ return {
577
+ success: true,
578
+ ...choices,
579
+ files: files.map(file => relativeForgePath(projectRoot, file.path)),
580
+ };
581
+ }
582
+
583
+ module.exports = {
584
+ name: 'init',
585
+ description: 'Initialize Forge adoption config in a fresh repository',
586
+ usage: usage(),
587
+ flags: {
588
+ '--profile <name>': 'Adoption profile: minimal, standard, or full',
589
+ '--minimal': 'Shortcut for --profile minimal --yes',
590
+ '--standard': 'Shortcut for --profile standard --yes',
591
+ '--full': 'Shortcut for --profile full --yes',
592
+ '--classification <name>': 'Default workflow classification: critical, standard, or refactor',
593
+ '--harness <targets>': 'Comma-separated harness targets: claude, cursor, codex',
594
+ '--yes': 'Use the standard profile when no profile is provided',
595
+ '--force': 'Overwrite existing day-one .forge files',
596
+ '--dry-run': 'Print the generated config YAML without writing files',
597
+ },
598
+ handler,
599
+ detectHarnessTargets,
600
+ parseInitFlags,
601
+ renderDayOneConfigYaml,
602
+ renderPatchMd,
603
+ renderProtectedPathsYaml,
604
+ };
@@ -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
+ };