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,262 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('node:crypto');
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+
7
+ const LOCKFILE_NAME = 'forge.lock';
8
+ const AUDIT_LOG_PATH = path.join('.forge', 'log.jsonl');
9
+ const REMOTE_SOURCE_PATTERN = /^(?:https?:|gh:|gist:|npm:)/i;
10
+
11
+ function toPosixPath(value) {
12
+ return String(value || '').replaceAll('\\', '/');
13
+ }
14
+
15
+ function normalizeRelativePath(projectRoot, fullPath) {
16
+ return toPosixPath(path.relative(projectRoot, fullPath));
17
+ }
18
+
19
+ function isInsideRoot(projectRoot, fullPath) {
20
+ const relative = path.relative(projectRoot, fullPath);
21
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
22
+ }
23
+
24
+ function lockPath(projectRoot) {
25
+ return path.join(projectRoot, LOCKFILE_NAME);
26
+ }
27
+
28
+ function defaultLock() {
29
+ return {
30
+ version: 1,
31
+ generatedBy: 'forge',
32
+ extensions: [],
33
+ };
34
+ }
35
+
36
+ function readForgeLock(projectRoot) {
37
+ const filePath = lockPath(projectRoot);
38
+ if (!fs.existsSync(filePath)) return defaultLock();
39
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
40
+ return {
41
+ ...defaultLock(),
42
+ ...parsed,
43
+ extensions: Array.isArray(parsed.extensions) ? parsed.extensions : [],
44
+ };
45
+ }
46
+
47
+ function writeForgeLock(projectRoot, lock) {
48
+ fs.writeFileSync(lockPath(projectRoot), `${JSON.stringify(lock, null, 2)}\n`, 'utf8');
49
+ }
50
+
51
+ function appendAuditEvent(projectRoot, event) {
52
+ const logPath = path.join(projectRoot, AUDIT_LOG_PATH);
53
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
54
+ fs.appendFileSync(logPath, `${JSON.stringify({
55
+ kind: 'forge.lock',
56
+ schemaVersion: 1,
57
+ recordedAt: new Date().toISOString(),
58
+ ...event,
59
+ })}\n`, 'utf8');
60
+ }
61
+
62
+ function isRemoteSource(source) {
63
+ return REMOTE_SOURCE_PATTERN.test(String(source || ''));
64
+ }
65
+
66
+ function resolveLocalSource(projectRoot, source) {
67
+ const root = path.resolve(projectRoot);
68
+ const resolved = path.resolve(root, source);
69
+ if (!isInsideRoot(root, resolved)) {
70
+ throw new Error(`Source must stay inside project root: ${source}`);
71
+ }
72
+ if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
73
+ throw new Error(`Local source does not exist: ${source}`);
74
+ }
75
+ const realRoot = fs.realpathSync(root);
76
+ const realResolved = fs.realpathSync(resolved);
77
+ if (!isInsideRoot(realRoot, realResolved)) {
78
+ throw new Error(`Source must stay inside project root: ${source}`);
79
+ }
80
+ if (!fs.statSync(realResolved).isFile()) {
81
+ throw new Error(`Local source does not exist: ${source}`);
82
+ }
83
+ return realResolved;
84
+ }
85
+
86
+ function sourceIntegrity(filePath) {
87
+ const digest = crypto
88
+ .createHash('sha512')
89
+ .update(fs.readFileSync(filePath))
90
+ .digest('base64');
91
+ return `sha512-${digest}`;
92
+ }
93
+
94
+ function verifyUnsupportedRemoteEntry(entry) {
95
+ if (!isRemoteSource(entry.source)) {
96
+ return {
97
+ name: entry.name,
98
+ status: 'fail',
99
+ reason: 'unsupported remote verification requires a remote source locator',
100
+ };
101
+ }
102
+ if (entry.resolvedPath || entry.integrity || entry.trust?.allowUntrusted !== true) {
103
+ return {
104
+ name: entry.name,
105
+ status: 'fail',
106
+ reason: 'unsupported remote entry has inconsistent lock metadata',
107
+ };
108
+ }
109
+ return {
110
+ name: entry.name,
111
+ status: 'warn',
112
+ reason: 'remote source integrity cannot be rechecked by this foundation',
113
+ };
114
+ }
115
+
116
+ function classifySource(projectRoot, source, options = {}) {
117
+ if (!source) {
118
+ throw new Error('Source is required');
119
+ }
120
+
121
+ if (isRemoteSource(source)) {
122
+ if (!options.allowUntrusted) {
123
+ throw new Error(`Untrusted source refused: ${source}. Re-run with --allow-untrusted to record it explicitly.`);
124
+ }
125
+ return {
126
+ trusted: false,
127
+ allowUntrusted: true,
128
+ resolvedPath: null,
129
+ integrity: null,
130
+ verification: 'unsupported-remote',
131
+ reason: 'remote locator requires explicit --allow-untrusted',
132
+ };
133
+ }
134
+
135
+ const resolvedPath = resolveLocalSource(projectRoot, source);
136
+ return {
137
+ trusted: true,
138
+ allowUntrusted: false,
139
+ resolvedPath,
140
+ integrity: sourceIntegrity(resolvedPath),
141
+ verification: 'sri',
142
+ reason: 'local file inside project root',
143
+ };
144
+ }
145
+
146
+ function upsertExtension(extensions, entry) {
147
+ const next = extensions.filter(existing => existing.name !== entry.name);
148
+ next.push(entry);
149
+ return next.sort((a, b) => a.name.localeCompare(b.name));
150
+ }
151
+
152
+ function addLockEntry(projectRoot, options) {
153
+ const root = path.resolve(projectRoot);
154
+ const realRoot = fs.realpathSync(root);
155
+ const sourceInfo = classifySource(root, options.source, {
156
+ allowUntrusted: Boolean(options.allowUntrusted),
157
+ });
158
+ const lock = readForgeLock(root);
159
+ const entry = {
160
+ name: options.name,
161
+ source: options.source,
162
+ resolvedPath: sourceInfo.resolvedPath ? normalizeRelativePath(realRoot, sourceInfo.resolvedPath) : null,
163
+ integrity: sourceInfo.integrity,
164
+ verification: sourceInfo.verification,
165
+ trust: {
166
+ trusted: sourceInfo.trusted,
167
+ allowUntrusted: sourceInfo.allowUntrusted,
168
+ reason: sourceInfo.reason,
169
+ },
170
+ lockedAt: new Date().toISOString(),
171
+ };
172
+
173
+ lock.extensions = upsertExtension(lock.extensions, entry);
174
+ writeForgeLock(root, lock);
175
+ appendAuditEvent(root, {
176
+ action: 'add',
177
+ name: entry.name,
178
+ source: entry.source,
179
+ trusted: entry.trust.trusted,
180
+ verification: entry.verification,
181
+ integrity: entry.integrity,
182
+ });
183
+
184
+ return { lock, entry };
185
+ }
186
+
187
+ function verifyEntry(projectRoot, entry) {
188
+ if (entry.verification === 'unsupported-remote') {
189
+ return verifyUnsupportedRemoteEntry(entry);
190
+ }
191
+ if (isRemoteSource(entry.source) || isRemoteSource(entry.resolvedPath)) {
192
+ return {
193
+ name: entry.name,
194
+ status: 'fail',
195
+ reason: 'remote source locator requires unsupported-remote verification policy',
196
+ };
197
+ }
198
+
199
+ const relativePath = entry.resolvedPath || entry.source;
200
+ const root = path.resolve(projectRoot);
201
+ const realRoot = fs.realpathSync(root);
202
+ const fullPath = path.resolve(realRoot, relativePath);
203
+ if (!isInsideRoot(realRoot, fullPath)) {
204
+ return {
205
+ name: entry.name,
206
+ status: 'fail',
207
+ reason: `source escapes project root: ${relativePath}`,
208
+ };
209
+ }
210
+ if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
211
+ return {
212
+ name: entry.name,
213
+ status: 'fail',
214
+ reason: `source missing: ${relativePath}`,
215
+ };
216
+ }
217
+ const realFullPath = fs.realpathSync(fullPath);
218
+ if (!isInsideRoot(realRoot, realFullPath)) {
219
+ return {
220
+ name: entry.name,
221
+ status: 'fail',
222
+ reason: `source escapes project root: ${relativePath}`,
223
+ };
224
+ }
225
+
226
+ const actual = sourceIntegrity(realFullPath);
227
+ if (actual !== entry.integrity) {
228
+ return {
229
+ name: entry.name,
230
+ status: 'fail',
231
+ reason: `integrity mismatch: expected ${entry.integrity}, got ${actual}`,
232
+ };
233
+ }
234
+
235
+ return {
236
+ name: entry.name,
237
+ status: 'pass',
238
+ reason: 'integrity verified',
239
+ };
240
+ }
241
+
242
+ function verifyForgeLock(projectRoot) {
243
+ const lock = readForgeLock(projectRoot);
244
+ const results = lock.extensions.map(entry => verifyEntry(projectRoot, entry));
245
+ return {
246
+ ok: results.every(result => result.status !== 'fail'),
247
+ path: LOCKFILE_NAME,
248
+ results,
249
+ lock,
250
+ };
251
+ }
252
+
253
+ module.exports = {
254
+ LOCKFILE_NAME,
255
+ AUDIT_LOG_PATH,
256
+ addLockEntry,
257
+ classifySource,
258
+ readForgeLock,
259
+ sourceIntegrity,
260
+ verifyForgeLock,
261
+ writeForgeLock,
262
+ };
@@ -0,0 +1,193 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @module gate-events
5
+ *
6
+ * Human gates enforced by kernel EVENTS, not skill prose (Fable's key insight,
7
+ * locked in docs/work/2026-07-04-kernel-native-skills/decisions.md). A gate
8
+ * approval is a durable, queryable, resume-safe kernel event on the ISSUE:
9
+ * `gate.approved` / `gate.rejected` by an actor. `forge gate check` (and, later,
10
+ * the ship stage) refuses to proceed without the required `gate.approved` event
11
+ * UNLESS the gate is toggled off — giving evidence-checked HARD-GATEs and
12
+ * resume-from-kernel-state after a compaction/crash for free.
13
+ *
14
+ * Mechanism: a PURE APPEND to the issue's event stream via the driver primitives
15
+ * (`insertKernelEvent` + `listKernelEvents`), NOT the guarded issue-mutation
16
+ * pipeline. Gate events do not mutate the issue, so they must not participate in
17
+ * the issue-revision CAS (`lib/kernel/evaluators.js` would otherwise quarantine an
18
+ * `expected_revision:0` append against a non-zero issue revision as `stale_revision`).
19
+ * Idempotency is enforced two ways: a pre-check on the idempotency key, plus the
20
+ * unique index `idx_kernel_events_idempotency` catching a concurrent race.
21
+ *
22
+ * Config-surface note: which gates EXIST (and whether each is enabled) lives in the
23
+ * runtime graph + `.forge/config.yaml`; this module only records/reads the events.
24
+ */
25
+
26
+ const { buildMigratedKernelIssueDeps } = require('./kernel/cli-broker-factory');
27
+ const { resolveIssueActor } = require('./forge-issues');
28
+
29
+ const GATE_APPROVED_EVENT = 'gate.approved';
30
+ const GATE_REJECTED_EVENT = 'gate.rejected';
31
+
32
+ const GATE_EVENT_TYPES = {
33
+ approved: GATE_APPROVED_EVENT,
34
+ rejected: GATE_REJECTED_EVENT,
35
+ };
36
+
37
+ const ISSUE_ENTITY_TYPE = 'issue';
38
+ const GATE_EVENT_ORIGIN = 'cli';
39
+
40
+ /**
41
+ * Resolve the kernel broker + driver + config. Tests (and the orchestrator) inject
42
+ * a shared, already-migrated kernel via `deps`; the CLI path builds a fresh one for
43
+ * the (short-lived) process.
44
+ *
45
+ * @param {string} projectRoot
46
+ * @param {{ kernelBroker?: Object, kernelDriver?: Object }} [deps]
47
+ * @returns {Promise<{ broker: Object, driver: Object, config: Object }>}
48
+ */
49
+ async function resolveGateKernel(projectRoot, deps = {}) {
50
+ if (deps.kernelBroker && deps.kernelDriver) {
51
+ return { broker: deps.kernelBroker, driver: deps.kernelDriver, config: deps.kernelBroker.config };
52
+ }
53
+ const built = await buildMigratedKernelIssueDeps({ projectRoot });
54
+ return { broker: built.kernelBroker, driver: built.kernelDriver, config: built.kernelBroker.config };
55
+ }
56
+
57
+ /**
58
+ * Idempotency key for a gate event. Scoped to issue + gate + actor + decision so a
59
+ * SAME-actor re-approval mints no duplicate, while a later reject (different
60
+ * decision) or a different actor is recorded as its own event.
61
+ */
62
+ function gateIdempotencyKey(eventType, issueId, gateId, actor) {
63
+ return `${eventType}:${issueId}:${gateId}:${actor}`;
64
+ }
65
+
66
+ /** Shape a stored kernel_events row into the gate-event view callers consume. */
67
+ function parseGateEvent(row) {
68
+ let payload;
69
+ try {
70
+ payload = row.payload_json ? JSON.parse(row.payload_json) : {};
71
+ } catch {
72
+ payload = {};
73
+ }
74
+ const view = {
75
+ event_type: row.event_type,
76
+ gate: payload.gate,
77
+ actor: row.actor,
78
+ created_at: row.created_at,
79
+ };
80
+ if (payload.reason !== undefined) view.reason = payload.reason;
81
+ return view;
82
+ }
83
+
84
+ function isIdempotencyRace(error) {
85
+ const message = error && error.message ? String(error.message) : '';
86
+ return /UNIQUE constraint failed/i.test(message) && /idempotency_key/i.test(message);
87
+ }
88
+
89
+ /**
90
+ * Record a gate decision as a kernel event on the issue. Idempotent per
91
+ * issue+gate+actor+decision. Validates the issue exists (the caller validates the
92
+ * gate id against the known-gate set before calling).
93
+ *
94
+ * @param {string} projectRoot
95
+ * @param {Object} params
96
+ * @param {string} params.issueId
97
+ * @param {string} params.gateId
98
+ * @param {'approved'|'rejected'} params.decision
99
+ * @param {string} [params.reason]
100
+ * @param {Object} [params.env] - env source for actor resolution (FORGE_ACTOR → …).
101
+ * @param {Object} [params.deps] - injected { kernelBroker, kernelDriver }.
102
+ * @param {string} [params.now] - ISO timestamp (defaults to now).
103
+ * @returns {Promise<{ ok: boolean, duplicate?: boolean, issueMissing?: boolean, event?: Object, actor?: string }>}
104
+ */
105
+ async function recordGateEvent(projectRoot, params = {}) {
106
+ const { issueId, gateId, decision, reason, env, deps, now } = params;
107
+ const eventType = GATE_EVENT_TYPES[decision];
108
+ if (!eventType) {
109
+ throw new Error(`Unknown gate decision '${decision}' (expected 'approved' or 'rejected').`);
110
+ }
111
+
112
+ const actor = resolveIssueActor(env || process.env) || 'forge';
113
+ const { driver, config } = await resolveGateKernel(projectRoot, deps);
114
+
115
+ const entity = await driver.loadKernelEntity(ISSUE_ENTITY_TYPE, issueId, {}, config);
116
+ if (!entity) {
117
+ return { ok: false, issueMissing: true, actor };
118
+ }
119
+
120
+ const idempotencyKey = gateIdempotencyKey(eventType, issueId, gateId, actor);
121
+
122
+ const existing = await driver.loadKernelEventByIdempotencyKey(idempotencyKey, {}, config);
123
+ if (existing) {
124
+ return { ok: true, duplicate: true, event: parseGateEvent(existing), actor };
125
+ }
126
+
127
+ const payload = { gate: gateId, actor };
128
+ if (typeof reason === 'string' && reason.length > 0) payload.reason = reason;
129
+
130
+ const event = {
131
+ entity_type: ISSUE_ENTITY_TYPE,
132
+ entity_id: issueId,
133
+ event_type: eventType,
134
+ idempotency_key: idempotencyKey,
135
+ expected_revision: 0,
136
+ actor,
137
+ origin: GATE_EVENT_ORIGIN,
138
+ payload,
139
+ created_at: now || new Date().toISOString(),
140
+ };
141
+
142
+ try {
143
+ const inserted = await driver.insertKernelEvent(event, {}, config);
144
+ return { ok: true, duplicate: false, event: parseGateEvent(inserted), actor };
145
+ } catch (error) {
146
+ if (isIdempotencyRace(error)) {
147
+ const winner = await driver.loadKernelEventByIdempotencyKey(idempotencyKey, {}, config);
148
+ return { ok: true, duplicate: true, event: winner ? parseGateEvent(winner) : parseGateEvent(event), actor };
149
+ }
150
+ throw error;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * List every gate event on an issue, oldest first.
156
+ *
157
+ * @param {string} projectRoot
158
+ * @param {string} issueId
159
+ * @param {{ deps?: Object }} [options]
160
+ * @returns {Promise<Array<{ event_type: string, gate: string, actor: string, created_at: string, reason?: string }>>}
161
+ */
162
+ async function listGateEvents(projectRoot, issueId, options = {}) {
163
+ const { driver, config } = await resolveGateKernel(projectRoot, options.deps);
164
+ const rows = await driver.listKernelEvents(ISSUE_ENTITY_TYPE, issueId, {}, config);
165
+ return (rows || [])
166
+ .filter(row => typeof row.event_type === 'string' && row.event_type.startsWith('gate.'))
167
+ .map(parseGateEvent);
168
+ }
169
+
170
+ /**
171
+ * True iff a `gate.approved` event exists for this issue + gate.
172
+ *
173
+ * @param {string} projectRoot
174
+ * @param {string} issueId
175
+ * @param {string} gateId
176
+ * @param {{ deps?: Object }} [options]
177
+ * @returns {Promise<boolean>}
178
+ */
179
+ async function isGateApproved(projectRoot, issueId, gateId, options = {}) {
180
+ const events = await listGateEvents(projectRoot, issueId, options);
181
+ return events.some(event => event.event_type === GATE_APPROVED_EVENT && event.gate === gateId);
182
+ }
183
+
184
+ module.exports = {
185
+ GATE_APPROVED_EVENT,
186
+ GATE_REJECTED_EVENT,
187
+ GATE_EVENT_TYPES,
188
+ gateIdempotencyKey,
189
+ parseGateEvent,
190
+ recordGateEvent,
191
+ listGateEvents,
192
+ isGateApproved,
193
+ };
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Global CLI flags recognized by bin/forge.js parseGlobalFlags().
5
+ *
6
+ * bin/forge.js parses these into the `flags` object but leaves them inside the
7
+ * positional `args` array it hands to command handlers. Free-text commands
8
+ * (remember, recall) that join positionals into content must strip them first,
9
+ * or `forge remember "note" -p <dir>` stores the literal `note -p <dir>`
10
+ * (kernel issue c1e090ff).
11
+ *
12
+ * Keep these sets in sync with parseGlobalFlags() in bin/forge.js.
13
+ * @module lib/global-flags
14
+ */
15
+
16
+ // Flags that consume a following value token (also accepted as --flag=value).
17
+ const GLOBAL_VALUE_FLAGS = new Set([
18
+ '--path', '-p',
19
+ '--agents',
20
+ '--merge',
21
+ '--type',
22
+ '--budget',
23
+ ]);
24
+
25
+ const GLOBAL_VALUE_FLAG_PREFIXES = ['--path=', '--agents=', '--merge=', '--type=', '--budget='];
26
+
27
+ // Boolean flags that take no value.
28
+ const GLOBAL_BOOLEAN_FLAGS = new Set([
29
+ '--quick', '-q',
30
+ '--skip-external', '--skip-services',
31
+ '--all',
32
+ '--help', '-h',
33
+ '--version', '-V',
34
+ '--yes', '-y',
35
+ '--non-interactive',
36
+ '--force',
37
+ '--verbose',
38
+ '--dry-run',
39
+ '--symlink',
40
+ '--sync',
41
+ '--interview',
42
+ ]);
43
+
44
+ /**
45
+ * Remove recognized global flags (and their values) from a positional args
46
+ * array. Plain words that merely resemble flag names (no leading dash) are
47
+ * always kept.
48
+ *
49
+ * @param {string[]} args - Raw command arguments.
50
+ * @returns {string[]} Arguments with global flags stripped.
51
+ */
52
+ function stripGlobalFlags(args) {
53
+ const kept = [];
54
+ for (let index = 0; index < args.length; index += 1) {
55
+ const arg = args[index];
56
+ if (GLOBAL_BOOLEAN_FLAGS.has(arg)) continue;
57
+ if (GLOBAL_VALUE_FLAG_PREFIXES.some((prefix) => arg.startsWith(prefix))) continue;
58
+ if (GLOBAL_VALUE_FLAGS.has(arg)) {
59
+ const next = args[index + 1];
60
+ // Mirror parsePathFlag() in bin/forge.js: the value is consumed only
61
+ // when it exists and is not itself a flag.
62
+ if (next !== undefined && !next.startsWith('-')) index += 1;
63
+ continue;
64
+ }
65
+ kept.push(arg);
66
+ }
67
+ return kept;
68
+ }
69
+
70
+ module.exports = {
71
+ GLOBAL_BOOLEAN_FLAGS,
72
+ GLOBAL_VALUE_FLAGS,
73
+ stripGlobalFlags,
74
+ };
@@ -1,80 +1,24 @@
1
1
  'use strict';
2
2
 
3
- const { execFileSync } = require('node:child_process');
3
+ const { GreptileReviewAdapter } = require('./adapters/greptile-review-adapter');
4
4
 
5
5
  /**
6
6
  * Match unresolved review threads to recent commits that touched the same files.
7
7
  *
8
- * For each thread, runs `git log --oneline --follow -- <file>` to find
9
- * recent commits. If a commit exists, the thread is considered resolved
10
- * (the file was modified after the review comment).
11
- *
12
- * Uses execFileSync (not exec) to prevent shell injection (OWASP A03).
8
+ * This backward-compatible export now delegates to the Greptile review adapter
9
+ * so existing Greptile scripts keep working while new review integrations use
10
+ * the ReviewAdapter SPI.
13
11
  *
14
12
  * @param {Array<{file: string, line: number}>} threads - Review threads with file paths
15
13
  * @param {string} projectRoot - Absolute path to the project root (used with git -C)
16
14
  * @param {object} [opts] - Options
17
- * @param {Function} [opts._exec] - Injected exec function for testing (defaults to execFileSync)
15
+ * @param {Function} [opts._exec] - Injected exec function for testing
18
16
  * @param {string} [opts.sinceCommit] - Merge-base SHA to restrict log to PR commits only
19
17
  * @returns {Array<{file: string, line: number, resolved: boolean, sha?: string, reason?: string}>}
20
18
  */
21
19
  function matchThreadsToCommits(threads, projectRoot, opts = {}) {
22
- if (!threads || threads.length === 0) {
23
- return [];
24
- }
25
-
26
- const exec = opts._exec || ((cmd, args) => {
27
- return execFileSync(cmd, args, { encoding: 'utf8' });
28
- });
29
-
30
- // Determine commit range — restrict to PR commits when sinceCommit provided
31
- let sinceCommit = opts.sinceCommit;
32
- if (!sinceCommit) {
33
- try {
34
- sinceCommit = exec('git', ['-C', projectRoot, 'merge-base', 'HEAD', 'main']).trim();
35
- } catch (_e) { // NOSONAR S2486
36
- /* intentional: main branch not found, try master instead */
37
- try {
38
- sinceCommit = exec('git', ['-C', projectRoot, 'merge-base', 'HEAD', 'master']).trim();
39
- } catch (_e2) { /* intentional: no merge-base available, fall back to unbounded log */ // NOSONAR S2486
40
- sinceCommit = null;
41
- }
42
- }
43
- }
44
-
45
- return threads.map((thread) => {
46
- const { file, line } = thread;
47
-
48
- try {
49
- const logArgs = [
50
- '-C', projectRoot,
51
- 'log',
52
- '--oneline',
53
- '--follow',
54
- ];
55
- if (sinceCommit) {
56
- logArgs.push(`${sinceCommit}..HEAD`);
57
- }
58
- logArgs.push('--', file);
59
-
60
- const output = exec('git', logArgs);
61
-
62
- const trimmed = (output || '').trim();
63
-
64
- if (!trimmed) {
65
- return { file, line, resolved: false, reason: 'no matching commit' };
66
- }
67
-
68
- // git log --oneline format: "<sha> <message>"
69
- // Take the first (most recent) line
70
- const firstLine = trimmed.split('\n')[0];
71
- const sha = firstLine.split(' ')[0];
72
-
73
- return { file, line, resolved: true, sha };
74
- } catch (_err) { /* intentional: git log failed (file missing or git unavailable), mark unresolved */ // NOSONAR S2486
75
- return { file, line, resolved: false, reason: 'no matching commit' };
76
- }
77
- });
20
+ const adapter = new GreptileReviewAdapter();
21
+ return adapter.score(threads, { ...opts, projectRoot });
78
22
  }
79
23
 
80
24
  module.exports = { matchThreadsToCommits };