@bonesofspring/ai-rules 0.2.23 → 0.2.25

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 (287) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +20 -5
  3. package/bin/cli.js +121 -15
  4. package/bin/mavis-install.js +5 -1
  5. package/bin/opencode-install.js +535 -0
  6. package/package.json +10 -3
  7. package/presets/_shared/core/agent-team/agent-team-orchestrator.md +2 -2
  8. package/presets/_shared/core/meta/preset-layering.md +3 -3
  9. package/presets/_shared/core/meta/preset-no-cross-stack-leakage.md +1 -1
  10. package/presets/_shared/core/meta/preset-pr-checklist.md +4 -3
  11. package/presets/_shared/core/meta/preset-token-budget.md +3 -0
  12. package/presets/_shared/core/meta/preset-twin-sync.md +16 -0
  13. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-layering.md +3 -3
  14. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  15. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  16. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-token-budget.md +3 -0
  17. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-twin-sync.md +16 -0
  18. package/presets/claude/go/rules/tooling-and-review/preset-layering.md +3 -3
  19. package/presets/claude/go/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  20. package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  21. package/presets/claude/go/rules/tooling-and-review/preset-token-budget.md +3 -0
  22. package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +16 -0
  23. package/presets/claude/ios-swift/rules/tooling-and-review/preset-layering.md +3 -3
  24. package/presets/claude/ios-swift/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  25. package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  26. package/presets/claude/ios-swift/rules/tooling-and-review/preset-token-budget.md +3 -0
  27. package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +16 -0
  28. package/presets/claude/java/rules/tooling-and-review/preset-layering.md +3 -3
  29. package/presets/claude/java/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  30. package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  31. package/presets/claude/java/rules/tooling-and-review/preset-token-budget.md +3 -0
  32. package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +16 -0
  33. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-layering.md +3 -3
  34. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  35. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  36. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-token-budget.md +3 -0
  37. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +16 -0
  38. package/presets/claude/next/REPO_AGENTS.md +34 -8
  39. package/presets/claude/next/agents/build-verifier.md +3 -1
  40. package/presets/claude/next/rules/tooling-and-review/ai-local-overlay.md +1 -1
  41. package/presets/claude/next/rules/tooling-and-review/preset-layering.md +3 -3
  42. package/presets/claude/next/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  43. package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  44. package/presets/claude/next/rules/tooling-and-review/preset-token-budget.md +3 -0
  45. package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +16 -0
  46. package/presets/claude/nuxt/rules/tooling-and-review/preset-layering.md +3 -3
  47. package/presets/claude/nuxt/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  48. package/presets/claude/nuxt/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  49. package/presets/claude/nuxt/rules/tooling-and-review/preset-token-budget.md +3 -0
  50. package/presets/claude/nuxt/rules/tooling-and-review/preset-twin-sync.md +16 -0
  51. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-layering.md +3 -3
  52. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  53. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  54. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-token-budget.md +3 -0
  55. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +16 -0
  56. package/presets/claude/php-laravel/rules/tooling-and-review/preset-layering.md +3 -3
  57. package/presets/claude/php-laravel/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  58. package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  59. package/presets/claude/php-laravel/rules/tooling-and-review/preset-token-budget.md +3 -0
  60. package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +16 -0
  61. package/presets/claude/svelte/rules/tooling-and-review/preset-layering.md +3 -3
  62. package/presets/claude/svelte/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  63. package/presets/claude/svelte/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  64. package/presets/claude/svelte/rules/tooling-and-review/preset-token-budget.md +3 -0
  65. package/presets/claude/svelte/rules/tooling-and-review/preset-twin-sync.md +16 -0
  66. package/presets/cursor/android-kotlin/rules/preset-layering.mdc +3 -3
  67. package/presets/cursor/android-kotlin/rules/preset-no-cross-stack-leakage.mdc +1 -1
  68. package/presets/cursor/android-kotlin/rules/preset-pr-checklist.mdc +4 -3
  69. package/presets/cursor/android-kotlin/rules/preset-token-budget.mdc +3 -0
  70. package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +16 -0
  71. package/presets/cursor/go/rules/preset-layering.mdc +3 -3
  72. package/presets/cursor/go/rules/preset-no-cross-stack-leakage.mdc +1 -1
  73. package/presets/cursor/go/rules/preset-pr-checklist.mdc +4 -3
  74. package/presets/cursor/go/rules/preset-token-budget.mdc +3 -0
  75. package/presets/cursor/go/rules/preset-twin-sync.mdc +16 -0
  76. package/presets/cursor/ios-swift/rules/preset-layering.mdc +3 -3
  77. package/presets/cursor/ios-swift/rules/preset-no-cross-stack-leakage.mdc +1 -1
  78. package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +4 -3
  79. package/presets/cursor/ios-swift/rules/preset-token-budget.mdc +3 -0
  80. package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +16 -0
  81. package/presets/cursor/java/rules/preset-layering.mdc +3 -3
  82. package/presets/cursor/java/rules/preset-no-cross-stack-leakage.mdc +1 -1
  83. package/presets/cursor/java/rules/preset-pr-checklist.mdc +4 -3
  84. package/presets/cursor/java/rules/preset-token-budget.mdc +3 -0
  85. package/presets/cursor/java/rules/preset-twin-sync.mdc +16 -0
  86. package/presets/cursor/mcp-ts/rules/preset-layering.mdc +3 -3
  87. package/presets/cursor/mcp-ts/rules/preset-no-cross-stack-leakage.mdc +1 -1
  88. package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +4 -3
  89. package/presets/cursor/mcp-ts/rules/preset-token-budget.mdc +3 -0
  90. package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +16 -0
  91. package/presets/cursor/next/REPO_AGENTS.md +26 -8
  92. package/presets/cursor/next/agents/build-verifier.md +3 -1
  93. package/presets/cursor/next/rules/ai-local-overlay.mdc +1 -1
  94. package/presets/cursor/next/rules/preset-layering.mdc +3 -3
  95. package/presets/cursor/next/rules/preset-no-cross-stack-leakage.mdc +1 -1
  96. package/presets/cursor/next/rules/preset-pr-checklist.mdc +4 -3
  97. package/presets/cursor/next/rules/preset-token-budget.mdc +3 -0
  98. package/presets/cursor/next/rules/preset-twin-sync.mdc +16 -0
  99. package/presets/cursor/nuxt/rules/preset-layering.mdc +3 -3
  100. package/presets/cursor/nuxt/rules/preset-no-cross-stack-leakage.mdc +1 -1
  101. package/presets/cursor/nuxt/rules/preset-pr-checklist.mdc +4 -3
  102. package/presets/cursor/nuxt/rules/preset-token-budget.mdc +3 -0
  103. package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +16 -0
  104. package/presets/cursor/php-hexagonal/rules/preset-layering.mdc +3 -3
  105. package/presets/cursor/php-hexagonal/rules/preset-no-cross-stack-leakage.mdc +1 -1
  106. package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +4 -3
  107. package/presets/cursor/php-hexagonal/rules/preset-token-budget.mdc +3 -0
  108. package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +16 -0
  109. package/presets/cursor/php-laravel/rules/preset-layering.mdc +3 -3
  110. package/presets/cursor/php-laravel/rules/preset-no-cross-stack-leakage.mdc +1 -1
  111. package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +4 -3
  112. package/presets/cursor/php-laravel/rules/preset-token-budget.mdc +3 -0
  113. package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +16 -0
  114. package/presets/cursor/svelte/rules/preset-layering.mdc +3 -3
  115. package/presets/cursor/svelte/rules/preset-no-cross-stack-leakage.mdc +1 -1
  116. package/presets/cursor/svelte/rules/preset-pr-checklist.mdc +4 -3
  117. package/presets/cursor/svelte/rules/preset-token-budget.mdc +3 -0
  118. package/presets/cursor/svelte/rules/preset-twin-sync.mdc +16 -0
  119. package/presets/mavis/next/MAVIS.md +1 -0
  120. package/presets/mavis/next/REPO_AGENTS.md +28 -48
  121. package/presets/mavis/next/adapter-map.json +3 -3
  122. package/presets/mavis/next/agents/build-verifier.md +3 -1
  123. package/presets/mavis/next/rules/agent-team-orchestrator.md +2 -2
  124. package/presets/mavis/next/rules/ai-local-overlay.md +1 -1
  125. package/presets/mavis/next/rules/preset-layering.md +3 -3
  126. package/presets/mavis/next/rules/preset-no-cross-stack-leakage.md +1 -1
  127. package/presets/mavis/next/rules/preset-pr-checklist.md +4 -3
  128. package/presets/mavis/next/rules/preset-token-budget.md +3 -0
  129. package/presets/mavis/next/rules/preset-twin-sync.md +16 -0
  130. package/presets/opencode/README.md +61 -0
  131. package/presets/opencode/next/MCP.md +36 -0
  132. package/presets/opencode/next/OPENCODE.md +65 -0
  133. package/presets/opencode/next/README.md +16 -0
  134. package/presets/opencode/next/REPO_AGENTS.md +74 -0
  135. package/presets/opencode/next/adapter-map.json +959 -0
  136. package/presets/opencode/next/agents/README.md +201 -0
  137. package/presets/opencode/next/agents/accessibility-reviewer.md +73 -0
  138. package/presets/opencode/next/agents/api-contract-reviewer.md +69 -0
  139. package/presets/opencode/next/agents/build-verifier.md +124 -0
  140. package/presets/opencode/next/agents/ci-investigator.md +71 -0
  141. package/presets/opencode/next/agents/code-reviewer.md +73 -0
  142. package/presets/opencode/next/agents/codebase-analyzer.md +33 -0
  143. package/presets/opencode/next/agents/debugger.md +88 -0
  144. package/presets/opencode/next/agents/feature-developer.md +109 -0
  145. package/presets/opencode/next/agents/migration-specialist.md +79 -0
  146. package/presets/opencode/next/agents/mockup-reviewer.md +65 -0
  147. package/presets/opencode/next/agents/openspec-author.md +169 -0
  148. package/presets/opencode/next/agents/performance-auditor.md +80 -0
  149. package/presets/opencode/next/agents/playwright-test-generator.md +36 -0
  150. package/presets/opencode/next/agents/playwright-test-healer.md +36 -0
  151. package/presets/opencode/next/agents/playwright-test-planner.md +33 -0
  152. package/presets/opencode/next/agents/qa-tester.md +60 -0
  153. package/presets/opencode/next/agents/security-reviewer.md +66 -0
  154. package/presets/opencode/next/agents/solution-architect.md +80 -0
  155. package/presets/opencode/next/agents/task-analyst.md +178 -0
  156. package/presets/opencode/next/agents/task-router.md +190 -0
  157. package/presets/opencode/next/agents/tech-writer.md +69 -0
  158. package/presets/opencode/next/agents/unit-test-generator.md +39 -0
  159. package/presets/opencode/next/agents/unit-test-healer.md +40 -0
  160. package/presets/opencode/next/agents/unit-test-planner.md +66 -0
  161. package/presets/opencode/next/commands/README.md +49 -0
  162. package/presets/opencode/next/commands/spec-start.md +32 -0
  163. package/presets/opencode/next/commands/task-continue.md +58 -0
  164. package/presets/opencode/next/commands/task.md +58 -0
  165. package/presets/opencode/next/commands/technical-retro.md +88 -0
  166. package/presets/opencode/next/hooks/README.md +22 -0
  167. package/presets/opencode/next/hooks/chain-team-phases.sh +845 -0
  168. package/presets/opencode/next/opencode.json +33 -0
  169. package/presets/opencode/next/rules/README.md +20 -0
  170. package/presets/opencode/next/rules/agent-team-intake.md +11 -0
  171. package/presets/opencode/next/rules/agent-team-orchestrator.md +214 -0
  172. package/presets/opencode/next/rules/ai-local-overlay.md +37 -0
  173. package/presets/opencode/next/rules/anti-sycophancy-discipline.md +23 -0
  174. package/presets/opencode/next/rules/api-services.md +72 -0
  175. package/presets/opencode/next/rules/architecture-boundaries-ui.md +9 -0
  176. package/presets/opencode/next/rules/architecture-boundaries.md +86 -0
  177. package/presets/opencode/next/rules/arrow-functions.md +25 -0
  178. package/presets/opencode/next/rules/code-quality-and-refactoring.md +40 -0
  179. package/presets/opencode/next/rules/code-review-mr.md +40 -0
  180. package/presets/opencode/next/rules/css-property-order-stylelint.md +5 -0
  181. package/presets/opencode/next/rules/design-guidance.md +99 -0
  182. package/presets/opencode/next/rules/feature-delivery-workflow.md +49 -0
  183. package/presets/opencode/next/rules/http-client.md +55 -0
  184. package/presets/opencode/next/rules/layer-barrel-exports.md +68 -0
  185. package/presets/opencode/next/rules/mcp-usage.md +86 -0
  186. package/presets/opencode/next/rules/mockup-review.md +107 -0
  187. package/presets/opencode/next/rules/navigation-router-stack.md +56 -0
  188. package/presets/opencode/next/rules/navigation-router-ui.md +10 -0
  189. package/presets/opencode/next/rules/next-app-core.md +29 -0
  190. package/presets/opencode/next/rules/next-app-router.md +44 -0
  191. package/presets/opencode/next/rules/no-props-spread.md +56 -0
  192. package/presets/opencode/next/rules/no-type-assertion-as-import-export.md +51 -0
  193. package/presets/opencode/next/rules/package-manager.md +11 -0
  194. package/presets/opencode/next/rules/playwright-agents.md +67 -0
  195. package/presets/opencode/next/rules/post-change-lint.md +35 -0
  196. package/presets/opencode/next/rules/preset-layering.md +27 -0
  197. package/presets/opencode/next/rules/preset-no-cross-stack-leakage.md +90 -0
  198. package/presets/opencode/next/rules/preset-pr-checklist.md +36 -0
  199. package/presets/opencode/next/rules/preset-token-budget.md +39 -0
  200. package/presets/opencode/next/rules/preset-twin-sync.md +81 -0
  201. package/presets/opencode/next/rules/product-specs-authoring.md +112 -0
  202. package/presets/opencode/next/rules/product-specs.md +56 -0
  203. package/presets/opencode/next/rules/public-imports.md +50 -0
  204. package/presets/opencode/next/rules/react-a11y-coding.md +31 -0
  205. package/presets/opencode/next/rules/react-next-anti-hallucination.md +60 -0
  206. package/presets/opencode/next/rules/react-ui.md +105 -0
  207. package/presets/opencode/next/rules/reference-features.md +45 -0
  208. package/presets/opencode/next/rules/security-next.md +45 -0
  209. package/presets/opencode/next/rules/store-rtk.md +78 -0
  210. package/presets/opencode/next/rules/technical-retro.md +11 -0
  211. package/presets/opencode/next/rules/tests-e2e-structure.md +72 -0
  212. package/presets/opencode/next/rules/tests-unit.md +72 -0
  213. package/presets/opencode/next/rules/types-jsdoc.md +43 -0
  214. package/presets/opencode/next/rules/ux-design.md +99 -0
  215. package/presets/opencode/next/skills/README.md +21 -0
  216. package/presets/opencode/next/skills/ci-investigation/SKILL.md +44 -0
  217. package/presets/opencode/next/skills/code-review/SKILL.md +44 -0
  218. package/presets/opencode/next/skills/debug-investigation/SKILL.md +59 -0
  219. package/presets/opencode/next/skills/feature-delivery/SKILL.md +39 -0
  220. package/presets/opencode/next/skills/mockup-review/SKILL.md +29 -0
  221. package/presets/opencode/next/skills/playwright-e2e/SKILL.md +33 -0
  222. package/presets/opencode/next/skills/react-performance/SKILL.md +44 -0
  223. package/presets/opencode/next/skills/spec-authoring/SKILL.md +26 -0
  224. package/presets/opencode/next/skills/spec-discovery/SKILL.md +58 -0
  225. package/presets/opencode/next/skills/technical-retro/SKILL.md +54 -0
  226. package/presets/opencode/next/skills/unit-testing/SKILL.md +34 -0
  227. package/presets/opencode/next/skills/ux-design/SKILL.md +36 -0
  228. package/presets/opencode/next/skills/write-adr/SKILL.md +41 -0
  229. package/presets/opencode/next/team/README.md +148 -0
  230. package/presets/opencode/next/team/conventions.md +21 -0
  231. package/presets/opencode/next/team/fixtures/artifact-manifest.example.json +79 -0
  232. package/presets/opencode/next/team/fixtures/bugfix-standard.json +25 -0
  233. package/presets/opencode/next/team/fixtures/feature-full.json +29 -0
  234. package/presets/opencode/next/team/fixtures/feature-light.json +27 -0
  235. package/presets/opencode/next/team/fixtures/feature-with-figma-url.json +17 -0
  236. package/presets/opencode/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
  237. package/presets/opencode/next/team/fixtures/memlog.example.md +9 -0
  238. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
  239. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
  240. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
  241. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
  242. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
  243. package/presets/opencode/next/team/fixtures/metrics.example.json +54 -0
  244. package/presets/opencode/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
  245. package/presets/opencode/next/team/fixtures/mockup-review-standalone.json +14 -0
  246. package/presets/opencode/next/team/fixtures/ux-design-standalone.json +15 -0
  247. package/presets/opencode/next/team/tasks/.gitkeep +0 -0
  248. package/presets/opencode/platform-contract.json +131 -0
  249. package/scripts/capture-cli-golden.mjs +5 -1
  250. package/scripts/check-chain-hook-coverage.sh +1 -0
  251. package/scripts/check-opencode-adapter-map.mjs +294 -0
  252. package/scripts/check-opencode-local-overlay.mjs +56 -0
  253. package/scripts/check-opencode-platform-contract.mjs +388 -0
  254. package/scripts/check-opencode-token-budget.mjs +48 -0
  255. package/scripts/check-preset-leakage.sh +30 -1
  256. package/scripts/check-preset-structure.sh +45 -0
  257. package/scripts/check-task-router-intents.sh +4 -4
  258. package/scripts/fixtures/cli-golden/claude/next.json +8 -8
  259. package/scripts/fixtures/cli-golden/cursor/next.json +8 -8
  260. package/scripts/fixtures/cli-golden/mavis/next.json +10 -10
  261. package/scripts/fixtures/cli-golden/opencode/next.json +125 -0
  262. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
  263. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
  264. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
  265. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/OPENCODE.md +1 -0
  266. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/adapter-map.json +26 -0
  267. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/rules/sample.md +2 -0
  268. package/scripts/fixtures/opencode-adapter-map/valid/cursor/AGENTS.md +1 -0
  269. package/scripts/fixtures/opencode-adapter-map/valid/cursor/rules/sample.mdc +2 -0
  270. package/scripts/fixtures/opencode-adapter-map/valid/opencode/OPENCODE.md +1 -0
  271. package/scripts/fixtures/opencode-adapter-map/valid/opencode/adapter-map.json +26 -0
  272. package/scripts/fixtures/opencode-adapter-map/valid/opencode/rules/sample.md +2 -0
  273. package/scripts/fixtures/opencode-platform-contract/invalid-home-destination.json +131 -0
  274. package/scripts/fixtures/opencode-platform-contract/invalid-missing-capability.json +126 -0
  275. package/scripts/fixtures/opencode-platform-contract/invalid-released-unknown-stack.json +133 -0
  276. package/scripts/fixtures/opencode-platform-contract/invalid-unknown-native-path.json +131 -0
  277. package/scripts/fixtures/opencode-platform-contract/invalid-version-pin.json +132 -0
  278. package/scripts/fixtures/opencode-platform-contract/valid.json +131 -0
  279. package/scripts/generate-mavis-next.mjs +4 -101
  280. package/scripts/generate-opencode-next.mjs +730 -0
  281. package/scripts/lib/discover-hooks.mjs +23 -1
  282. package/scripts/sync-dogfood-mavis.sh +90 -0
  283. package/scripts/sync-dogfood-opencode.sh +85 -0
  284. package/scripts/test-agent-task-metrics-hooks.mjs +4 -1
  285. package/scripts/test-chain-team-phases-coverage.mjs +6 -1
  286. package/scripts/test-cli-init-clean.mjs +180 -2
  287. package/scripts/test-opencode-context-budget.mjs +53 -0
@@ -0,0 +1,730 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Derive presets/opencode/next/** and adapter-map.json from cursor/next.
4
+ * Maintainer tool; not a consumer CLI command.
5
+ */
6
+ import crypto from 'node:crypto';
7
+ import fs from 'node:fs';
8
+ import path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const scriptDirectory = path.dirname(fileURLToPath(import.meta.url));
12
+ const packageRoot = path.join(scriptDirectory, '..');
13
+ const cursorRoot = path.join(packageRoot, 'presets', 'cursor', 'next');
14
+ const opencodeRoot = path.join(packageRoot, 'presets', 'opencode', 'next');
15
+
16
+ const ALWAYS_ON_RULES = new Set([
17
+ 'next-app-core',
18
+ 'package-manager',
19
+ 'code-quality-and-refactoring',
20
+ 'ai-local-overlay',
21
+ ]);
22
+
23
+ const DEVELOPER_AGENTS = new Set([
24
+ 'feature-developer',
25
+ 'debugger',
26
+ 'ci-investigator',
27
+ 'qa-tester',
28
+ 'unit-test-generator',
29
+ 'unit-test-healer',
30
+ 'playwright-test-generator',
31
+ 'playwright-test-healer',
32
+ ]);
33
+
34
+ const REVIEW_AGENTS = new Set(['code-reviewer']);
35
+
36
+ const OMIT = new Map([
37
+ [
38
+ 'BUGBOT.md',
39
+ 'no-equivalent: Bugbot is Cursor-only; review stays a Verifier skill/role',
40
+ ],
41
+ [
42
+ 'hooks.json',
43
+ 'omit until an OpenCode plugin wraps chain-team-phases.sh',
44
+ ],
45
+ [
46
+ 'hooks/guard-shell-command.sh',
47
+ 'omit from active wiring; safety policy stays in entry prose, not enforcement',
48
+ ],
49
+ [
50
+ 'hooks/examples/format-edited.example.sh',
51
+ 'omit until an OpenCode plugin event API is shipped',
52
+ ],
53
+ [
54
+ 'hooks/examples/secret-guard.example.sh',
55
+ 'omit until an OpenCode plugin event API is shipped',
56
+ ],
57
+ [
58
+ 'hooks/examples/test-on-save.example.sh',
59
+ 'omit until an OpenCode plugin event API is shipped',
60
+ ],
61
+ ]);
62
+
63
+ function walkFiles(root) {
64
+ const files = [];
65
+ const visit = (dir) => {
66
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
67
+ const full = path.join(dir, entry.name);
68
+ if (entry.isDirectory()) {
69
+ visit(full);
70
+ } else if (entry.isFile()) {
71
+ files.push(path.relative(root, full).split(path.sep).join('/'));
72
+ }
73
+ }
74
+ };
75
+ visit(root);
76
+ return files.sort();
77
+ }
78
+
79
+ function stripFrontmatter(text) {
80
+ if (!text.startsWith('---\n')) {
81
+ return { frontmatter: '', body: text };
82
+ }
83
+ const end = text.indexOf('\n---\n', 4);
84
+ if (end < 0) {
85
+ return { frontmatter: '', body: text };
86
+ }
87
+ return {
88
+ frontmatter: text.slice(4, end),
89
+ body: text.slice(end + 5).replace(/^\n/, ''),
90
+ };
91
+ }
92
+
93
+ function parseFrontmatterFields(frontmatter) {
94
+ const fields = {};
95
+ for (const line of frontmatter.split('\n')) {
96
+ const match = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
97
+ if (!match) {
98
+ continue;
99
+ }
100
+ const raw = match[2].trim();
101
+ fields[match[1]] = raw.replace(/^["']|["']$/g, '');
102
+ }
103
+ return fields;
104
+ }
105
+
106
+ const sharedCoreRoot = path.join(packageRoot, 'presets', '_shared', 'core');
107
+
108
+ function rewriteConsumerPaths(text) {
109
+ return text
110
+ .replaceAll('.cursor/team', '.opencode/team')
111
+ .replaceAll('.cursor/rules', '.opencode/rules')
112
+ .replaceAll('.cursor/skills', '.opencode/skills')
113
+ .replaceAll('.cursor/agents', '.opencode/agents')
114
+ .replaceAll('.cursor/commands', '.opencode/commands')
115
+ .replaceAll('.cursor/hooks', '.opencode/hooks')
116
+ .replaceAll('.cursor/AGENTS.md', '.opencode/AGENTS.md')
117
+ .replaceAll("'.cursor'", "'.opencode'")
118
+ .replaceAll('".cursor"', '".opencode"');
119
+ }
120
+
121
+ function finalizeOpencodeProse(text) {
122
+ return rewriteConsumerPaths(text)
123
+ .replace(/`([^`\n]+)\.mdc`/g, '`$1.md`')
124
+ .replace(/(?<![\w./-])([\w-]+)\.mdc\b/g, '$1.md')
125
+ .replaceAll('init cursor --preset', 'init opencode --preset')
126
+ .replaceAll('# MCP (preset `next`, Cursor)', '# MCP (preset `next`, OpenCode)')
127
+ .replaceAll('(preset `next`, Cursor)', '(preset `next`, OpenCode)')
128
+ .replaceAll('Copied to `.cursor/mcp.json`', 'Merged into project `opencode.json` `mcp`')
129
+ .replaceAll('OAuth in Cursor', 'OAuth in OpenCode (operator MCP config)');
130
+ }
131
+
132
+ function rewriteProse(text) {
133
+ return finalizeOpencodeProse(text);
134
+ }
135
+
136
+ function sha256(content) {
137
+ return crypto.createHash('sha256').update(content).digest('hex');
138
+ }
139
+
140
+ function writeFile(relPath, content) {
141
+ const dest = path.join(opencodeRoot, relPath);
142
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
143
+ fs.writeFileSync(dest, content);
144
+ }
145
+
146
+ function sharedCoreStem(text) {
147
+ const match = text.match(/<!--\s*shared-core:\s*([^\s>]+)\s*-->/);
148
+ return match ? match[1] : undefined;
149
+ }
150
+
151
+ function entry({
152
+ source,
153
+ target,
154
+ category,
155
+ disposition,
156
+ loadMode,
157
+ reason,
158
+ sourceText,
159
+ }) {
160
+ const item = {
161
+ source,
162
+ target,
163
+ category,
164
+ disposition,
165
+ loadMode,
166
+ reason,
167
+ };
168
+ const stem = sourceText ? sharedCoreStem(sourceText) : undefined;
169
+ if (stem) {
170
+ item.sharedCoreStem = stem;
171
+ }
172
+ return item;
173
+ }
174
+
175
+ function isEmbedEntryBody(body) {
176
+ const first = body
177
+ .split('\n')
178
+ .map((line) => line.trim())
179
+ .find((line) => line.length > 0);
180
+ return Boolean(first && /^<!--\s*shared-core:\s*[^\s>]+\s*-->\s*$/.test(first));
181
+ }
182
+
183
+ function transformMarkdown(sourceText, { allowEmbedEntry = true } = {}) {
184
+ const { body } = stripFrontmatter(sourceText);
185
+ const stem =
186
+ allowEmbedEntry && isEmbedEntryBody(body) ? sharedCoreStem(body) : undefined;
187
+ let next;
188
+ if (stem) {
189
+ const corePath = path.join(sharedCoreRoot, stem);
190
+ if (fs.existsSync(corePath)) {
191
+ const coreBody = fs.readFileSync(corePath, 'utf8').trimEnd();
192
+ const marker = `<!-- shared-core: ${stem} -->`;
193
+ next = `${marker}\n\n${coreBody}\n`;
194
+ next = rewriteConsumerPaths(next);
195
+ } else {
196
+ next = rewriteProse(body);
197
+ }
198
+ } else {
199
+ next = rewriteProse(body);
200
+ }
201
+ return next.endsWith('\n') ? next : `${next}\n`;
202
+ }
203
+
204
+ function yamlString(value) {
205
+ return JSON.stringify(value ?? '');
206
+ }
207
+
208
+ function transformAgent(source, sourceText) {
209
+ const { frontmatter, body } = stripFrontmatter(sourceText);
210
+ const fields = parseFrontmatterFields(frontmatter);
211
+ const stem = path.basename(source, '.md');
212
+ const canEditProduction = DEVELOPER_AGENTS.has(stem);
213
+ const permission = canEditProduction
214
+ ? 'permission:\n edit: allow'
215
+ : 'permission:\n edit:\n "*": deny\n ".opencode/team/**": allow';
216
+ const description = fields.description || stem;
217
+ const header = `---\nname: ${stem}\ndescription: ${yamlString(description)}\nmode: subagent\n${permission}\n---\n\n`;
218
+ const transformed = rewriteProse(body);
219
+ let withNote = transformed;
220
+ if (!canEditProduction) {
221
+ const leadIn = REVIEW_AGENTS.has(stem)
222
+ ? 'Write only review artifacts under `.opencode/team/**`. Do not edit production code.'
223
+ : 'Write only team artifacts under `.opencode/team/**`. Do not edit production code.';
224
+ withNote = `${leadIn}\n\n${transformed}`;
225
+ }
226
+ return `${header}${withNote.endsWith('\n') ? withNote : `${withNote}\n`}`;
227
+ }
228
+
229
+ function transformOrchestrator(markdown) {
230
+ let next = markdown.replace(
231
+ /\*\*Cursor frontmatter:\*\* all team pipeline agents use `readonly: false` so they can write team artifacts\. Production\/app restrictions live in the agent prompt body — do \*\*not\*\* set `readonly: true`\./,
232
+ '**OpenCode frontmatter:** all team pipeline agents use `mode: subagent` and `permission.edit` so they can write team artifacts (`edit: allow` for implementers; deny except `.opencode/team/**` for reviewers/planners). Production/app restrictions live in the agent prompt body. See `agents/README.md`.',
233
+ );
234
+ next = next.replace(
235
+ '## Team artifact I/O (Cursor Shell sandbox)',
236
+ '## Team artifact I/O',
237
+ );
238
+ if (!next.includes('## Stack notes')) {
239
+ next = next.replace(
240
+ '## Dynamic pipeline',
241
+ `## Stack notes
242
+
243
+ Roles, write scopes, and OpenCode \`mode\` / \`permission\` packaging — **\`agents/README.md\`**.
244
+
245
+ ## Dynamic pipeline`,
246
+ );
247
+ }
248
+ return next;
249
+ }
250
+
251
+ function transformSkill(sourceText) {
252
+ const { frontmatter, body } = stripFrontmatter(sourceText);
253
+ const fields = parseFrontmatterFields(frontmatter);
254
+ const name = fields.name || '';
255
+ const description = fields.description || '';
256
+ const header = `---\nname: ${name}\ndescription: ${description}\n---\n\n`;
257
+ const transformed = rewriteProse(body);
258
+ return `${header}${transformed.endsWith('\n') ? transformed : `${transformed}\n`}`;
259
+ }
260
+
261
+ function transformCommand(sourceText) {
262
+ const { frontmatter, body } = stripFrontmatter(sourceText);
263
+ const fields = parseFrontmatterFields(frontmatter);
264
+ const description = fields.description || '';
265
+ let next = rewriteProse(body);
266
+ if (!next.includes('$ARGUMENTS')) {
267
+ next = `## Invocation arguments\n\n$ARGUMENTS\n\n${next}`;
268
+ }
269
+ const header = `---\ndescription: ${description}\n---\n\n`;
270
+ return `${header}${next.endsWith('\n') ? next : `${next}\n`}`;
271
+ }
272
+
273
+ function cursorMcpToOpencode(sourceText) {
274
+ const parsed = JSON.parse(sourceText);
275
+ const mcp = {};
276
+ for (const [id, server] of Object.entries(parsed.mcpServers || {})) {
277
+ if (!server || typeof server !== 'object') {
278
+ continue;
279
+ }
280
+ if (typeof server.url === 'string' && server.url) {
281
+ mcp[id] = { type: 'remote', url: server.url };
282
+ continue;
283
+ }
284
+ if (typeof server.command === 'string' && server.command) {
285
+ const command = [server.command, ...(Array.isArray(server.args) ? server.args : [])];
286
+ const entry = { type: 'local', command };
287
+ if (server.env && typeof server.env === 'object') {
288
+ entry.environment = server.env;
289
+ }
290
+ mcp[id] = entry;
291
+ }
292
+ }
293
+ return `${JSON.stringify({ $schema: 'https://opencode.ai/config.json', mcp }, null, 2)}\n`;
294
+ }
295
+
296
+ function main() {
297
+ fs.rmSync(opencodeRoot, { recursive: true, force: true });
298
+ fs.mkdirSync(opencodeRoot, { recursive: true });
299
+
300
+ const cursorFiles = walkFiles(cursorRoot);
301
+ const mappedSources = cursorFiles.filter((rel) => path.basename(rel) !== 'README.md');
302
+ const entries = [];
303
+ const digestParts = [];
304
+
305
+ for (const source of mappedSources) {
306
+ const abs = path.join(cursorRoot, source);
307
+ const sourceText = fs.readFileSync(abs, 'utf8');
308
+ digestParts.push(`${source}\t${sha256(sourceText)}`);
309
+
310
+ if (OMIT.has(source)) {
311
+ entries.push(
312
+ entry({
313
+ source,
314
+ target: null,
315
+ category: source.startsWith('hooks') ? 'hooks' : 'platform',
316
+ disposition: 'omit',
317
+ loadMode: 'not-installed',
318
+ reason: OMIT.get(source),
319
+ sourceText,
320
+ }),
321
+ );
322
+ continue;
323
+ }
324
+
325
+ if (source === 'AGENTS.md') {
326
+ const target = 'OPENCODE.md';
327
+ writeFile(target, buildOpencodeEntry(sourceText));
328
+ entries.push(
329
+ entry({
330
+ source,
331
+ target,
332
+ category: 'entry',
333
+ disposition: 'embed-entry',
334
+ loadMode: 'root-entry',
335
+ reason: 'Cursor AGENTS.md semantics feed .opencode/AGENTS.md',
336
+ sourceText,
337
+ }),
338
+ );
339
+ continue;
340
+ }
341
+
342
+ if (source === 'REPO_AGENTS.md') {
343
+ const target = 'REPO_AGENTS.md';
344
+ writeFile(target, sourceText);
345
+ entries.push(
346
+ entry({
347
+ source,
348
+ target,
349
+ category: 'entry',
350
+ disposition: 'copy',
351
+ loadMode: 'root-entry',
352
+ reason: 'Canonical cross-tool root AGENTS.md; identical for all platforms',
353
+ sourceText,
354
+ }),
355
+ );
356
+ continue;
357
+ }
358
+
359
+ if (source.startsWith('rules/') && source.endsWith('.mdc')) {
360
+ const stem = path.basename(source, '.mdc');
361
+ const target = `rules/${stem}.md`;
362
+ const alwaysOn = ALWAYS_ON_RULES.has(stem);
363
+ let markdown = transformMarkdown(sourceText);
364
+ if (stem === 'agent-team-orchestrator') {
365
+ markdown = transformOrchestrator(markdown);
366
+ }
367
+ writeFile(target, markdown);
368
+ entries.push(
369
+ entry({
370
+ source,
371
+ target,
372
+ category: 'rules',
373
+ disposition: 'transform',
374
+ loadMode: alwaysOn ? 'root-entry' : 'referenced',
375
+ reason: alwaysOn
376
+ ? 'Essential Cursor always-on rule: listed in AGENTS.md; body under .opencode/rules'
377
+ : 'Requestable Cursor rule: lazy Read from .opencode/rules; not auto-loaded',
378
+ sourceText,
379
+ }),
380
+ );
381
+ continue;
382
+ }
383
+
384
+ if (source.startsWith('agents/') && source.endsWith('.md')) {
385
+ const target = source;
386
+ writeFile(target, transformAgent(source, sourceText));
387
+ entries.push(
388
+ entry({
389
+ source,
390
+ target,
391
+ category: 'agents',
392
+ disposition: 'transform',
393
+ loadMode: 'native-team',
394
+ reason: 'Native .opencode/agents discovery; OpenCode frontmatter (mode/permission)',
395
+ sourceText,
396
+ }),
397
+ );
398
+ continue;
399
+ }
400
+
401
+ if (source.startsWith('skills/') && source.endsWith('/SKILL.md')) {
402
+ const target = source;
403
+ writeFile(target, transformSkill(sourceText));
404
+ entries.push(
405
+ entry({
406
+ source,
407
+ target,
408
+ category: 'skills',
409
+ disposition: 'transform',
410
+ loadMode: 'native-skill',
411
+ reason: 'Native .opencode/skills/<name>/SKILL.md',
412
+ sourceText,
413
+ }),
414
+ );
415
+ continue;
416
+ }
417
+
418
+ if (source.startsWith('commands/') && source.endsWith('.md')) {
419
+ const target = source;
420
+ writeFile(target, transformCommand(sourceText));
421
+ entries.push(
422
+ entry({
423
+ source,
424
+ target,
425
+ category: 'commands',
426
+ disposition: 'transform',
427
+ loadMode: 'native-skill',
428
+ reason: 'Native slash commands under .opencode/commands; $ARGUMENTS kept',
429
+ sourceText,
430
+ }),
431
+ );
432
+ continue;
433
+ }
434
+
435
+ if (source === 'hooks/chain-team-phases.sh') {
436
+ const target = source;
437
+ writeFile(target, rewriteProse(sourceText));
438
+ entries.push(
439
+ entry({
440
+ source,
441
+ target,
442
+ category: 'hooks',
443
+ disposition: 'transform',
444
+ loadMode: 'manual',
445
+ reason: 'Manual/semi-auto chain runner under .opencode/hooks; not a plugin',
446
+ sourceText,
447
+ }),
448
+ );
449
+ continue;
450
+ }
451
+
452
+ if (source === 'mcp.json') {
453
+ const target = 'opencode.json';
454
+ writeFile(target, cursorMcpToOpencode(sourceText));
455
+ entries.push(
456
+ entry({
457
+ source,
458
+ target,
459
+ category: 'mcp',
460
+ disposition: 'transform',
461
+ loadMode: 'manual',
462
+ reason: 'Cursor mcp.json → opencode.json mcp (local/remote types)',
463
+ sourceText,
464
+ }),
465
+ );
466
+ continue;
467
+ }
468
+
469
+ if (source === 'MCP.md') {
470
+ const target = source;
471
+ writeFile(target, rewriteProse(sourceText));
472
+ entries.push(
473
+ entry({
474
+ source,
475
+ target,
476
+ category: 'mcp',
477
+ disposition: 'transform',
478
+ loadMode: 'manual',
479
+ reason: 'MCP operator notes copied to .opencode/MCP.md',
480
+ sourceText,
481
+ }),
482
+ );
483
+ continue;
484
+ }
485
+
486
+ if (source.startsWith('team/')) {
487
+ const target = source;
488
+ const transformed = source.endsWith('.md') ? rewriteProse(sourceText) : sourceText;
489
+ writeFile(
490
+ target,
491
+ transformed.endsWith('\n') || !source.endsWith('.md') ? transformed : `${transformed}\n`,
492
+ );
493
+ entries.push(
494
+ entry({
495
+ source,
496
+ target,
497
+ category: 'team',
498
+ disposition: source.endsWith('.md') ? 'transform' : 'copy',
499
+ loadMode: 'manual',
500
+ reason: 'File pipeline remains audit SoT under .opencode/team',
501
+ sourceText,
502
+ }),
503
+ );
504
+ continue;
505
+ }
506
+
507
+ throw new Error(`unmapped cursor source: ${source}`);
508
+ }
509
+
510
+ writeFile('README.md', buildStackReadme());
511
+ writeFile('rules/README.md', buildRulesReadme());
512
+ writeFile('agents/README.md', buildAgentsReadme());
513
+ writeFile('skills/README.md', rewriteProse(readSiblingReadme('skills/README.md')));
514
+ writeFile('commands/README.md', buildCommandsReadme());
515
+ writeFile('hooks/README.md', buildHooksReadme());
516
+ writeFile('team/README.md', rewriteProse(readSiblingReadme('team/README.md')));
517
+
518
+ digestParts.sort();
519
+ const adapterMap = {
520
+ schemaVersion: 1,
521
+ platform: 'opencode',
522
+ stack: 'next',
523
+ semanticSource: 'cursor/next',
524
+ sourceRevision: sha256(digestParts.join('\n')),
525
+ entries,
526
+ };
527
+ writeFile('adapter-map.json', `${JSON.stringify(adapterMap, null, 2)}\n`);
528
+ console.log(
529
+ `generated presets/opencode/next (${entries.length} mapped sources, revision ${adapterMap.sourceRevision.slice(0, 12)})`,
530
+ );
531
+ }
532
+
533
+ function readSiblingReadme(rel) {
534
+ const filePath = path.join(cursorRoot, rel);
535
+ return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : `# ${rel}\n`;
536
+ }
537
+
538
+ function dropLeadingHeading(text) {
539
+ return text
540
+ .replace(/^# [^\n]+\n+/, '')
541
+ .replace(/^Short Cursor entry point\.[^\n]*\n+/, '');
542
+ }
543
+
544
+ function stripInstallSection(body) {
545
+ const index = body.search(/^## Install\r?\n/m);
546
+ if (index < 0) {
547
+ return body.trimEnd();
548
+ }
549
+ return body.slice(0, index).trimEnd();
550
+ }
551
+
552
+ function stripEmbeddedCatalogLine(body) {
553
+ return body.replace(
554
+ /^See `\.opencode\/rules\/README\.md` for the full catalog[^\n]*\n+/m,
555
+ '',
556
+ );
557
+ }
558
+
559
+ function buildOpencodeEntry(sourceText) {
560
+ const body = stripEmbeddedCatalogLine(
561
+ stripInstallSection(
562
+ dropLeadingHeading(rewriteProse(stripFrontmatter(sourceText).body)),
563
+ ),
564
+ );
565
+ return `# Next.js stack — OpenCode agent team
566
+
567
+ Short OpenCode entry. Native discovery: \`.opencode/agents/\`,
568
+ \`.opencode/skills/\`, \`.opencode/commands/\`. Rule files under
569
+ \`.opencode/rules/*.md\` are **not** auto-loaded.
570
+
571
+ Root \`AGENTS.md\` is native always-on. Read it first, then this file.
572
+
573
+ ## How to load
574
+
575
+ - Essential rules: Read immediately (paths below). Do not preload the catalog.
576
+ - Other rules: lazy \`Read\` \`.opencode/rules/<stem>.md\` when the task matches.
577
+ - Agents: native \`@name\` / Task from \`.opencode/agents/\`.
578
+ - Commands: native \`/task\` from \`.opencode/commands/\`.
579
+ - After a producer receipt, run \`bash .opencode/hooks/chain-team-phases.sh\`.
580
+ Agents must not write lifecycle fields in \`status.json\` / \`metrics.json\`.
581
+
582
+ ## Essential rules (read every turn)
583
+
584
+ - \`.opencode/rules/next-app-core.md\`
585
+ - \`.opencode/rules/package-manager.md\`
586
+ - \`.opencode/rules/code-quality-and-refactoring.md\`
587
+ - \`.opencode/rules/ai-local-overlay.md\`
588
+
589
+ See \`.opencode/rules/README.md\` for the full catalog and loading strategy.
590
+
591
+ **Platform docs:** published with the \`@bonesofspring/ai-rules\` package
592
+ (\`docs/OPENCODE.md\`, \`docs/OPENCODE-COMPATIBILITY.md\`) — **not** copied into
593
+ the consumer repo. Source: https://github.com/bonesofspring/ai-rules
594
+
595
+ ${body}
596
+
597
+ ## Install
598
+
599
+ \`\`\`bash
600
+ npx @bonesofspring/ai-rules init opencode --preset next
601
+ \`\`\`
602
+
603
+ Copies rules, commands, agents, hooks, skills, team, \`OPENCODE.md\` →
604
+ \`.opencode/AGENTS.md\`, \`MCP.md\`, \`opencode.json\` → project \`opencode.json\`
605
+ (\`mcp\` merge), plus \`REPO_AGENTS.md\` → root \`AGENTS.md\`.
606
+ `;
607
+ }
608
+
609
+ function buildCommandsReadme() {
610
+ let text = rewriteProse(readSiblingReadme('commands/README.md'));
611
+ text = text.replace(
612
+ '# Cursor commands (next preset)',
613
+ '# OpenCode commands (next preset)',
614
+ );
615
+ text = text.replace(
616
+ 'Slash commands are copied to `.opencode/commands/` on `ai-rules init opencode --preset next`.',
617
+ 'Slash commands are copied to `.opencode/commands/` on `ai-rules init opencode --preset next`. OpenCode loads `/<filename>` natively.',
618
+ );
619
+ text = text.replace(
620
+ '- Orchestrator: `../rules/agent-team-orchestrator.mdc`',
621
+ '- Orchestrator: `../rules/agent-team-orchestrator.md`',
622
+ );
623
+ text = text.replace(
624
+ '- Hooks: `../hooks.json`, `../hooks/chain-team-phases.sh`',
625
+ '- Hooks: `../hooks/chain-team-phases.sh` only (semi-auto runner; no `hooks.json`)',
626
+ );
627
+ return text.endsWith('\n') ? text : `${text}\n`;
628
+ }
629
+
630
+ function buildAgentsReadme() {
631
+ let text = rewriteProse(readSiblingReadme('agents/README.md'));
632
+ text = text.replace(
633
+ 'Role-based subagents for the Cursor team pipeline.',
634
+ 'Role-based subagents for the OpenCode team pipeline (native `.opencode/agents/`).',
635
+ );
636
+ text = text.replace(
637
+ /## Frontmatter `readonly`[\s\S]*?(?=## Orchestration)/,
638
+ `## OpenCode packaging
639
+
640
+ Agents ship with OpenCode YAML frontmatter: \`description\`, \`mode: subagent\`,
641
+ and \`permission\`. Do **not** pin \`model\` in this preset.
642
+
643
+ - Implementers (\`feature-developer\`, \`debugger\`, \`ci-investigator\`, test generators/healers): \`permission.edit: allow\`.
644
+ - Reviewer / planner roles: \`edit\` deny except \`.opencode/team/**\`. Prompt bodies still forbid production edits.
645
+
646
+ **Team artifacts:** agents write \`.opencode/team/**\` deliverables. They must
647
+ **not** mutate lifecycle fields in \`status.json\` or \`metrics.json\` —
648
+ \`bash .opencode/hooks/chain-team-phases.sh\` owns those after each producer receipt.
649
+
650
+ ## Model tiers (advisory)
651
+
652
+ Optional \`steps[].model\` in \`pipeline.json\` (\`cheap\` | \`standard\` | \`strong\`)
653
+ is advisory for the operator's OpenCode provider — see \`task-router.md\`.
654
+
655
+ `,
656
+ );
657
+ return text.endsWith('\n') ? text : `${text}\n`;
658
+ }
659
+
660
+ function buildStackReadme() {
661
+ return `# Next.js preset (OpenCode)
662
+
663
+ Sibling adapter derived from \`presets/cursor/next\`. Platform id \`opencode\`;
664
+ runtime \`opencode\`. Consumer install root is \`.opencode/\` (native agents,
665
+ skills, commands) plus root \`AGENTS.md\` and \`opencode.json\`.
666
+
667
+ Mapping: \`adapter-map.json\`. Do not derive this tree from Claude.
668
+
669
+ \`\`\`bash
670
+ npx @bonesofspring/ai-rules init opencode --preset next
671
+ \`\`\`
672
+
673
+ \`releasedStacks\` currently lists **\`next\`** only.
674
+
675
+ Omitted from this pilot: \`BUGBOT.md\`, \`hooks.json\`, hook examples, and
676
+ unwired \`guard-shell-command.sh\`.
677
+ `;
678
+ }
679
+
680
+ function buildRulesReadme() {
681
+ return `# OpenCode Next rules
682
+
683
+ Plain Markdown. OpenCode does **not** auto-load these files. Root \`AGENTS.md\`
684
+ and \`.opencode/AGENTS.md\` list four essential paths to Read immediately; the
685
+ rest are lazy-loaded when the task matches.
686
+
687
+ Do not put this catalog into \`opencode.json\` \`instructions\` as the only
688
+ always-on path (V2 may ignore that field).
689
+
690
+ ## Essential (entry-critical)
691
+
692
+ \`next-app-core\`, \`package-manager\`, \`code-quality-and-refactoring\`,
693
+ \`ai-local-overlay\`.
694
+
695
+ ## Requestable
696
+
697
+ All other \`*.md\` files in this directory. Same stems as Cursor
698
+ \`rules/README.md\`.
699
+
700
+ Shared-core markers (\`<!-- shared-core: … -->\`) are preserved from Cursor.
701
+ `;
702
+ }
703
+
704
+ function buildHooksReadme() {
705
+ return `# OpenCode hooks
706
+
707
+ No Cursor \`hooks.json\` and no TypeScript plugin in v1.
708
+
709
+ ## Shipped
710
+
711
+ \`chain-team-phases.sh\` — manual/semi-auto runner. After a producer receipt:
712
+
713
+ \`\`\`bash
714
+ printf '%s\\n' '{"status":"completed","attemptId":"<runtime-attempt-id>"}' \\
715
+ | bash .opencode/hooks/chain-team-phases.sh
716
+ \`\`\`
717
+
718
+ \`<runtime-attempt-id>\` comes from \`.opencode/team/tasks/<slug>/status.json\`.
719
+ Do not invent it. Agents never write lifecycle fields in \`status.json\` or
720
+ \`metrics.json\`.
721
+
722
+ ## Omitted
723
+
724
+ - \`hooks.json\` — no native registration schema in this preset
725
+ - \`guard-shell-command.sh\` — not wired; do not describe it as enforcement
726
+ - \`hooks/examples/**\` — plugin port is a follow-up
727
+ `;
728
+ }
729
+
730
+ main();