forge-workflow 0.0.9 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (479) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +3 -0
  5. package/.forge/hooks/forge-native-hook.js +245 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +681 -0
  9. package/CLAUDE.md +9 -106
  10. package/QUICKSTART.md +171 -0
  11. package/README.md +271 -363
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +466 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/{TOOLCHAIN.md → forge/TOOLCHAIN.md} +56 -47
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +118 -0
  29. package/docs/guides/SUPPORT.md +185 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +205 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/reference/TOOLCHAIN.md +658 -0
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +115 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/adapter-cli.js +307 -0
  67. package/lib/adapters/beads-issue-adapter.js +127 -0
  68. package/lib/adapters/beads-kernel-compat.js +1042 -0
  69. package/lib/adapters/greptile-review-adapter.js +141 -0
  70. package/lib/adapters/kernel-issue-adapter.js +101 -0
  71. package/lib/adapters/pr-state-adapter.js +484 -0
  72. package/lib/adoption-profiles.js +126 -0
  73. package/lib/agents/README.md +2 -6
  74. package/lib/agents/claude.plugin.json +3 -8
  75. package/lib/agents/codex.plugin.json +9 -1
  76. package/lib/agents/cursor.plugin.json +2 -6
  77. package/lib/agents/hermes.plugin.json +22 -0
  78. package/lib/agents-config.js +39 -1236
  79. package/lib/audit-evidence.js +282 -0
  80. package/lib/beads-setup.js +225 -28
  81. package/lib/beads-sync-scaffold.js +36 -107
  82. package/lib/codex-skills.js +51 -1
  83. package/lib/commands/_issue.js +744 -70
  84. package/lib/commands/_manifest.js +91 -0
  85. package/lib/commands/_registry.js +85 -34
  86. package/lib/commands/_resolve-command-opts.js +261 -0
  87. package/lib/commands/_serve-security.js +270 -0
  88. package/lib/commands/adapter.js +12 -0
  89. package/lib/commands/add.js +118 -0
  90. package/lib/commands/audit.js +70 -0
  91. package/lib/commands/blocked.js +5 -0
  92. package/lib/commands/board.js +64 -0
  93. package/lib/commands/claim.js +21 -2
  94. package/lib/commands/claims.js +7 -0
  95. package/lib/commands/clean.js +485 -75
  96. package/lib/commands/close.js +2 -2
  97. package/lib/commands/comment.js +5 -0
  98. package/lib/commands/control.js +148 -0
  99. package/lib/commands/create.js +2 -2
  100. package/lib/commands/dev.js +185 -7
  101. package/lib/commands/doc-gate.js +336 -0
  102. package/lib/commands/doctor.js +156 -0
  103. package/lib/commands/explain.js +15 -0
  104. package/lib/commands/export.js +237 -0
  105. package/lib/commands/gate.js +192 -0
  106. package/lib/commands/hooks.js +242 -0
  107. package/lib/commands/inbox.js +118 -0
  108. package/lib/commands/init.js +598 -0
  109. package/lib/commands/insights.js +79 -0
  110. package/lib/commands/issue.js +12 -1
  111. package/lib/commands/issues.js +66 -0
  112. package/lib/commands/lint.js +5 -0
  113. package/lib/commands/list.js +2 -2
  114. package/lib/commands/merge.js +312 -0
  115. package/lib/commands/migrate.js +523 -0
  116. package/lib/commands/new.js +12 -0
  117. package/lib/commands/options.js +241 -0
  118. package/lib/commands/orient.js +13 -0
  119. package/lib/commands/orphans.js +5 -0
  120. package/lib/commands/patch.js +67 -0
  121. package/lib/commands/plan.js +436 -24
  122. package/lib/commands/preflight.js +211 -0
  123. package/lib/commands/prime.js +13 -0
  124. package/lib/commands/push.js +69 -2
  125. package/lib/commands/ready.js +2 -2
  126. package/lib/commands/recall.js +116 -0
  127. package/lib/commands/recap.js +61 -0
  128. package/lib/commands/recommend.js +22 -2
  129. package/lib/commands/release.js +91 -0
  130. package/lib/commands/remember.js +74 -0
  131. package/lib/commands/role.js +99 -0
  132. package/lib/commands/serve.js +581 -0
  133. package/lib/commands/setup.js +851 -979
  134. package/lib/commands/shepherd.js +436 -0
  135. package/lib/commands/ship.js +23 -1
  136. package/lib/commands/show.js +2 -2
  137. package/lib/commands/stage.js +192 -0
  138. package/lib/commands/stale.js +5 -0
  139. package/lib/commands/status.js +329 -11
  140. package/lib/commands/sync.js +34 -46
  141. package/lib/commands/team.js +15 -2
  142. package/lib/commands/test.js +58 -7
  143. package/lib/commands/update.js +2 -2
  144. package/lib/commands/upgrade.js +47 -0
  145. package/lib/commands/validate.js +56 -25
  146. package/lib/commands/worktree.js +308 -128
  147. package/lib/config-writer.js +202 -0
  148. package/lib/control-plane.js +236 -0
  149. package/lib/core/runtime-graph.js +946 -0
  150. package/lib/dep-guard/keyword-ripple.js +184 -0
  151. package/lib/deprecated-sync-cleanup.js +362 -0
  152. package/lib/detect-agent.js +2 -28
  153. package/lib/detect-worktree.js +42 -17
  154. package/lib/doc-gate/declaration.js +177 -0
  155. package/lib/doc-gate/detect.js +289 -0
  156. package/lib/doc-gate/gate.js +375 -0
  157. package/lib/doc-gate/okf-config.js +128 -0
  158. package/lib/doc-gate/okf.js +429 -0
  159. package/lib/docs-command.js +1161 -6
  160. package/lib/forge-issues.js +697 -0
  161. package/lib/forge-lock.js +262 -0
  162. package/lib/gate-events.js +193 -0
  163. package/lib/global-flags.js +74 -0
  164. package/lib/greptile-match.js +7 -63
  165. package/lib/harness-capability-matrix.js +380 -0
  166. package/lib/hook-global-installer.js +347 -0
  167. package/lib/hook-renderer.js +451 -0
  168. package/lib/inbox.js +391 -0
  169. package/lib/insights.js +397 -0
  170. package/lib/issue-adapter.js +156 -0
  171. package/lib/issue-backend.js +145 -0
  172. package/lib/issue-render.js +220 -0
  173. package/lib/issue-sync/authority.js +100 -0
  174. package/lib/issue-sync/github-pull.js +184 -0
  175. package/lib/issue-sync/import-primitives.js +98 -0
  176. package/lib/issue-sync/legacy-link-bridge.js +436 -0
  177. package/lib/issue-sync/link-store.js +292 -0
  178. package/lib/issue-sync/project-github.js +123 -0
  179. package/lib/issue-sync/reconcile.js +195 -0
  180. package/lib/issue-sync/schema.js +126 -0
  181. package/lib/kernel/backing-issue.js +305 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/planning-buckets-schema.js +109 -0
  192. package/lib/kernel/projection-jsonl-writer.js +450 -0
  193. package/lib/kernel/readiness-model.js +329 -0
  194. package/lib/kernel/schema.js +356 -0
  195. package/lib/kernel/sqlite-driver.js +2504 -0
  196. package/lib/kernel/taxonomy-validator.js +394 -0
  197. package/lib/lefthook-check.js +8 -4
  198. package/lib/lefthook-wiring.js +413 -0
  199. package/lib/mcp-config-renderer.js +288 -0
  200. package/lib/memory/graphiti-mcp.js +106 -0
  201. package/lib/memory/router.js +387 -0
  202. package/lib/memory/typed-api.js +102 -0
  203. package/lib/memory-digest.js +195 -0
  204. package/lib/merge-rules.js +395 -0
  205. package/lib/migrate-dry-run.js +466 -0
  206. package/lib/orientation.js +863 -0
  207. package/lib/package-manager-remediation.js +103 -0
  208. package/lib/package-root.js +381 -0
  209. package/lib/patch-intent.js +890 -0
  210. package/lib/plugin-catalog.js +3 -4
  211. package/lib/plugin-manager.js +0 -5
  212. package/lib/pr-bundle.js +186 -0
  213. package/lib/pr-monitor/differ.js +195 -0
  214. package/lib/pr-monitor/events.js +0 -0
  215. package/lib/pr-monitor/gather.js +124 -0
  216. package/lib/pr-monitor/journal.js +299 -0
  217. package/lib/pr-monitor/monitor.js +146 -0
  218. package/lib/pr-monitor/render-sticky.js +157 -0
  219. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  220. package/lib/pr-monitor/watch.js +247 -0
  221. package/lib/pr-pull.js +1273 -0
  222. package/lib/pr-shepherd.js +494 -0
  223. package/lib/pr-state-validator.js +59 -0
  224. package/lib/preflight/gates.js +237 -0
  225. package/lib/preflight/runner.js +116 -0
  226. package/lib/project-discovery.js +0 -53
  227. package/lib/project-memory.js +166 -0
  228. package/lib/protected-path-manifest.js +281 -0
  229. package/lib/protected-state-surfaces.js +387 -0
  230. package/lib/release-readiness.js +2089 -0
  231. package/lib/reset.js +59 -45
  232. package/lib/review-adapter.js +68 -0
  233. package/lib/rules-sync.js +260 -0
  234. package/lib/runtime-health.js +332 -23
  235. package/lib/safety-config-renderer.js +268 -0
  236. package/lib/setup-action-log.js +1 -7
  237. package/lib/setup.js +27 -65
  238. package/lib/shell-utils.js +76 -6
  239. package/lib/skills-sync.js +330 -0
  240. package/lib/smart-status/conflicts.js +205 -0
  241. package/lib/smart-status/scoring.js +191 -0
  242. package/lib/status/beads-snapshot.js +145 -0
  243. package/lib/status/presenter.js +216 -0
  244. package/lib/status/snapshot.js +186 -0
  245. package/lib/sync-backend.js +202 -0
  246. package/lib/untrusted-content.js +52 -0
  247. package/lib/upgrade-safety.js +199 -0
  248. package/lib/workflow/enforce-stage.js +298 -47
  249. package/lib/workflow/stage-transition.js +115 -0
  250. package/lib/workflow/stages.js +30 -6
  251. package/lib/workflow/state-manager.js +159 -14
  252. package/lib/workflow/state.js +23 -1
  253. package/lib/workflow-profiles.js +17 -5
  254. package/package.json +46 -36
  255. package/rules/documentation.md +19 -0
  256. package/rules/kernel-tracking.md +26 -0
  257. package/rules/security.md +22 -0
  258. package/rules/tdd.md +20 -0
  259. package/rules/workflow.md +27 -0
  260. package/scripts/auto-backing-issue.js +47 -0
  261. package/scripts/beads-context.sh +165 -22
  262. package/scripts/beads-migrate-to-dolt.sh +7 -0
  263. package/scripts/beads-upgrade-smoke.sh +284 -0
  264. package/scripts/behavioral-judge.sh +115 -11
  265. package/scripts/benchmark.js +349 -63
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard-analyze.js +52 -17
  272. package/scripts/dep-guard-keyword-ripple.js +29 -0
  273. package/scripts/dep-guard-render-review.js +86 -0
  274. package/scripts/dep-guard.sh +64 -232
  275. package/scripts/file-index.sh +3 -0
  276. package/scripts/forge-team/lib/claim.sh +34 -18
  277. package/scripts/forge-team/lib/dashboard.sh +61 -86
  278. package/scripts/forge-team/lib/epic.sh +99 -263
  279. package/scripts/forge-team/lib/hooks.sh +26 -28
  280. package/scripts/forge-team/lib/identity.sh +4 -4
  281. package/scripts/forge-team/lib/sync-github.sh +144 -47
  282. package/scripts/forge-team/lib/verify.sh +93 -83
  283. package/scripts/forge-team/lib/workload.sh +41 -65
  284. package/scripts/forge-team/tests/claim.test.sh +25 -19
  285. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  286. package/scripts/forge-team/tests/epic.test.sh +52 -71
  287. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  288. package/scripts/forge-team/tests/identity.test.sh +3 -3
  289. package/scripts/forge-team/tests/integration.test.sh +44 -66
  290. package/scripts/forge-team/tests/sync-github.test.sh +183 -79
  291. package/scripts/forge-team/tests/verify.test.sh +37 -46
  292. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  293. package/scripts/forge-team/tests/workload.test.sh +32 -66
  294. package/scripts/gen-command-manifest.js +153 -0
  295. package/scripts/gen-embedded-assets.mjs +129 -0
  296. package/scripts/install.ps1 +139 -0
  297. package/scripts/install.sh +268 -0
  298. package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
  299. package/scripts/lib/release-asset.mjs +84 -0
  300. package/scripts/parity-check.mjs +145 -0
  301. package/scripts/parity-check.test.mjs +58 -0
  302. package/scripts/pin-agentic-workflow-images.js +112 -0
  303. package/scripts/pr-coordinator.sh +3 -0
  304. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  305. package/scripts/preflight.sh +108 -0
  306. package/scripts/protected-state-check.js +104 -0
  307. package/scripts/smart-status-score.js +31 -0
  308. package/scripts/smart-status-sessions.js +51 -0
  309. package/scripts/smart-status.sh +117 -369
  310. package/scripts/spikes/config-race-bench.js +111 -0
  311. package/scripts/spikes/harness-capability-matrix.js +13 -0
  312. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  313. package/scripts/spikes/protected-path-manifest.js +20 -0
  314. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  315. package/scripts/sync-agent-skills.js +62 -0
  316. package/scripts/sync-agentic-workflow.js +48 -0
  317. package/scripts/sync-utils.sh +3 -0
  318. package/scripts/test-ci-shard.js +251 -0
  319. package/scripts/test-dashboard.js +188 -52
  320. package/scripts/test-full-suite.js +186 -0
  321. package/scripts/test-profile.js +278 -0
  322. package/scripts/test.js +302 -28
  323. package/scripts/validate.js +143 -0
  324. package/scripts/validate.sh +18 -1
  325. package/skills/claim-safety/SKILL.md +102 -0
  326. package/skills/claim-safety/evals/evals.json +46 -0
  327. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +46 -52
  328. package/skills/dev/evals/evals.json +50 -0
  329. package/skills/hermes-forge/SKILL.md +185 -0
  330. package/skills/hermes-forge/evals/evals.json +46 -0
  331. package/skills/issue-basics/SKILL.md +111 -0
  332. package/skills/issue-basics/evals/evals.json +46 -0
  333. package/skills/kernel/SKILL.md +166 -0
  334. package/skills/kernel/evals/evals.json +50 -0
  335. package/skills/memory/SKILL.md +102 -0
  336. package/skills/parallel-deep-research/SKILL.md +14 -11
  337. package/skills/parallel-deep-research/evals/evals.json +11 -27
  338. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +134 -159
  339. package/skills/plan/evals/evals.json +42 -0
  340. package/skills/research/SKILL.md +195 -0
  341. package/skills/research/evals/evals.json +42 -0
  342. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  343. package/skills/review/evals/evals.json +42 -0
  344. package/skills/rollback/SKILL.md +110 -0
  345. package/skills/rollback/evals/evals.json +46 -0
  346. package/skills/rollback/references/methods.md +204 -0
  347. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  348. package/skills/shepherd/SKILL.md +66 -0
  349. package/skills/shepherd/evals/evals.json +42 -0
  350. package/skills/ship/SKILL.md +251 -0
  351. package/skills/ship/evals/evals.json +42 -0
  352. package/skills/smith/SKILL.md +142 -0
  353. package/skills/smith/evals/evals.json +46 -0
  354. package/skills/smith/references/autonomy-and-gates.md +94 -0
  355. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  356. package/skills/sonarcloud/evals/evals.json +46 -0
  357. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  358. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  359. package/skills/status/SKILL.md +102 -0
  360. package/skills/status/evals/evals.json +50 -0
  361. package/skills/triage-ready/SKILL.md +121 -0
  362. package/skills/triage-ready/evals/evals.json +42 -0
  363. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  364. package/skills/validate/evals/evals.json +42 -0
  365. package/skills/verify/SKILL.md +299 -0
  366. package/skills/verify/evals/evals.json +50 -0
  367. package/.claude/commands/dev.md +0 -345
  368. package/.claude/commands/plan.md +0 -566
  369. package/.claude/commands/premerge.md +0 -186
  370. package/.claude/commands/research.md +0 -42
  371. package/.claude/commands/review.md +0 -451
  372. package/.claude/commands/rollback.md +0 -721
  373. package/.claude/commands/ship.md +0 -213
  374. package/.claude/commands/sonarcloud.md +0 -152
  375. package/.claude/commands/status.md +0 -90
  376. package/.claude/commands/validate.md +0 -288
  377. package/.claude/commands/verify.md +0 -269
  378. package/.claude/rules/workflow.md +0 -121
  379. package/.cline/workflows/dev.md +0 -342
  380. package/.cline/workflows/plan.md +0 -563
  381. package/.cline/workflows/premerge.md +0 -183
  382. package/.cline/workflows/research.md +0 -39
  383. package/.cline/workflows/review.md +0 -448
  384. package/.cline/workflows/rollback.md +0 -718
  385. package/.cline/workflows/ship.md +0 -210
  386. package/.cline/workflows/sonarcloud.md +0 -146
  387. package/.cline/workflows/status.md +0 -87
  388. package/.cline/workflows/validate.md +0 -285
  389. package/.cline/workflows/verify.md +0 -266
  390. package/.codex/config.toml +0 -11
  391. package/.codex/skills/dev/SKILL.md +0 -345
  392. package/.codex/skills/plan/SKILL.md +0 -566
  393. package/.codex/skills/premerge/SKILL.md +0 -186
  394. package/.codex/skills/research/SKILL.md +0 -42
  395. package/.codex/skills/review/SKILL.md +0 -451
  396. package/.codex/skills/rollback/SKILL.md +0 -721
  397. package/.codex/skills/ship/SKILL.md +0 -213
  398. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  399. package/.codex/skills/status/SKILL.md +0 -90
  400. package/.codex/skills/validate/SKILL.md +0 -288
  401. package/.codex/skills/verify/SKILL.md +0 -269
  402. package/.cursor/commands/dev.md +0 -342
  403. package/.cursor/commands/plan.md +0 -563
  404. package/.cursor/commands/premerge.md +0 -183
  405. package/.cursor/commands/research.md +0 -39
  406. package/.cursor/commands/review.md +0 -448
  407. package/.cursor/commands/ship.md +0 -210
  408. package/.cursor/commands/sonarcloud.md +0 -146
  409. package/.cursor/commands/status.md +0 -87
  410. package/.cursor/commands/validate.md +0 -285
  411. package/.cursor/commands/verify.md +0 -266
  412. package/.cursorrules +0 -149
  413. package/.github/prompts/premerge.prompt.md +0 -188
  414. package/.github/prompts/research.prompt.md +0 -44
  415. package/.github/prompts/rollback.prompt.md +0 -723
  416. package/.github/prompts/ship.prompt.md +0 -215
  417. package/.github/prompts/status.prompt.md +0 -92
  418. package/.github/prompts/verify.prompt.md +0 -271
  419. package/.github/workflows/beads-to-github.yml +0 -56
  420. package/.github/workflows/github-to-beads.yml +0 -97
  421. package/.kilocode/workflows/dev.md +0 -346
  422. package/.kilocode/workflows/plan.md +0 -567
  423. package/.kilocode/workflows/premerge.md +0 -187
  424. package/.kilocode/workflows/research.md +0 -43
  425. package/.kilocode/workflows/review.md +0 -452
  426. package/.kilocode/workflows/rollback.md +0 -722
  427. package/.kilocode/workflows/ship.md +0 -214
  428. package/.kilocode/workflows/sonarcloud.md +0 -150
  429. package/.kilocode/workflows/status.md +0 -91
  430. package/.kilocode/workflows/validate.md +0 -289
  431. package/.kilocode/workflows/verify.md +0 -270
  432. package/.opencode/commands/dev.md +0 -345
  433. package/.opencode/commands/plan.md +0 -566
  434. package/.opencode/commands/premerge.md +0 -186
  435. package/.opencode/commands/research.md +0 -42
  436. package/.opencode/commands/review.md +0 -451
  437. package/.opencode/commands/rollback.md +0 -721
  438. package/.opencode/commands/ship.md +0 -213
  439. package/.opencode/commands/sonarcloud.md +0 -149
  440. package/.opencode/commands/status.md +0 -90
  441. package/.opencode/commands/validate.md +0 -288
  442. package/.opencode/commands/verify.md +0 -269
  443. package/.roo/commands/dev.md +0 -346
  444. package/.roo/commands/plan.md +0 -567
  445. package/.roo/commands/premerge.md +0 -187
  446. package/.roo/commands/research.md +0 -43
  447. package/.roo/commands/review.md +0 -452
  448. package/.roo/commands/rollback.md +0 -722
  449. package/.roo/commands/ship.md +0 -214
  450. package/.roo/commands/sonarcloud.md +0 -150
  451. package/.roo/commands/status.md +0 -91
  452. package/.roo/commands/validate.md +0 -289
  453. package/.roo/commands/verify.md +0 -270
  454. package/docs/BEADS_GITHUB_SYNC.md +0 -255
  455. package/docs/GREPTILE_SETUP.md +0 -400
  456. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  457. package/docs/SETUP.md +0 -663
  458. package/docs/VALIDATION.md +0 -363
  459. package/lib/agents/cline.plugin.json +0 -29
  460. package/lib/agents/copilot.plugin.json +0 -24
  461. package/lib/agents/kilocode.plugin.json +0 -22
  462. package/lib/agents/opencode.plugin.json +0 -23
  463. package/lib/agents/roo.plugin.json +0 -30
  464. package/lib/beads-health-check.js +0 -143
  465. package/lib/commands/commands-reset.js +0 -147
  466. package/opencode.json +0 -67
  467. package/scripts/beads-context.test.js +0 -567
  468. package/scripts/github-beads-sync/comment.mjs +0 -64
  469. package/scripts/github-beads-sync/config.mjs +0 -148
  470. package/scripts/github-beads-sync/github-api.mjs +0 -131
  471. package/scripts/github-beads-sync/index.mjs +0 -332
  472. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  473. package/scripts/github-beads-sync/mapping.mjs +0 -78
  474. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  475. package/scripts/github-beads-sync/reverse-sync.mjs +0 -138
  476. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  477. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  478. package/scripts/github-beads-sync.config.json +0 -26
  479. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,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 };