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
package/lib/reset.js CHANGED
@@ -1,29 +1,33 @@
1
1
  const fs = require('node:fs');
2
2
  const path = require('node:path');
3
+ const { listCanonicalSkills } = require('./skills-sync');
4
+ const { getPackageRoot } = require('./package-root');
5
+
6
+ /**
7
+ * Package root that ships the canonical `skills/` source. Used to determine
8
+ * which skill directories Forge itself installed (so user/third-party skills
9
+ * sharing a skills dir are not removed during reset).
10
+ */
11
+ const FORGE_PACKAGE_ROOT = path.resolve(__dirname, '..');
3
12
 
4
13
  /**
5
14
  * Known forge-created file patterns.
6
15
  * Used to distinguish forge files from user-created files.
7
16
  */
8
- const FORGE_COMMANDS = [
9
- '.claude/commands/plan.md',
10
- '.claude/commands/dev.md',
11
- '.claude/commands/validate.md',
12
- '.claude/commands/ship.md',
13
- '.claude/commands/review.md',
14
- '.claude/commands/premerge.md',
15
- '.claude/commands/verify.md',
16
- '.claude/commands/status.md',
17
- '.claude/commands/preflight.md',
17
+ const FORGE_SKILL_DIRS = [
18
+ '.agents/skills',
19
+ '.claude/skills',
20
+ '.cursor/skills',
21
+ '.codex/skills',
18
22
  ];
19
23
 
20
24
  const FORGE_RULES = [
21
25
  '.claude/rules/workflow.md',
22
- '.claude/rules/greptile-review-process.md',
26
+ '.claude/rules/review-process.md',
23
27
  ];
24
28
 
25
29
  const FORGE_SCRIPTS = [
26
- '.claude/scripts/greptile-resolve.sh',
30
+ '.claude/scripts/review-resolve.sh',
27
31
  '.claude/scripts/validate.sh',
28
32
  ];
29
33
 
@@ -39,11 +43,7 @@ const FORGE_DOCS = [
39
43
 
40
44
  const FORGE_AGENT_DIRS = [
41
45
  '.cursor',
42
- '.cline',
43
- '.roo',
44
46
  '.codex',
45
- '.kilocode',
46
- '.opencode',
47
47
  ];
48
48
 
49
49
  const FORGE_SYNC_SCRIPTS_DIR = 'scripts/github-beads-sync';
@@ -68,7 +68,7 @@ const FORGE_SYNC_SCRIPTS_DIR = 'scripts/github-beads-sync';
68
68
  function getForgeFiles(projectRoot) {
69
69
  const result = {
70
70
  config: [],
71
- commands: [],
71
+ skillDirs: [],
72
72
  rules: [],
73
73
  scripts: [],
74
74
  docs: [],
@@ -82,30 +82,11 @@ function getForgeFiles(projectRoot) {
82
82
  result.config.push('.forge');
83
83
  }
84
84
 
85
- // Known forge commands (static list)
86
- for (const f of FORGE_COMMANDS) {
87
- if (fs.existsSync(path.join(projectRoot, f))) {
88
- result.commands.push(f);
89
- }
90
- }
91
-
92
- // Dynamic discovery: scan the package's .claude/commands/ directory to find
93
- // any commands that setup may have copied but aren't in the static list.
94
- // Only files that exist in both the package source AND the project are included.
95
- const packageCommandsDir = path.join(__dirname, '..', '.claude', 'commands');
96
- try {
97
- const packageEntries = fs.readdirSync(packageCommandsDir);
98
- for (const entry of packageEntries) {
99
- if (entry.endsWith('.md')) {
100
- const relPath = `.claude/commands/${entry}`;
101
- if (!result.commands.includes(relPath) &&
102
- fs.existsSync(path.join(projectRoot, relPath))) {
103
- result.commands.push(relPath);
104
- }
105
- }
85
+ // Known forge skill directories
86
+ for (const dir of FORGE_SKILL_DIRS) {
87
+ if (fs.existsSync(path.join(projectRoot, dir))) {
88
+ result.skillDirs.push(dir);
106
89
  }
107
- } catch (_error) {
108
- // Package commands directory read failed, skip
109
90
  }
110
91
 
111
92
  // Known forge rules
@@ -184,10 +165,10 @@ function resetSoft(projectRoot, { force = false } = {}) {
184
165
 
185
166
  // Record what was preserved
186
167
  const inventory = getForgeFiles(projectRoot);
187
- for (const f of [...inventory.commands, ...inventory.rules, ...inventory.scripts]) {
168
+ for (const f of [...inventory.rules, ...inventory.scripts]) {
188
169
  preserved.push(f);
189
170
  }
190
- for (const dir of inventory.agentDirs) {
171
+ for (const dir of [...inventory.skillDirs, ...inventory.agentDirs]) {
191
172
  preserved.push(dir);
192
173
  }
193
174
 
@@ -203,11 +184,16 @@ function resetSoft(projectRoot, { force = false } = {}) {
203
184
  * @param {{ force?: boolean }} options
204
185
  * @returns {{ removed: string[], preserved: string[] }}
205
186
  */
206
- function resetHard(projectRoot, { force = false } = {}) {
187
+ function resetHard(projectRoot, { force = false, sourceRoot } = {}) {
207
188
  if (!force) {
208
189
  throw new Error('Hard reset requires --force flag. This will remove ALL forge files.');
209
190
  }
210
191
 
192
+ // Resolve the package/asset root AFTER the force guard so an unforced reset
193
+ // never triggers extraction work or fails with an asset-resolution error
194
+ // instead of the intended safety message.
195
+ const resolvedSourceRoot = sourceRoot ?? getPackageRoot(FORGE_PACKAGE_ROOT);
196
+
211
197
  const removed = [];
212
198
  const preserved = [];
213
199
  const inventory = getForgeFiles(projectRoot);
@@ -223,7 +209,6 @@ function resetHard(projectRoot, { force = false } = {}) {
223
209
 
224
210
  // Remove individual forge files
225
211
  const allFiles = [
226
- ...inventory.commands,
227
212
  ...inventory.rules,
228
213
  ...inventory.scripts,
229
214
  ...inventory.docs,
@@ -260,6 +245,35 @@ function resetHard(projectRoot, { force = false } = {}) {
260
245
  }
261
246
  }
262
247
 
248
+ // Remove Forge-managed skill directories ONLY.
249
+ // Shared skills dirs (e.g. .claude/skills) can also hold user/third-party
250
+ // skills. Scope removal to the canonical Forge skill set so user work is
251
+ // preserved; the surrounding dir is removed only if nothing else remains.
252
+ const forgeSkillNames = new Set(
253
+ listCanonicalSkills(resolvedSourceRoot).map((s) => s.name)
254
+ );
255
+ for (const dir of inventory.skillDirs) {
256
+ const skillsRoot = path.join(projectRoot, dir);
257
+ if (!fs.existsSync(skillsRoot)) continue;
258
+
259
+ for (const entry of fs.readdirSync(skillsRoot, { withFileTypes: true })) {
260
+ if (!entry.isDirectory()) continue;
261
+ if (!forgeSkillNames.has(entry.name)) continue;
262
+ fs.rmSync(path.join(skillsRoot, entry.name), { recursive: true, force: true });
263
+ removed.push([dir, entry.name].join('/'));
264
+ }
265
+
266
+ // Drop the now-empty skills dir, but keep it if user/third-party skills remain.
267
+ try {
268
+ if (fs.readdirSync(skillsRoot).length === 0) {
269
+ fs.rmSync(skillsRoot, { recursive: true, force: true });
270
+ removed.push(dir);
271
+ }
272
+ } catch (_error) {
273
+ // Directory read failed, skip
274
+ }
275
+ }
276
+
263
277
  // Remove agent directories
264
278
  for (const dir of inventory.agentDirs) {
265
279
  const fullPath = path.join(projectRoot, dir);
@@ -300,7 +314,7 @@ module.exports = {
300
314
  resetSoft,
301
315
  resetHard,
302
316
  reinstall,
303
- FORGE_COMMANDS,
317
+ FORGE_SKILL_DIRS,
304
318
  FORGE_RULES,
305
319
  FORGE_SCRIPTS,
306
320
  FORGE_DOCS,
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ const REQUIRED_REVIEW_ADAPTER_METHODS = [
4
+ 'fetchThreads',
5
+ 'parse',
6
+ 'reply',
7
+ 'resolve',
8
+ 'score',
9
+ ];
10
+
11
+ class ReviewAdapter {
12
+ constructor(options = {}) {
13
+ this.id = options.id || 'review-adapter';
14
+ this.kind = options.kind || 'review';
15
+ this.name = options.name || this.id;
16
+ this.version = options.version || '0.1.0';
17
+ }
18
+
19
+ async fetchThreads() {
20
+ throw new Error(`${this.id}.fetchThreads is not implemented`);
21
+ }
22
+
23
+ parse() {
24
+ throw new Error(`${this.id}.parse is not implemented`);
25
+ }
26
+
27
+ async reply() {
28
+ throw new Error(`${this.id}.reply is not implemented`);
29
+ }
30
+
31
+ async resolve() {
32
+ throw new Error(`${this.id}.resolve is not implemented`);
33
+ }
34
+
35
+ score() {
36
+ throw new Error(`${this.id}.score is not implemented`);
37
+ }
38
+ }
39
+
40
+ function validateReviewAdapter(adapter) {
41
+ const errors = [];
42
+
43
+ if (!adapter || typeof adapter !== 'object') {
44
+ return { valid: false, errors: ['adapter must be an object'] };
45
+ }
46
+
47
+ if (!adapter.id || typeof adapter.id !== 'string') {
48
+ errors.push('id must be a non-empty string');
49
+ }
50
+
51
+ if (adapter.kind !== 'review') {
52
+ errors.push('kind must be "review"');
53
+ }
54
+
55
+ for (const method of REQUIRED_REVIEW_ADAPTER_METHODS) {
56
+ if (typeof adapter[method] !== 'function') {
57
+ errors.push(`${method} must be a function`);
58
+ }
59
+ }
60
+
61
+ return { valid: errors.length === 0, errors };
62
+ }
63
+
64
+ module.exports = {
65
+ ReviewAdapter,
66
+ REQUIRED_REVIEW_ADAPTER_METHODS,
67
+ validateReviewAdapter,
68
+ };
@@ -0,0 +1,260 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Canonical rule source → Cursor native rule renderer.
5
+ *
6
+ * Forge policy rules (workflow, TDD, security, documentation) have ONE canonical
7
+ * source in `rules/<name>.md`. Each canonical file is a THIN POINTER: a short
8
+ * frontmatter (`description`, `globs`, `alwaysApply`) plus a few lines that point
9
+ * at `AGENTS.md` and the relevant stage skill. The full policy bodies are NOT
10
+ * duplicated here — they live in `AGENTS.md` and `skills/<stage>/SKILL.md`.
11
+ *
12
+ * Only ONE harness has a first-class native rule surface, so only one renderer
13
+ * exists:
14
+ *
15
+ * - Cursor : `.cursor/rules/<name>.mdc` (MDC frontmatter + pointer body)
16
+ *
17
+ * Claude, Codex, and Hermes receive this same policy through their INSTRUCTION
18
+ * projection (`CLAUDE.md` → `AGENTS.md`, `AGENTS.md`, `forge orient`) — adding
19
+ * always-on `.claude/rules/*.md` files would triple-deliver the same policy into
20
+ * every session as pure token bloat. See `lib/harness-capability-matrix.js`
21
+ * (the `rules` row) and `docs/reference/AGENT_SKILL_PARITY.md`.
22
+ *
23
+ * The generated `.cursor/rules/*.mdc` files are NOT committed — they are produced
24
+ * at `forge setup` from the canonical source (like the gitignored, setup-populated
25
+ * skill dirs). The structural drift gate regenerates them into a temp dir and
26
+ * asserts the render is deterministic and well-formed, rather than committing a
27
+ * mirror. Never hand-edit a generated `.mdc` — edit `rules/<name>.md`.
28
+ *
29
+ * Dependency-free (no YAML/markdown deps) so it runs under `bun test` and the
30
+ * release gates without pulling `@forge/*` workspaces.
31
+ *
32
+ * @module rules-sync
33
+ */
34
+
35
+ const fs = require('node:fs');
36
+ const path = require('node:path');
37
+
38
+ /** Canonical rule source directory (relative to a repo/package root). */
39
+ const CANONICAL_RULES_DIR = 'rules';
40
+
41
+ /** Cursor native rule surface (relative to a project root). */
42
+ const CURSOR_RULES_DIR = '.cursor/rules';
43
+
44
+ /** Canonical rule name → Cursor `.mdc` filename. */
45
+ const CURSOR_RULE_FILES = {
46
+ workflow: 'forge-workflow.mdc',
47
+ tdd: 'tdd-enforcement.mdc',
48
+ security: 'security-scanning.mdc',
49
+ documentation: 'documentation.mdc',
50
+ 'kernel-tracking': 'kernel-tracking.mdc',
51
+ };
52
+
53
+ const CANONICAL_RULE_NAMES = Object.keys(CURSOR_RULE_FILES);
54
+
55
+ const GENERATED_NOTE =
56
+ 'Generated by Forge from the canonical rules/ source. Do not edit — edit ' +
57
+ 'rules/<name>.md and re-run `forge setup` (or the rules renderer).';
58
+
59
+ // Stable substring stamped into every generated rule file. Used to distinguish a
60
+ // Forge-managed generated `.mdc` (safe to refresh on upgrade) from a user-authored
61
+ // rule (must never be clobbered).
62
+ const GENERATED_RULE_MARKER = 'Generated by Forge from the canonical rules/ source';
63
+
64
+ /**
65
+ * Parse the minimal YAML frontmatter Forge authors in canonical rule files.
66
+ * Only flat `key: value` pairs are supported (values kept as raw strings so
67
+ * arrays/booleans round-trip verbatim into the rendered output).
68
+ *
69
+ * @param {string} raw
70
+ * @returns {{ meta: Record<string,string>, body: string }}
71
+ */
72
+ function parseFrontmatter(raw) {
73
+ const norm = String(raw).replace(/\r\n/g, '\n');
74
+ if (!norm.startsWith('---\n')) {
75
+ return { meta: {}, body: norm };
76
+ }
77
+ const end = norm.indexOf('\n---\n', 4);
78
+ if (end === -1) {
79
+ return { meta: {}, body: norm };
80
+ }
81
+ const block = norm.slice(4, end);
82
+ const body = norm.slice(end + 5);
83
+ const meta = {};
84
+ for (const line of block.split('\n')) {
85
+ const m = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
86
+ if (m) meta[m[1]] = m[2].trim();
87
+ }
88
+ return { meta, body };
89
+ }
90
+
91
+ /** Body with leading blank lines removed and a single trailing newline. */
92
+ function normalizeBody(body) {
93
+ return body.replace(/^\n+/, '').replace(/\s+$/, '') + '\n';
94
+ }
95
+
96
+ /**
97
+ * Read one canonical rule from disk into a render-ready descriptor.
98
+ *
99
+ * @param {string} sourceRoot - Repo/package root containing `rules/`.
100
+ * @param {string} name - Canonical rule name (stem of `rules/<name>.md`).
101
+ * @returns {{ name, description, globs, alwaysApply, body }}
102
+ */
103
+ function readCanonicalRule(sourceRoot, name) {
104
+ const filePath = path.join(sourceRoot, CANONICAL_RULES_DIR, `${name}.md`);
105
+ const raw = fs.readFileSync(filePath, 'utf-8');
106
+ const { meta, body } = parseFrontmatter(raw);
107
+ return {
108
+ name,
109
+ description: meta.description || '""',
110
+ globs: meta.globs || '[]',
111
+ alwaysApply: meta.alwaysApply || 'false',
112
+ body: normalizeBody(body),
113
+ };
114
+ }
115
+
116
+ /**
117
+ * List all canonical rules (in stable CURSOR_RULE_FILES order).
118
+ *
119
+ * @param {string} sourceRoot - Repo/package root containing `rules/`.
120
+ * @param {{ only?: Iterable<string> }} [options]
121
+ * @returns {Array<ReturnType<typeof readCanonicalRule>>}
122
+ */
123
+ function listCanonicalRules(sourceRoot, options = {}) {
124
+ const only = options.only ? new Set(options.only) : null;
125
+ return CANONICAL_RULE_NAMES
126
+ .filter((name) => !only || only.has(name))
127
+ .map((name) => readCanonicalRule(sourceRoot, name));
128
+ }
129
+
130
+ /** Render the Cursor native rule (`.mdc` — MDC frontmatter + pointer body). */
131
+ function renderCursorRule(rule) {
132
+ const frontmatter = [
133
+ '---',
134
+ `description: ${rule.description}`,
135
+ `globs: ${rule.globs}`,
136
+ `alwaysApply: ${rule.alwaysApply}`,
137
+ '---',
138
+ '',
139
+ `<!-- ${GENERATED_NOTE} -->`,
140
+ '',
141
+ '',
142
+ ].join('\n');
143
+ return frontmatter + rule.body;
144
+ }
145
+
146
+ /**
147
+ * Render every canonical rule to Cursor `.mdc` content, keyed by output filename.
148
+ * Pure with respect to the target filesystem (only reads canonical `rules/`),
149
+ * so callers can diff two renders to prove determinism.
150
+ *
151
+ * @param {string} sourceRoot - Root containing canonical `rules/`.
152
+ * @param {{ only?: Iterable<string> }} [options]
153
+ * @returns {Record<string, string>} filename → rendered content
154
+ */
155
+ function renderCursorRuleMap(sourceRoot, options = {}) {
156
+ const map = {};
157
+ for (const rule of listCanonicalRules(sourceRoot, options)) {
158
+ map[CURSOR_RULE_FILES[rule.name]] = renderCursorRule(rule);
159
+ }
160
+ return map;
161
+ }
162
+
163
+ /**
164
+ * Render canonical rules into a project's Cursor rule directory.
165
+ *
166
+ * @param {object} params
167
+ * @param {string} params.sourceRoot - Root containing canonical `rules/`.
168
+ * @param {string} params.targetRoot - Project root to write native rules under.
169
+ * @param {'cursor'} [params.harness='cursor'] - Only Cursor has a native rule surface.
170
+ * @param {boolean} [params.overwrite=false] - Overwrite existing files.
171
+ * @param {Iterable<string>} [params.only] - Restrict to these canonical names.
172
+ * @returns {{ written: string[], skipped: string[] }}
173
+ */
174
+ function renderRulesForHarness({ sourceRoot, targetRoot, harness = 'cursor', overwrite = false, only }) {
175
+ if (harness !== 'cursor') {
176
+ throw new Error(
177
+ `No native rule surface for harness "${harness}"; Claude/Codex/Hermes ` +
178
+ 'receive rule policy through their AGENTS.md instruction projection.',
179
+ );
180
+ }
181
+ const outDir = path.join(targetRoot, CURSOR_RULES_DIR);
182
+ fs.mkdirSync(outDir, { recursive: true });
183
+
184
+ const map = renderCursorRuleMap(sourceRoot, { only });
185
+ const written = [];
186
+ const skipped = [];
187
+ for (const [file, content] of Object.entries(map)) {
188
+ const outPath = path.join(outDir, file);
189
+ if (!overwrite && fs.existsSync(outPath)) {
190
+ // Refresh Forge-managed generated files even without --overwrite so an
191
+ // UPGRADE (`forge setup` on an existing project) picks up canonical changes
192
+ // and a stale drifted rule cannot survive forever. A user-authored rule
193
+ // (no generated marker) is respected and left untouched.
194
+ const existing = fs.readFileSync(outPath, 'utf-8');
195
+ if (!existing.includes(GENERATED_RULE_MARKER)) {
196
+ skipped.push(file);
197
+ continue;
198
+ }
199
+ }
200
+ fs.writeFileSync(outPath, content, 'utf-8');
201
+ written.push(file);
202
+ }
203
+ return { written, skipped };
204
+ }
205
+
206
+ /**
207
+ * Structural drift gate for rules: regenerate into a temp dir and assert the
208
+ * render is deterministic and well-formed. No committed mirror is compared —
209
+ * the canonical `rules/` source is the only tracked artifact, and Cursor rules
210
+ * are setup-populated (like the gitignored skill dirs).
211
+ *
212
+ * @param {object} params
213
+ * @param {string} params.sourceRoot - Root containing canonical `rules/`.
214
+ * @returns {{ ok: boolean, issues: object[], files: string[] }}
215
+ */
216
+ function checkRulesRenderDeterminism({ sourceRoot }) {
217
+ const issues = [];
218
+ const first = renderCursorRuleMap(sourceRoot);
219
+ const second = renderCursorRuleMap(sourceRoot);
220
+ const files = Object.keys(first);
221
+
222
+ for (const file of files) {
223
+ if (first[file] !== second[file]) {
224
+ issues.push({ file, status: 'non-deterministic' });
225
+ continue;
226
+ }
227
+ const content = first[file];
228
+ if (!content.startsWith('---\n')) {
229
+ issues.push({ file, status: 'missing-frontmatter' });
230
+ }
231
+ if (!content.includes('description:')) {
232
+ issues.push({ file, status: 'missing-description' });
233
+ }
234
+ }
235
+
236
+ // Every canonical rule must map to exactly one Cursor output file.
237
+ for (const name of CANONICAL_RULE_NAMES) {
238
+ if (!files.includes(CURSOR_RULE_FILES[name])) {
239
+ issues.push({ file: CURSOR_RULE_FILES[name], status: 'unrendered-canonical' });
240
+ }
241
+ }
242
+
243
+ return { ok: issues.length === 0, issues, files };
244
+ }
245
+
246
+ module.exports = {
247
+ CANONICAL_RULES_DIR,
248
+ CURSOR_RULES_DIR,
249
+ CURSOR_RULE_FILES,
250
+ CANONICAL_RULE_NAMES,
251
+ GENERATED_NOTE,
252
+ GENERATED_RULE_MARKER,
253
+ parseFrontmatter,
254
+ listCanonicalRules,
255
+ readCanonicalRule,
256
+ renderCursorRule,
257
+ renderCursorRuleMap,
258
+ renderRulesForHarness,
259
+ checkRulesRenderDeterminism,
260
+ };