forge-workflow 0.0.10 → 0.1.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (468) hide show
  1. package/.claude/rules/{greptile-review-process.md → review-process.md} +56 -41
  2. package/.claude/scripts/{greptile-resolve.sh → review-resolve.sh} +13 -3
  3. package/.cursor/rules/permissions-guidance.mdc +2 -2
  4. package/.forge/hooks/check-tdd.js +82 -5
  5. package/.forge/hooks/forge-native-hook.js +431 -0
  6. package/.forge/protected-paths.yaml +157 -0
  7. package/AGENTS.md +151 -61
  8. package/CHANGELOG.md +709 -0
  9. package/CLAUDE.md +9 -118
  10. package/QUICKSTART.md +175 -0
  11. package/README.md +275 -365
  12. package/bin/forge-cmd.js +120 -9
  13. package/bin/forge-preflight.js +26 -5
  14. package/bin/forge.js +532 -489
  15. package/docs/INDEX.md +93 -0
  16. package/docs/PROJECT_DESIGN.md +685 -0
  17. package/docs/architecture/index.md +66 -0
  18. package/docs/architecture/notes/README.md +35 -0
  19. package/docs/architecture/subsystems/README.md +46 -0
  20. package/docs/forge/TOOLCHAIN.md +670 -0
  21. package/docs/forge/VALIDATION.md +82 -0
  22. package/docs/{AGENT_INSTALL_PROMPT.md → guides/AGENT_INSTALL_PROMPT.md} +3 -3
  23. package/docs/guides/BEADS_GITHUB_SYNC.md +32 -0
  24. package/docs/{ENHANCED_ONBOARDING.md → guides/ENHANCED_ONBOARDING.md} +16 -12
  25. package/docs/guides/GREPTILE_SETUP.md +46 -0
  26. package/docs/guides/MANUAL_REVIEW_GUIDE.md +58 -0
  27. package/docs/guides/MIGRATION.md +56 -0
  28. package/docs/guides/SETUP.md +121 -0
  29. package/docs/guides/SUPPORT.md +190 -0
  30. package/docs/guides/WORKFLOW_TEMPLATES.md +74 -0
  31. package/docs/guides/memory-backends.md +183 -0
  32. package/docs/reference/ADAPTERS.md +128 -0
  33. package/docs/reference/AGENT_SKILL_PARITY.md +175 -0
  34. package/docs/reference/COMMANDS.md +214 -0
  35. package/docs/reference/DECISION_DRIFT_GUARDS.md +97 -0
  36. package/docs/{EXAMPLES.md → reference/EXAMPLES.md} +7 -5
  37. package/docs/reference/FORGE_KERNEL_STORAGE_MODEL.md +135 -0
  38. package/docs/reference/HERMES_INTEGRATION.md +118 -0
  39. package/docs/reference/INSIGHTS_RECAP.md +63 -0
  40. package/docs/reference/INSTALL.md +164 -0
  41. package/docs/reference/KERNEL_TAXONOMY_VALIDATION.md +161 -0
  42. package/docs/reference/PROTECTED_PATH_MANIFEST.md +25 -0
  43. package/docs/reference/RELEASE.md +68 -0
  44. package/docs/reference/RESEARCH_TEMPLATE.md +292 -0
  45. package/docs/{ROADMAP.md → reference/ROADMAP.md} +12 -9
  46. package/docs/reference/SKILLS.md +35 -0
  47. package/docs/reference/STATUS_BOARD.md +80 -0
  48. package/docs/reference/TEMPLATES.md +106 -0
  49. package/docs/{TOOLCHAIN.md → reference/TOOLCHAIN.md} +62 -47
  50. package/docs/reference/VALIDATION.md +82 -0
  51. package/docs/reference/agent-permissions.md +169 -0
  52. package/docs/reference/beads-to-kernel-migration-ux.md +61 -0
  53. package/docs/reference/control-plane-guarantees.md +125 -0
  54. package/docs/reference/dependency-chain.md +331 -0
  55. package/docs/reference/forge-kernel-issue-command-contract.md +161 -0
  56. package/docs/reference/forge-kernel-schema.md +72 -0
  57. package/docs/reference/kernel-conflict-evaluators.md +27 -0
  58. package/docs/reference/patch-md-format.md +77 -0
  59. package/docs/reference/protected-state-surfaces.md +59 -0
  60. package/docs/reference/shepherd.md +155 -0
  61. package/docs/reference/superpowers-analysis.md +320 -0
  62. package/docs/reference/superpowers-integration-options.md +404 -0
  63. package/docs/reference/test-environment.md +519 -0
  64. package/docs/reference/upgrade-safety.md +59 -0
  65. package/lefthook.yml +18 -0
  66. package/lib/activation/ensure-forge-home.js +135 -0
  67. package/lib/adapter-cli.js +307 -0
  68. package/lib/adapters/beads-issue-adapter.js +127 -0
  69. package/lib/adapters/beads-kernel-compat.js +1109 -0
  70. package/lib/adapters/greptile-review-adapter.js +141 -0
  71. package/lib/adapters/kernel-issue-adapter.js +101 -0
  72. package/lib/adapters/pr-state-adapter.js +484 -0
  73. package/lib/adoption-profiles.js +139 -0
  74. package/lib/agents/README.md +2 -6
  75. package/lib/agents/claude.plugin.json +3 -8
  76. package/lib/agents/codex.plugin.json +9 -1
  77. package/lib/agents/cursor.plugin.json +2 -6
  78. package/lib/agents/hermes.plugin.json +22 -0
  79. package/lib/agents-config.js +39 -1236
  80. package/lib/audit-evidence.js +282 -0
  81. package/lib/beads-detect.js +60 -0
  82. package/lib/beads-nudge.js +91 -0
  83. package/lib/beads-setup.js +121 -0
  84. package/lib/beads-sync-scaffold.js +25 -101
  85. package/lib/codex-skills.js +51 -1
  86. package/lib/commands/_aliases.js +248 -0
  87. package/lib/commands/_issue.js +780 -77
  88. package/lib/commands/_manifest.js +93 -0
  89. package/lib/commands/_registry.js +99 -34
  90. package/lib/commands/_resolve-command-opts.js +230 -0
  91. package/lib/commands/_serve-security.js +270 -0
  92. package/lib/commands/adapter.js +12 -0
  93. package/lib/commands/add.js +118 -0
  94. package/lib/commands/audit.js +70 -0
  95. package/lib/commands/blocked.js +5 -0
  96. package/lib/commands/board.js +64 -0
  97. package/lib/commands/claim.js +21 -2
  98. package/lib/commands/claims.js +7 -0
  99. package/lib/commands/clean.js +485 -75
  100. package/lib/commands/close.js +2 -2
  101. package/lib/commands/comment.js +5 -0
  102. package/lib/commands/control.js +148 -0
  103. package/lib/commands/create.js +2 -2
  104. package/lib/commands/dev.js +185 -7
  105. package/lib/commands/doc-gate.js +336 -0
  106. package/lib/commands/doctor.js +156 -0
  107. package/lib/commands/explain.js +15 -0
  108. package/lib/commands/export.js +237 -0
  109. package/lib/commands/gate.js +209 -0
  110. package/lib/commands/hooks.js +377 -0
  111. package/lib/commands/inbox.js +118 -0
  112. package/lib/commands/init.js +604 -0
  113. package/lib/commands/insights.js +79 -0
  114. package/lib/commands/issue.js +12 -1
  115. package/lib/commands/issues.js +17 -0
  116. package/lib/commands/lint.js +5 -0
  117. package/lib/commands/list.js +2 -2
  118. package/lib/commands/memory.js +81 -0
  119. package/lib/commands/merge.js +312 -0
  120. package/lib/commands/migrate.js +362 -0
  121. package/lib/commands/new.js +12 -0
  122. package/lib/commands/options.js +241 -0
  123. package/lib/commands/orient.js +13 -0
  124. package/lib/commands/orphans.js +5 -0
  125. package/lib/commands/patch.js +67 -0
  126. package/lib/commands/plan.js +481 -29
  127. package/lib/commands/pr.js +88 -0
  128. package/lib/commands/preflight.js +211 -0
  129. package/lib/commands/prime.js +13 -0
  130. package/lib/commands/push.js +135 -2
  131. package/lib/commands/ready.js +2 -2
  132. package/lib/commands/recall.js +171 -0
  133. package/lib/commands/recap.js +75 -0
  134. package/lib/commands/recommend.js +0 -1
  135. package/lib/commands/release.js +104 -0
  136. package/lib/commands/remember.js +140 -0
  137. package/lib/commands/role.js +99 -0
  138. package/lib/commands/serve.js +581 -0
  139. package/lib/commands/setup.js +900 -971
  140. package/lib/commands/shepherd.js +501 -0
  141. package/lib/commands/ship.js +59 -1
  142. package/lib/commands/show.js +2 -2
  143. package/lib/commands/stage.js +192 -0
  144. package/lib/commands/stale.js +5 -0
  145. package/lib/commands/status.js +158 -21
  146. package/lib/commands/sync.js +34 -46
  147. package/lib/commands/team.js +4 -1
  148. package/lib/commands/test.js +43 -27
  149. package/lib/commands/update.js +2 -2
  150. package/lib/commands/upgrade.js +47 -0
  151. package/lib/commands/validate.js +43 -18
  152. package/lib/commands/worktree.js +362 -99
  153. package/lib/config-writer.js +202 -0
  154. package/lib/control-plane.js +236 -0
  155. package/lib/core/runtime-graph.js +977 -0
  156. package/lib/dep-guard/keyword-ripple.js +2 -2
  157. package/lib/deprecated-sync-cleanup.js +362 -0
  158. package/lib/detect-agent.js +2 -28
  159. package/lib/detect-worktree.js +35 -9
  160. package/lib/doc-gate/declaration.js +177 -0
  161. package/lib/doc-gate/detect.js +289 -0
  162. package/lib/doc-gate/gate.js +375 -0
  163. package/lib/doc-gate/okf-config.js +128 -0
  164. package/lib/doc-gate/okf.js +429 -0
  165. package/lib/docs-command.js +1161 -6
  166. package/lib/forge-issues.js +382 -11
  167. package/lib/forge-lock.js +262 -0
  168. package/lib/gate-events.js +192 -0
  169. package/lib/global-flags.js +104 -0
  170. package/lib/greptile-match.js +7 -63
  171. package/lib/grounding/context-events.js +230 -0
  172. package/lib/grounding/read-first.js +112 -0
  173. package/lib/harness-capability-matrix.js +380 -0
  174. package/lib/hook-global-installer.js +347 -0
  175. package/lib/hook-renderer.js +541 -0
  176. package/lib/inbox.js +391 -0
  177. package/lib/insights.js +397 -0
  178. package/lib/issue-adapter.js +156 -0
  179. package/lib/issue-backend.js +145 -0
  180. package/lib/issue-render.js +220 -0
  181. package/lib/kernel/backing-issue.js +311 -0
  182. package/lib/kernel/broker.js +1218 -0
  183. package/lib/kernel/cli-broker-factory.js +130 -0
  184. package/lib/kernel/conflict-signal.js +82 -0
  185. package/lib/kernel/evaluators.js +195 -0
  186. package/lib/kernel/fs-class.js +495 -0
  187. package/lib/kernel/issue-command-contract.js +559 -0
  188. package/lib/kernel/issue-id-resolver.js +186 -0
  189. package/lib/kernel/lease-enforcer.js +158 -0
  190. package/lib/kernel/migrations.js +333 -0
  191. package/lib/kernel/owned-kernel.js +43 -0
  192. package/lib/kernel/planning-buckets-schema.js +109 -0
  193. package/lib/kernel/projection-jsonl-writer.js +450 -0
  194. package/lib/kernel/readiness-model.js +329 -0
  195. package/lib/kernel/schema.js +356 -0
  196. package/lib/kernel/sqlite-driver.js +2540 -0
  197. package/lib/kernel/taxonomy-validator.js +394 -0
  198. package/lib/lefthook-check.js +3 -2
  199. package/lib/lefthook-wiring.js +413 -0
  200. package/lib/mcp-config-renderer.js +288 -0
  201. package/lib/memory/graphiti-mcp.js +106 -0
  202. package/lib/memory/router.js +387 -0
  203. package/lib/memory/typed-api.js +102 -0
  204. package/lib/memory-digest.js +195 -0
  205. package/lib/merge-rules.js +395 -0
  206. package/lib/migrate-dry-run.js +466 -0
  207. package/lib/orientation.js +863 -0
  208. package/lib/package-manager-remediation.js +103 -0
  209. package/lib/package-root.js +381 -0
  210. package/lib/patch-intent.js +890 -0
  211. package/lib/plugin-catalog.js +3 -4
  212. package/lib/plugin-manager.js +0 -5
  213. package/lib/pr-bundle.js +186 -0
  214. package/lib/pr-monitor/auto-actions.js +175 -0
  215. package/lib/pr-monitor/differ.js +195 -0
  216. package/lib/pr-monitor/digest.js +206 -0
  217. package/lib/pr-monitor/events.js +0 -0
  218. package/lib/pr-monitor/gather.js +124 -0
  219. package/lib/pr-monitor/journal.js +299 -0
  220. package/lib/pr-monitor/monitor.js +146 -0
  221. package/lib/pr-monitor/render-sticky.js +192 -0
  222. package/lib/pr-monitor/upsert-sticky.js +169 -0
  223. package/lib/pr-monitor/watch-lifecycle.js +95 -0
  224. package/lib/pr-monitor/watch.js +247 -0
  225. package/lib/pr-pull.js +1314 -0
  226. package/lib/pr-shepherd.js +494 -0
  227. package/lib/pr-state-validator.js +59 -0
  228. package/lib/preflight/gates.js +237 -0
  229. package/lib/preflight/runner.js +116 -0
  230. package/lib/project-discovery.js +0 -53
  231. package/lib/project-memory.js +99 -497
  232. package/lib/protected-path-manifest.js +281 -0
  233. package/lib/protected-state-surfaces.js +387 -0
  234. package/lib/release-readiness.js +2105 -0
  235. package/lib/reset.js +59 -45
  236. package/lib/review-adapter.js +68 -0
  237. package/lib/rules-sync.js +260 -0
  238. package/lib/runtime-health.js +241 -20
  239. package/lib/safety-config-renderer.js +268 -0
  240. package/lib/setup-action-log.js +1 -7
  241. package/lib/setup.js +27 -65
  242. package/lib/shell-utils.js +76 -6
  243. package/lib/skills-sync.js +330 -0
  244. package/lib/smart-status/scoring.js +17 -3
  245. package/lib/status/beads-snapshot.js +45 -2
  246. package/lib/status/presenter.js +169 -18
  247. package/lib/status/snapshot.js +186 -0
  248. package/lib/sync-backend.js +202 -0
  249. package/lib/untrusted-content.js +52 -0
  250. package/lib/upgrade-safety.js +251 -0
  251. package/lib/workflow/enforce-stage.js +351 -45
  252. package/lib/workflow/stage-transition.js +115 -0
  253. package/lib/workflow/stages.js +30 -6
  254. package/lib/workflow/state-manager.js +11 -22
  255. package/lib/workflow/state.js +23 -1
  256. package/lib/workflow-profiles.js +17 -5
  257. package/package.json +37 -35
  258. package/rules/documentation.md +19 -0
  259. package/rules/kernel-tracking.md +26 -0
  260. package/rules/security.md +22 -0
  261. package/rules/tdd.md +20 -0
  262. package/rules/workflow.md +27 -0
  263. package/scripts/auto-backing-issue.js +47 -0
  264. package/scripts/beads-context.sh +81 -57
  265. package/scripts/beads-upgrade-smoke.sh +24 -3
  266. package/scripts/bootstrap-windows-tools.sh +78 -0
  267. package/scripts/branch-protection.js +2 -3
  268. package/scripts/check-agents.js +34 -137
  269. package/scripts/commitlint.js +3 -1
  270. package/scripts/conflict-detect.sh +3 -0
  271. package/scripts/dep-guard.sh +22 -3
  272. package/scripts/file-index.sh +3 -0
  273. package/scripts/forge-team/lib/claim.sh +34 -18
  274. package/scripts/forge-team/lib/dashboard.sh +61 -86
  275. package/scripts/forge-team/lib/epic.sh +99 -263
  276. package/scripts/forge-team/lib/hooks.sh +26 -28
  277. package/scripts/forge-team/lib/identity.sh +4 -4
  278. package/scripts/forge-team/lib/sync-github.sh +49 -84
  279. package/scripts/forge-team/lib/verify.sh +93 -83
  280. package/scripts/forge-team/lib/workload.sh +41 -65
  281. package/scripts/forge-team/tests/claim.test.sh +25 -19
  282. package/scripts/forge-team/tests/dashboard.test.sh +31 -46
  283. package/scripts/forge-team/tests/epic.test.sh +52 -71
  284. package/scripts/forge-team/tests/hooks.test.sh +38 -50
  285. package/scripts/forge-team/tests/identity.test.sh +3 -3
  286. package/scripts/forge-team/tests/integration.test.sh +44 -66
  287. package/scripts/forge-team/tests/sync-github.test.sh +50 -83
  288. package/scripts/forge-team/tests/verify.test.sh +37 -46
  289. package/scripts/forge-team/tests/workflow-integration.test.sh +4 -4
  290. package/scripts/forge-team/tests/workload.test.sh +32 -66
  291. package/scripts/gen-command-manifest.js +153 -0
  292. package/scripts/gen-embedded-assets.mjs +129 -0
  293. package/scripts/install.ps1 +139 -0
  294. package/scripts/install.sh +268 -0
  295. package/scripts/lib/release-asset.mjs +84 -0
  296. package/scripts/parity-check.mjs +145 -0
  297. package/scripts/parity-check.test.mjs +58 -0
  298. package/scripts/pin-agentic-workflow-images.js +112 -0
  299. package/scripts/pr-auto-actions.js +93 -0
  300. package/scripts/pr-coordinator.sh +3 -0
  301. package/scripts/pr-verdict-label.js +50 -0
  302. package/scripts/preflight-sonar.eslint.config.mjs +44 -0
  303. package/scripts/preflight.sh +21 -94
  304. package/scripts/protected-state-check.js +104 -0
  305. package/scripts/smart-status.sh +60 -57
  306. package/scripts/spikes/config-race-bench.js +111 -0
  307. package/scripts/spikes/harness-capability-matrix.js +13 -0
  308. package/scripts/spikes/patch-anchor-stability-bench.js +125 -0
  309. package/scripts/spikes/protected-path-manifest.js +20 -0
  310. package/scripts/spikes/skill-auto-invoke-parity.js +292 -0
  311. package/scripts/sync-agent-skills.js +62 -0
  312. package/scripts/sync-utils.sh +3 -0
  313. package/scripts/test-ci-shard.js +13 -6
  314. package/scripts/test.js +95 -12
  315. package/skills/claim-safety/SKILL.md +102 -0
  316. package/skills/claim-safety/evals/evals.json +46 -0
  317. package/{.github/prompts/dev.prompt.md → skills/dev/SKILL.md} +44 -50
  318. package/skills/dev/evals/evals.json +50 -0
  319. package/skills/hermes-forge/SKILL.md +185 -0
  320. package/skills/hermes-forge/evals/evals.json +46 -0
  321. package/skills/issue-basics/SKILL.md +111 -0
  322. package/skills/issue-basics/evals/evals.json +46 -0
  323. package/skills/kernel/SKILL.md +166 -0
  324. package/skills/kernel/evals/evals.json +50 -0
  325. package/skills/memory/SKILL.md +102 -0
  326. package/skills/parallel-deep-research/SKILL.md +14 -11
  327. package/skills/parallel-deep-research/evals/evals.json +11 -27
  328. package/{.github/prompts/plan.prompt.md → skills/plan/SKILL.md} +132 -157
  329. package/skills/plan/evals/evals.json +42 -0
  330. package/skills/research/SKILL.md +195 -0
  331. package/skills/research/evals/evals.json +42 -0
  332. package/{.github/prompts/review.prompt.md → skills/review/SKILL.md} +98 -62
  333. package/skills/review/evals/evals.json +42 -0
  334. package/skills/rollback/SKILL.md +110 -0
  335. package/skills/rollback/evals/evals.json +46 -0
  336. package/skills/rollback/references/methods.md +204 -0
  337. package/{.cursor/commands/rollback.md → skills/rollback/references/workflow-integration.md} +10 -284
  338. package/skills/shepherd/SKILL.md +66 -0
  339. package/skills/shepherd/evals/evals.json +42 -0
  340. package/{.github/prompts/ship.prompt.md → skills/ship/SKILL.md} +81 -45
  341. package/skills/ship/evals/evals.json +42 -0
  342. package/skills/smith/SKILL.md +142 -0
  343. package/skills/smith/evals/evals.json +46 -0
  344. package/skills/smith/references/autonomy-and-gates.md +94 -0
  345. package/{.github/prompts/sonarcloud.prompt.md → skills/sonarcloud/SKILL.md} +14 -3
  346. package/skills/sonarcloud/evals/evals.json +46 -0
  347. package/skills/sonarcloud-analysis/SKILL.md +18 -13
  348. package/skills/sonarcloud-analysis/evals/evals.json +11 -15
  349. package/{.github/prompts/status.prompt.md → skills/status/SKILL.md} +20 -10
  350. package/skills/status/evals/evals.json +50 -0
  351. package/skills/triage-ready/SKILL.md +121 -0
  352. package/skills/triage-ready/evals/evals.json +42 -0
  353. package/{.github/prompts/validate.prompt.md → skills/validate/SKILL.md} +52 -29
  354. package/skills/validate/evals/evals.json +42 -0
  355. package/skills/verify/SKILL.md +299 -0
  356. package/skills/verify/evals/evals.json +50 -0
  357. package/.claude/commands/dev.md +0 -345
  358. package/.claude/commands/plan.md +0 -566
  359. package/.claude/commands/premerge.md +0 -186
  360. package/.claude/commands/research.md +0 -42
  361. package/.claude/commands/review.md +0 -451
  362. package/.claude/commands/rollback.md +0 -721
  363. package/.claude/commands/ship.md +0 -213
  364. package/.claude/commands/sonarcloud.md +0 -152
  365. package/.claude/commands/status.md +0 -90
  366. package/.claude/commands/validate.md +0 -288
  367. package/.claude/commands/verify.md +0 -269
  368. package/.claude/rules/workflow.md +0 -121
  369. package/.cline/workflows/dev.md +0 -342
  370. package/.cline/workflows/plan.md +0 -563
  371. package/.cline/workflows/premerge.md +0 -183
  372. package/.cline/workflows/research.md +0 -39
  373. package/.cline/workflows/review.md +0 -448
  374. package/.cline/workflows/rollback.md +0 -718
  375. package/.cline/workflows/ship.md +0 -210
  376. package/.cline/workflows/sonarcloud.md +0 -146
  377. package/.cline/workflows/status.md +0 -87
  378. package/.cline/workflows/validate.md +0 -285
  379. package/.cline/workflows/verify.md +0 -266
  380. package/.codex/config.toml +0 -11
  381. package/.codex/skills/dev/SKILL.md +0 -345
  382. package/.codex/skills/plan/SKILL.md +0 -566
  383. package/.codex/skills/premerge/SKILL.md +0 -186
  384. package/.codex/skills/research/SKILL.md +0 -42
  385. package/.codex/skills/review/SKILL.md +0 -451
  386. package/.codex/skills/rollback/SKILL.md +0 -721
  387. package/.codex/skills/ship/SKILL.md +0 -213
  388. package/.codex/skills/sonarcloud/SKILL.md +0 -149
  389. package/.codex/skills/status/SKILL.md +0 -90
  390. package/.codex/skills/validate/SKILL.md +0 -288
  391. package/.codex/skills/verify/SKILL.md +0 -269
  392. package/.cursor/commands/dev.md +0 -342
  393. package/.cursor/commands/plan.md +0 -563
  394. package/.cursor/commands/premerge.md +0 -183
  395. package/.cursor/commands/research.md +0 -39
  396. package/.cursor/commands/review.md +0 -448
  397. package/.cursor/commands/ship.md +0 -210
  398. package/.cursor/commands/sonarcloud.md +0 -146
  399. package/.cursor/commands/status.md +0 -87
  400. package/.cursor/commands/validate.md +0 -285
  401. package/.cursor/commands/verify.md +0 -266
  402. package/.cursorrules +0 -149
  403. package/.github/prompts/premerge.prompt.md +0 -188
  404. package/.github/prompts/research.prompt.md +0 -44
  405. package/.github/prompts/rollback.prompt.md +0 -723
  406. package/.github/prompts/verify.prompt.md +0 -271
  407. package/.github/workflows/beads-to-github.yml +0 -89
  408. package/.github/workflows/github-to-beads.yml +0 -100
  409. package/.kilocode/workflows/dev.md +0 -346
  410. package/.kilocode/workflows/plan.md +0 -567
  411. package/.kilocode/workflows/premerge.md +0 -187
  412. package/.kilocode/workflows/research.md +0 -43
  413. package/.kilocode/workflows/review.md +0 -452
  414. package/.kilocode/workflows/rollback.md +0 -722
  415. package/.kilocode/workflows/ship.md +0 -214
  416. package/.kilocode/workflows/sonarcloud.md +0 -150
  417. package/.kilocode/workflows/status.md +0 -91
  418. package/.kilocode/workflows/validate.md +0 -289
  419. package/.kilocode/workflows/verify.md +0 -270
  420. package/.opencode/commands/dev.md +0 -345
  421. package/.opencode/commands/plan.md +0 -566
  422. package/.opencode/commands/premerge.md +0 -186
  423. package/.opencode/commands/research.md +0 -42
  424. package/.opencode/commands/review.md +0 -451
  425. package/.opencode/commands/rollback.md +0 -721
  426. package/.opencode/commands/ship.md +0 -213
  427. package/.opencode/commands/sonarcloud.md +0 -149
  428. package/.opencode/commands/status.md +0 -90
  429. package/.opencode/commands/validate.md +0 -288
  430. package/.opencode/commands/verify.md +0 -269
  431. package/.roo/commands/dev.md +0 -346
  432. package/.roo/commands/plan.md +0 -567
  433. package/.roo/commands/premerge.md +0 -187
  434. package/.roo/commands/research.md +0 -43
  435. package/.roo/commands/review.md +0 -452
  436. package/.roo/commands/rollback.md +0 -722
  437. package/.roo/commands/ship.md +0 -214
  438. package/.roo/commands/sonarcloud.md +0 -150
  439. package/.roo/commands/status.md +0 -91
  440. package/.roo/commands/validate.md +0 -289
  441. package/.roo/commands/verify.md +0 -270
  442. package/docs/BEADS_GITHUB_SYNC.md +0 -281
  443. package/docs/GREPTILE_SETUP.md +0 -400
  444. package/docs/MANUAL_REVIEW_GUIDE.md +0 -106
  445. package/docs/SETUP.md +0 -663
  446. package/docs/VALIDATION.md +0 -363
  447. package/lib/agents/cline.plugin.json +0 -29
  448. package/lib/agents/copilot.plugin.json +0 -24
  449. package/lib/agents/kilocode.plugin.json +0 -22
  450. package/lib/agents/opencode.plugin.json +0 -23
  451. package/lib/agents/roo.plugin.json +0 -30
  452. package/lib/beads-bootstrap.js +0 -225
  453. package/lib/beads-health-check.js +0 -188
  454. package/lib/commands/commands-reset.js +0 -147
  455. package/opencode.json +0 -67
  456. package/scripts/beads-context.test.js +0 -584
  457. package/scripts/github-beads-sync/comment.mjs +0 -64
  458. package/scripts/github-beads-sync/config.mjs +0 -148
  459. package/scripts/github-beads-sync/github-api.mjs +0 -131
  460. package/scripts/github-beads-sync/index.mjs +0 -356
  461. package/scripts/github-beads-sync/label-mapper.mjs +0 -54
  462. package/scripts/github-beads-sync/mapping.mjs +0 -132
  463. package/scripts/github-beads-sync/reverse-sync-cli.mjs +0 -31
  464. package/scripts/github-beads-sync/reverse-sync.mjs +0 -162
  465. package/scripts/github-beads-sync/run-bd.mjs +0 -161
  466. package/scripts/github-beads-sync/sanitize.mjs +0 -121
  467. package/scripts/github-beads-sync.config.json +0 -26
  468. package/scripts/sync-commands.js +0 -600
@@ -0,0 +1,413 @@
1
+ /**
2
+ * Shared lefthook / git-hook wiring for `forge setup` and `forge init`.
3
+ *
4
+ * Single source of truth for the hook-wiring primitives. The LIVE `forge setup` /
5
+ * `forge init` path is the registry command in lib/commands/setup.js (it wins in the
6
+ * dispatcher and returns before bin/forge.js's inline setup branch is ever reached; the
7
+ * bin copy is LEGACY/DEAD — see the banner at bin/forge.js "LEGACY / DEAD SETUP PATH").
8
+ * The mid-stage repair path (enforce-stage → repairRuntimeReadiness) also consumes these
9
+ * primitives. Earlier the shadow-config fix landed only in one copy while `forge setup`
10
+ * ran a stale copy, leaving a fresh `forge setup` with TDD enforcement SILENTLY INERT
11
+ * (kernel e452422c / c713fce7 / 22e33dbf, beta blocker B3) — hence one shared module.
12
+ *
13
+ * Responsibilities:
14
+ * - Provide the REAL user-facing lefthook.yml (never lefthook's commented example).
15
+ * - Decide when it is safe to (over)write lefthook.yml (replace only a stub).
16
+ * - Install a native `.git/hooks` fallback when the lefthook binary is unavailable,
17
+ * so raw `git commit` / `git push` still enforce the TDD gate.
18
+ * - Verify honestly whether hooks are actually active, so setup can fail LOUDLY
19
+ * instead of no-op'ing.
20
+ *
21
+ * @module lib/lefthook-wiring
22
+ */
23
+
24
+ 'use strict';
25
+
26
+ const fs = require('node:fs');
27
+ const path = require('node:path');
28
+ // Shared secure spawner: resolves the command to its absolute path (which/where.exe)
29
+ // before executing, so we never search a potentially-writable PATH for `git` (S4036).
30
+ const { secureExecFileSync } = require('./shell-utils');
31
+
32
+ // Sentinel embedded in every native hook Forge writes. Lets verifyHooksActive
33
+ // distinguish a Forge-managed hook from a lefthook-managed one or a user's own.
34
+ const FORGE_NATIVE_HOOK_SENTINEL = '# forge-native-git-hook (installed by `forge setup`)';
35
+
36
+ // The lefthook.yml Forge writes into a USER project. The repo's own dev lefthook.yml
37
+ // references repo-internal scripts (scripts/branch-protection.js, lint.js, test.js, …)
38
+ // that a user's project never has, so shipping it produced hooks that fail on the first
39
+ // commit. This minimal config references only what every Forge project gets: the
40
+ // self-contained TDD gate (.forge/hooks/check-tdd.js) on pre-commit, and the project's
41
+ // own test script (a no-op when absent) on pre-push. Both hooks exist, so the
42
+ // HOOKS_NOT_ACTIVE gate is satisfied and the workflow is reachable out of the box.
43
+ const FORGE_USER_LEFTHOOK_YML = `# Forge git hooks — installed by \`forge setup\` / \`forge init\`.
44
+ # pre-commit enforces the TDD gate; pre-push runs your test script if you have one.
45
+ # Edit freely to add your own checks — Forge only replaces a fully-commented stub.
46
+
47
+ pre-commit:
48
+ commands:
49
+ forge-tdd:
50
+ run: node .forge/hooks/check-tdd.js
51
+
52
+ pre-push:
53
+ commands:
54
+ tests:
55
+ run: npm test --if-present
56
+ `;
57
+
58
+ /**
59
+ * A fresh `lefthook install` (run by lefthook's own npm postinstall) drops a stock
60
+ * EXAMPLE lefthook.yml with every hook commented out. That disposable stub used to
61
+ * block Forge from writing its config, leaving pre-commit/pre-push unwired so every
62
+ * stage command dead-ended on HOOKS_NOT_ACTIVE right after `forge init` (kernel
63
+ * c713fce7). Overwrite a missing file or such a stub, but never clobber a lefthook.yml
64
+ * that already has active (uncommented) jobs — a user's real config, or Forge's own
65
+ * once written.
66
+ *
67
+ * @param {string} lefthookTarget - Absolute path to the candidate lefthook.yml.
68
+ * @returns {boolean} true when Forge should (over)write its config at that path.
69
+ */
70
+ function forgeShouldWriteLefthookConfig(lefthookTarget) {
71
+ let stat;
72
+ try {
73
+ stat = fs.lstatSync(lefthookTarget);
74
+ } catch (error) {
75
+ if (error && error.code !== 'ENOENT') {
76
+ throw error;
77
+ }
78
+ return true; // no existing file → write Forge's config
79
+ }
80
+ // Never write THROUGH a symlink (or any non-regular file): a checked-out lefthook.yml
81
+ // pointing outside projectRoot could otherwise be created/overwritten via the
82
+ // follow-through of readFileSync/writeFileSync. Only regular files are safe.
83
+ if (!stat.isFile() || stat.isSymbolicLink()) {
84
+ return false;
85
+ }
86
+ const content = fs.readFileSync(lefthookTarget, 'utf8');
87
+ const activeLines = content
88
+ .split(/\r?\n/)
89
+ .map((line) => line.trim())
90
+ .filter((line) => line.length > 0 && !line.startsWith('#'));
91
+ return activeLines.length === 0;
92
+ }
93
+
94
+ /**
95
+ * Resolve the directory git ACTUALLY reads hooks from for a given project root.
96
+ *
97
+ * Asks git first (`git rev-parse --git-path hooks`), which is authoritative: it
98
+ * honors `core.hooksPath` (husky, a global config, etc.), so verify checks — and the
99
+ * native installer writes to — the directory git will really execute. Without this a
100
+ * project with `core.hooksPath` set would get hooks written to `.git/hooks` that git
101
+ * never runs, and verify would falsely report ACTIVE (a silent-inert false pass, B3).
102
+ *
103
+ * Falls back to a pure-filesystem resolution when git is unavailable or the path is not
104
+ * a git repo — keeps the function testable against synthetic `.git` layouts:
105
+ * - Normal repo: `.git` is a directory → `<root>/.git/hooks`.
106
+ * - Linked worktree: `.git` is a file `gitdir: <path>`; hooks live in the common git
107
+ * dir (resolved via the worktree's `commondir` file) → `<common>/hooks`.
108
+ *
109
+ * @param {string} projectRoot - Absolute path to the project root.
110
+ * @returns {string|null} Absolute hooks dir, or null when not a git repo.
111
+ */
112
+ function resolveGitHooksDir(projectRoot) {
113
+ return resolveHooksDirViaGit(projectRoot) || resolveGitHooksDirFromFs(projectRoot);
114
+ }
115
+
116
+ // Authoritative resolution via git (honors core.hooksPath). One fast spawn; returns
117
+ // null on any failure (git missing, not a repo) so the filesystem fallback can run.
118
+ function resolveHooksDirViaGit(projectRoot) {
119
+ // SAME-REPO GUARD (kernel N1 / B3): only trust git's answer when projectRoot is its OWN
120
+ // repo — a `.git` directory or a linked-worktree `.git` file present right here. Without
121
+ // this, `git -C <projectRoot> rev-parse` walks UP to an ANCESTOR repo when projectRoot is
122
+ // a not-yet-initialized folder sitting under one (e.g. a project inside a dotfiles-managed
123
+ // $HOME, or a subdir of any repo). We'd then write hooks into — and report ACTIVE from —
124
+ // a repo the user never pointed forge at (cross-repo mutation + a false-active verify).
125
+ // Returning null here defers to the filesystem fallback (which also returns null for such
126
+ // a path), so the caller warns "not a git repository" — the safe pre-F3 behavior.
127
+ if (!fs.existsSync(path.join(projectRoot, '.git'))) {
128
+ return null;
129
+ }
130
+ try {
131
+ // secureExecFileSync resolves `git` to its absolute path first (no PATH search, S4036).
132
+ // projectRoot is passed as an ARGUMENT to execFile (never a shell string), so it cannot
133
+ // be interpreted as a command — no command-injection surface.
134
+ const out = secureExecFileSync(
135
+ 'git',
136
+ ['-C', projectRoot, 'rev-parse', '--path-format=absolute', '--git-path', 'hooks'],
137
+ { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
138
+ );
139
+ return (typeof out === 'string' && out.trim()) ? out.trim() : null;
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
144
+
145
+ // Pure-filesystem fallback (no git spawn). Does NOT see core.hooksPath — only used when
146
+ // git could not answer.
147
+ function resolveGitHooksDirFromFs(projectRoot) {
148
+ const gitPath = path.join(projectRoot, '.git');
149
+ let stat;
150
+ try {
151
+ stat = fs.lstatSync(gitPath);
152
+ } catch {
153
+ return null;
154
+ }
155
+ if (stat.isDirectory()) {
156
+ return path.join(gitPath, 'hooks');
157
+ }
158
+ if (stat.isFile()) {
159
+ return resolveWorktreeHooksDir(gitPath, projectRoot);
160
+ }
161
+ return null;
162
+ }
163
+
164
+ // A linked worktree's `.git` is a file containing `gitdir: <path>`. Its hooks live in
165
+ // the common repo's hooks dir (resolved via the worktree's `commondir` file). Returns
166
+ // null when the pointer file is unreadable or malformed.
167
+ function resolveWorktreeHooksDir(gitFile, projectRoot) {
168
+ let content;
169
+ try {
170
+ content = fs.readFileSync(gitFile, 'utf8');
171
+ } catch {
172
+ return null;
173
+ }
174
+ const match = content.match(/gitdir:\s*(.+)/);
175
+ if (!match) return null;
176
+ const rawGitdir = match[1].trim();
177
+ const gitdir = path.isAbsolute(rawGitdir) ? rawGitdir : path.resolve(projectRoot, rawGitdir);
178
+ return path.join(resolveWorktreeCommonDir(gitdir), 'hooks');
179
+ }
180
+
181
+ // Follow the worktree's `commondir` pointer to the common git dir; fall back to the
182
+ // per-worktree gitdir when it is absent or unreadable.
183
+ function resolveWorktreeCommonDir(gitdir) {
184
+ const commonDirFile = path.join(gitdir, 'commondir');
185
+ try {
186
+ if (fs.existsSync(commonDirFile)) {
187
+ const rel = fs.readFileSync(commonDirFile, 'utf8').trim();
188
+ return path.isAbsolute(rel) ? rel : path.resolve(gitdir, rel);
189
+ }
190
+ } catch {
191
+ // fall through to the per-worktree gitdir
192
+ }
193
+ return gitdir;
194
+ }
195
+
196
+ const NATIVE_HOOK_BODIES = {
197
+ 'pre-commit': `#!/bin/sh
198
+ ${FORGE_NATIVE_HOOK_SENTINEL}
199
+ # Fallback TDD gate used when the lefthook binary is unavailable. Enforces the same
200
+ # check as the lefthook pre-commit job. Remove this file only if you install lefthook.
201
+ if [ -f ".forge/hooks/check-tdd.js" ]; then
202
+ node ".forge/hooks/check-tdd.js" || exit 1
203
+ fi
204
+ `,
205
+ 'pre-push': `#!/bin/sh
206
+ ${FORGE_NATIVE_HOOK_SENTINEL}
207
+ # Fallback pre-push gate used when the lefthook binary is unavailable. Runs your test
208
+ # script before pushing (a no-op when the project has none).
209
+ npm test --if-present || exit 1
210
+ `,
211
+ };
212
+
213
+ /**
214
+ * Install native `.git/hooks` pre-commit/pre-push scripts that invoke Forge's own
215
+ * TDD gate. This is the fallback that keeps enforcement live when the lefthook binary
216
+ * cannot be installed (e.g. a fresh consumer repo with no package.json), so `git
217
+ * commit` / `git push` are never silently unguarded.
218
+ *
219
+ * A pre-existing hook that is neither Forge-managed nor lefthook-managed is preserved:
220
+ * it is backed up to `<hook>.forge-backup` (once) and left in place (reported in
221
+ * `skipped`) rather than destroyed.
222
+ *
223
+ * @param {string} projectRoot - Absolute path to the project root.
224
+ * @returns {{ installed: boolean, method?: string, hooksDir?: string,
225
+ * written?: string[], skipped?: string[], reason?: string }}
226
+ */
227
+ function installNativeGitHooks(projectRoot) {
228
+ const hooksDir = resolveGitHooksDir(projectRoot);
229
+ if (!hooksDir) {
230
+ return { installed: false, reason: 'not-a-git-repo' };
231
+ }
232
+ try {
233
+ fs.mkdirSync(hooksDir, { recursive: true });
234
+ } catch (error) {
235
+ return { installed: false, reason: `hooks-dir-unwritable: ${error.message}` };
236
+ }
237
+
238
+ const written = [];
239
+ const skipped = [];
240
+ for (const [name, body] of Object.entries(NATIVE_HOOK_BODIES)) {
241
+ const outcome = writeNativeHook(path.join(hooksDir, name), body);
242
+ (outcome === 'written' ? written : skipped).push(name);
243
+ }
244
+
245
+ return {
246
+ installed: written.length > 0,
247
+ method: 'native',
248
+ hooksDir,
249
+ written,
250
+ skipped,
251
+ };
252
+ }
253
+
254
+ // Signatures that appear ONLY in a hook lefthook itself GENERATED — never in a user's own
255
+ // hook that merely runs `lefthook ...` among other steps. Lefthook's generated hooks define
256
+ // a `call_lefthook()` shim and honor the `LEFTHOOK`/`LEFTHOOK_BIN` env bypass; a custom hook
257
+ // invoking lefthook does not. Matching these (not the bare 'lefthook' substring) is what
258
+ // stops us from misclassifying a user's hook as a disposable generated artifact (F4 / kernel
259
+ // 269e5d05).
260
+ const LEFTHOOK_GENERATED_MARKERS = ['call_lefthook', 'LEFTHOOK_BIN'];
261
+
262
+ // True only for a hook lefthook GENERATED (safe to regenerate/overwrite), never for a user
263
+ // hook that merely invokes lefthook.
264
+ function isLefthookGeneratedHook(content) {
265
+ return LEFTHOOK_GENERATED_MARKERS.some((marker) => content.includes(marker));
266
+ }
267
+
268
+ // Write a single native hook. A pre-existing hook that is not Forge's own is ALWAYS backed
269
+ // up to `<hook>.forge-backup` first (once), so a user's hook is never destroyed without a
270
+ // copy — whether we preserve it (skip) or overwrite a disposable lefthook-generated one.
271
+ // Returns 'written' or 'skipped'.
272
+ function writeNativeHook(dest, body) {
273
+ if (shouldPreserveExistingHook(dest)) {
274
+ backupHookOnce(dest);
275
+ return 'skipped';
276
+ }
277
+ // Back up before overwriting an existing non-Forge hook (e.g. a lefthook-generated one, or
278
+ // anything a future classification change might reach) — never overwrite without a backup.
279
+ backupExistingNonForgeHook(dest);
280
+ fs.writeFileSync(dest, body, 'utf8');
281
+ try {
282
+ fs.chmodSync(dest, 0o755); // NOSONAR — hooks must be executable
283
+ } catch {
284
+ // Windows filesystems ignore the mode bits — non-fatal.
285
+ }
286
+ return 'written';
287
+ }
288
+
289
+ // A hook is ours to overwrite ONLY when it is missing, Forge-managed, or a genuine
290
+ // lefthook-GENERATED artifact. Anything else — including a user's custom hook that merely
291
+ // INVOKES lefthook among other steps — is preserved (F4 / kernel 269e5d05: a bare 'lefthook'
292
+ // substring test used to clobber such a user hook without a backup).
293
+ function shouldPreserveExistingHook(dest) {
294
+ if (!fs.existsSync(dest)) return false;
295
+ let existing;
296
+ try {
297
+ existing = fs.readFileSync(dest, 'utf8');
298
+ } catch {
299
+ existing = '';
300
+ }
301
+ if (existing.includes(FORGE_NATIVE_HOOK_SENTINEL)) return false; // our own hook
302
+ if (isLefthookGeneratedHook(existing)) return false; // disposable generated artifact
303
+ return true; // the user's / a third party's hook — preserve it
304
+ }
305
+
306
+ // Back up an existing hook that is NOT Forge's own before it is overwritten (once, never
307
+ // clobbering an existing backup). No-op when the file is missing/unreadable or already ours.
308
+ function backupExistingNonForgeHook(dest) {
309
+ let existing;
310
+ try {
311
+ existing = fs.readFileSync(dest, 'utf8');
312
+ } catch {
313
+ return; // missing (ENOENT) or unreadable — nothing to back up
314
+ }
315
+ if (!existing.includes(FORGE_NATIVE_HOOK_SENTINEL)) {
316
+ backupHookOnce(dest);
317
+ }
318
+ }
319
+
320
+ // Back up a hook to `<hook>.forge-backup` once (never clobber an existing backup).
321
+ function backupHookOnce(dest) {
322
+ const backup = `${dest}.forge-backup`;
323
+ try {
324
+ if (!fs.existsSync(backup)) fs.copyFileSync(dest, backup);
325
+ } catch {
326
+ // Best-effort backup; the caller still refuses to overwrite the original.
327
+ }
328
+ }
329
+
330
+ // The TDD gate script the native hook (and the lefthook pre-commit job) invokes, relative
331
+ // to the project root. The native hook guards its call with `if [ -f … ]`, so when this file
332
+ // is absent the hook runs but enforces nothing.
333
+ const FORGE_TDD_GATE_SCRIPT = path.join('.forge', 'hooks', 'check-tdd.js');
334
+
335
+ // Confirm the TDD gate script exists as a runnable (readable) regular file under projectRoot.
336
+ // Returns { ok: true } or { ok: false, reason } with a loud, specific reason.
337
+ function gateScriptStatus(projectRoot) {
338
+ const gatePath = path.join(projectRoot, FORGE_TDD_GATE_SCRIPT);
339
+ let stat;
340
+ try {
341
+ stat = fs.statSync(gatePath);
342
+ } catch {
343
+ return { ok: false, reason: `native pre-commit hook present but its gate script is missing: ${FORGE_TDD_GATE_SCRIPT}` };
344
+ }
345
+ if (!stat.isFile()) {
346
+ return { ok: false, reason: `native pre-commit hook gate script is not a regular file: ${FORGE_TDD_GATE_SCRIPT}` };
347
+ }
348
+ try {
349
+ fs.accessSync(gatePath, fs.constants.R_OK); // `node <script>` needs it readable
350
+ } catch {
351
+ return { ok: false, reason: `native pre-commit hook gate script is not readable: ${FORGE_TDD_GATE_SCRIPT}` };
352
+ }
353
+ return { ok: true };
354
+ }
355
+
356
+ /**
357
+ * Report — honestly — whether git hooks are actually active for the project, so
358
+ * `forge setup` can fail LOUDLY instead of silently no-op'ing. A hook counts as
359
+ * active only when a `pre-commit` hook exists AND is either lefthook-managed or
360
+ * carries Forge's native sentinel with its gate script present and runnable.
361
+ *
362
+ * @param {string} projectRoot - Absolute path to the project root.
363
+ * @returns {{ active: boolean, method: 'lefthook'|'native'|'none'|'unknown', reason?: string, hook?: string }}
364
+ */
365
+ function verifyHooksActive(projectRoot) {
366
+ const hooksDir = resolveGitHooksDir(projectRoot);
367
+ if (!hooksDir) {
368
+ return { active: false, method: 'none', reason: 'not a git repository' };
369
+ }
370
+ const preCommit = path.join(hooksDir, 'pre-commit');
371
+ if (!fs.existsSync(preCommit)) {
372
+ return { active: false, method: 'none', reason: 'no pre-commit hook installed' };
373
+ }
374
+ let body;
375
+ try {
376
+ body = fs.readFileSync(preCommit, 'utf8');
377
+ } catch (error) {
378
+ return { active: false, method: 'unknown', reason: `pre-commit unreadable: ${error.message}` };
379
+ }
380
+ // Check the Forge native sentinel FIRST: our native hook body mentions "lefthook"
381
+ // in a comment, so a substring test for lefthook would otherwise misclassify it.
382
+ if (body.includes(FORGE_NATIVE_HOOK_SENTINEL)) {
383
+ // F5 (kernel d96af31a): the native hook body only runs the gate when its script exists
384
+ // (`if [ -f ".forge/hooks/check-tdd.js" ]`). A present hook whose gate script is missing
385
+ // silently no-ops — so verify must confirm the script exists AND is runnable, else report
386
+ // NOT active (loud) rather than a false pass. installForgeHookScripts() copies check-tdd.js
387
+ // unconditionally, but silently continues if the packaged source was absent, so this is
388
+ // exactly the case a truthful verify has to catch.
389
+ const gate = gateScriptStatus(projectRoot);
390
+ if (!gate.ok) {
391
+ return { active: false, method: 'native', reason: gate.reason, hook: preCommit };
392
+ }
393
+ return { active: true, method: 'native', hook: preCommit };
394
+ }
395
+ if (body.includes('lefthook')) {
396
+ return { active: true, method: 'lefthook', hook: preCommit };
397
+ }
398
+ return {
399
+ active: false,
400
+ method: 'unknown',
401
+ reason: 'pre-commit hook present but not Forge- or lefthook-managed',
402
+ hook: preCommit,
403
+ };
404
+ }
405
+
406
+ module.exports = {
407
+ FORGE_NATIVE_HOOK_SENTINEL,
408
+ FORGE_USER_LEFTHOOK_YML,
409
+ forgeShouldWriteLefthookConfig,
410
+ resolveGitHooksDir,
411
+ installNativeGitHooks,
412
+ verifyHooksActive,
413
+ };