@bonesofspring/ai-rules 0.2.23 → 0.2.24

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 (285) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +5 -1
  3. package/bin/cli.js +32 -9
  4. package/bin/opencode-install.js +531 -0
  5. package/package.json +10 -3
  6. package/presets/_shared/core/agent-team/agent-team-orchestrator.md +2 -2
  7. package/presets/_shared/core/meta/preset-layering.md +3 -3
  8. package/presets/_shared/core/meta/preset-no-cross-stack-leakage.md +1 -1
  9. package/presets/_shared/core/meta/preset-pr-checklist.md +4 -3
  10. package/presets/_shared/core/meta/preset-token-budget.md +3 -0
  11. package/presets/_shared/core/meta/preset-twin-sync.md +16 -0
  12. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-layering.md +3 -3
  13. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  14. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  15. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-token-budget.md +3 -0
  16. package/presets/claude/android-kotlin/rules/tooling-and-review/preset-twin-sync.md +16 -0
  17. package/presets/claude/go/rules/tooling-and-review/preset-layering.md +3 -3
  18. package/presets/claude/go/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  19. package/presets/claude/go/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  20. package/presets/claude/go/rules/tooling-and-review/preset-token-budget.md +3 -0
  21. package/presets/claude/go/rules/tooling-and-review/preset-twin-sync.md +16 -0
  22. package/presets/claude/ios-swift/rules/tooling-and-review/preset-layering.md +3 -3
  23. package/presets/claude/ios-swift/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  24. package/presets/claude/ios-swift/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  25. package/presets/claude/ios-swift/rules/tooling-and-review/preset-token-budget.md +3 -0
  26. package/presets/claude/ios-swift/rules/tooling-and-review/preset-twin-sync.md +16 -0
  27. package/presets/claude/java/rules/tooling-and-review/preset-layering.md +3 -3
  28. package/presets/claude/java/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  29. package/presets/claude/java/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  30. package/presets/claude/java/rules/tooling-and-review/preset-token-budget.md +3 -0
  31. package/presets/claude/java/rules/tooling-and-review/preset-twin-sync.md +16 -0
  32. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-layering.md +3 -3
  33. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  34. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  35. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-token-budget.md +3 -0
  36. package/presets/claude/mcp-ts/rules/tooling-and-review/preset-twin-sync.md +16 -0
  37. package/presets/claude/next/REPO_AGENTS.md +10 -5
  38. package/presets/claude/next/agents/build-verifier.md +3 -1
  39. package/presets/claude/next/rules/tooling-and-review/ai-local-overlay.md +1 -1
  40. package/presets/claude/next/rules/tooling-and-review/preset-layering.md +3 -3
  41. package/presets/claude/next/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  42. package/presets/claude/next/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  43. package/presets/claude/next/rules/tooling-and-review/preset-token-budget.md +3 -0
  44. package/presets/claude/next/rules/tooling-and-review/preset-twin-sync.md +16 -0
  45. package/presets/claude/nuxt/rules/tooling-and-review/preset-layering.md +3 -3
  46. package/presets/claude/nuxt/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  47. package/presets/claude/nuxt/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  48. package/presets/claude/nuxt/rules/tooling-and-review/preset-token-budget.md +3 -0
  49. package/presets/claude/nuxt/rules/tooling-and-review/preset-twin-sync.md +16 -0
  50. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-layering.md +3 -3
  51. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  52. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  53. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-token-budget.md +3 -0
  54. package/presets/claude/php-hexagonal/rules/tooling-and-review/preset-twin-sync.md +16 -0
  55. package/presets/claude/php-laravel/rules/tooling-and-review/preset-layering.md +3 -3
  56. package/presets/claude/php-laravel/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  57. package/presets/claude/php-laravel/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  58. package/presets/claude/php-laravel/rules/tooling-and-review/preset-token-budget.md +3 -0
  59. package/presets/claude/php-laravel/rules/tooling-and-review/preset-twin-sync.md +16 -0
  60. package/presets/claude/svelte/rules/tooling-and-review/preset-layering.md +3 -3
  61. package/presets/claude/svelte/rules/tooling-and-review/preset-no-cross-stack-leakage.md +1 -1
  62. package/presets/claude/svelte/rules/tooling-and-review/preset-pr-checklist.md +4 -3
  63. package/presets/claude/svelte/rules/tooling-and-review/preset-token-budget.md +3 -0
  64. package/presets/claude/svelte/rules/tooling-and-review/preset-twin-sync.md +16 -0
  65. package/presets/cursor/android-kotlin/rules/preset-layering.mdc +3 -3
  66. package/presets/cursor/android-kotlin/rules/preset-no-cross-stack-leakage.mdc +1 -1
  67. package/presets/cursor/android-kotlin/rules/preset-pr-checklist.mdc +4 -3
  68. package/presets/cursor/android-kotlin/rules/preset-token-budget.mdc +3 -0
  69. package/presets/cursor/android-kotlin/rules/preset-twin-sync.mdc +16 -0
  70. package/presets/cursor/go/rules/preset-layering.mdc +3 -3
  71. package/presets/cursor/go/rules/preset-no-cross-stack-leakage.mdc +1 -1
  72. package/presets/cursor/go/rules/preset-pr-checklist.mdc +4 -3
  73. package/presets/cursor/go/rules/preset-token-budget.mdc +3 -0
  74. package/presets/cursor/go/rules/preset-twin-sync.mdc +16 -0
  75. package/presets/cursor/ios-swift/rules/preset-layering.mdc +3 -3
  76. package/presets/cursor/ios-swift/rules/preset-no-cross-stack-leakage.mdc +1 -1
  77. package/presets/cursor/ios-swift/rules/preset-pr-checklist.mdc +4 -3
  78. package/presets/cursor/ios-swift/rules/preset-token-budget.mdc +3 -0
  79. package/presets/cursor/ios-swift/rules/preset-twin-sync.mdc +16 -0
  80. package/presets/cursor/java/rules/preset-layering.mdc +3 -3
  81. package/presets/cursor/java/rules/preset-no-cross-stack-leakage.mdc +1 -1
  82. package/presets/cursor/java/rules/preset-pr-checklist.mdc +4 -3
  83. package/presets/cursor/java/rules/preset-token-budget.mdc +3 -0
  84. package/presets/cursor/java/rules/preset-twin-sync.mdc +16 -0
  85. package/presets/cursor/mcp-ts/rules/preset-layering.mdc +3 -3
  86. package/presets/cursor/mcp-ts/rules/preset-no-cross-stack-leakage.mdc +1 -1
  87. package/presets/cursor/mcp-ts/rules/preset-pr-checklist.mdc +4 -3
  88. package/presets/cursor/mcp-ts/rules/preset-token-budget.mdc +3 -0
  89. package/presets/cursor/mcp-ts/rules/preset-twin-sync.mdc +16 -0
  90. package/presets/cursor/next/REPO_AGENTS.md +10 -5
  91. package/presets/cursor/next/agents/build-verifier.md +3 -1
  92. package/presets/cursor/next/rules/ai-local-overlay.mdc +1 -1
  93. package/presets/cursor/next/rules/preset-layering.mdc +3 -3
  94. package/presets/cursor/next/rules/preset-no-cross-stack-leakage.mdc +1 -1
  95. package/presets/cursor/next/rules/preset-pr-checklist.mdc +4 -3
  96. package/presets/cursor/next/rules/preset-token-budget.mdc +3 -0
  97. package/presets/cursor/next/rules/preset-twin-sync.mdc +16 -0
  98. package/presets/cursor/nuxt/rules/preset-layering.mdc +3 -3
  99. package/presets/cursor/nuxt/rules/preset-no-cross-stack-leakage.mdc +1 -1
  100. package/presets/cursor/nuxt/rules/preset-pr-checklist.mdc +4 -3
  101. package/presets/cursor/nuxt/rules/preset-token-budget.mdc +3 -0
  102. package/presets/cursor/nuxt/rules/preset-twin-sync.mdc +16 -0
  103. package/presets/cursor/php-hexagonal/rules/preset-layering.mdc +3 -3
  104. package/presets/cursor/php-hexagonal/rules/preset-no-cross-stack-leakage.mdc +1 -1
  105. package/presets/cursor/php-hexagonal/rules/preset-pr-checklist.mdc +4 -3
  106. package/presets/cursor/php-hexagonal/rules/preset-token-budget.mdc +3 -0
  107. package/presets/cursor/php-hexagonal/rules/preset-twin-sync.mdc +16 -0
  108. package/presets/cursor/php-laravel/rules/preset-layering.mdc +3 -3
  109. package/presets/cursor/php-laravel/rules/preset-no-cross-stack-leakage.mdc +1 -1
  110. package/presets/cursor/php-laravel/rules/preset-pr-checklist.mdc +4 -3
  111. package/presets/cursor/php-laravel/rules/preset-token-budget.mdc +3 -0
  112. package/presets/cursor/php-laravel/rules/preset-twin-sync.mdc +16 -0
  113. package/presets/cursor/svelte/rules/preset-layering.mdc +3 -3
  114. package/presets/cursor/svelte/rules/preset-no-cross-stack-leakage.mdc +1 -1
  115. package/presets/cursor/svelte/rules/preset-pr-checklist.mdc +4 -3
  116. package/presets/cursor/svelte/rules/preset-token-budget.mdc +3 -0
  117. package/presets/cursor/svelte/rules/preset-twin-sync.mdc +16 -0
  118. package/presets/mavis/next/REPO_AGENTS.md +9 -7
  119. package/presets/mavis/next/adapter-map.json +1 -1
  120. package/presets/mavis/next/agents/build-verifier.md +3 -1
  121. package/presets/mavis/next/rules/agent-team-orchestrator.md +2 -2
  122. package/presets/mavis/next/rules/ai-local-overlay.md +1 -1
  123. package/presets/mavis/next/rules/preset-layering.md +3 -3
  124. package/presets/mavis/next/rules/preset-no-cross-stack-leakage.md +1 -1
  125. package/presets/mavis/next/rules/preset-pr-checklist.md +4 -3
  126. package/presets/mavis/next/rules/preset-token-budget.md +3 -0
  127. package/presets/mavis/next/rules/preset-twin-sync.md +16 -0
  128. package/presets/opencode/README.md +61 -0
  129. package/presets/opencode/next/MCP.md +36 -0
  130. package/presets/opencode/next/OPENCODE.md +65 -0
  131. package/presets/opencode/next/README.md +16 -0
  132. package/presets/opencode/next/REPO_AGENTS.md +97 -0
  133. package/presets/opencode/next/adapter-map.json +959 -0
  134. package/presets/opencode/next/agents/README.md +201 -0
  135. package/presets/opencode/next/agents/accessibility-reviewer.md +73 -0
  136. package/presets/opencode/next/agents/api-contract-reviewer.md +69 -0
  137. package/presets/opencode/next/agents/build-verifier.md +124 -0
  138. package/presets/opencode/next/agents/ci-investigator.md +71 -0
  139. package/presets/opencode/next/agents/code-reviewer.md +73 -0
  140. package/presets/opencode/next/agents/codebase-analyzer.md +33 -0
  141. package/presets/opencode/next/agents/debugger.md +88 -0
  142. package/presets/opencode/next/agents/feature-developer.md +109 -0
  143. package/presets/opencode/next/agents/migration-specialist.md +79 -0
  144. package/presets/opencode/next/agents/mockup-reviewer.md +65 -0
  145. package/presets/opencode/next/agents/openspec-author.md +169 -0
  146. package/presets/opencode/next/agents/performance-auditor.md +80 -0
  147. package/presets/opencode/next/agents/playwright-test-generator.md +36 -0
  148. package/presets/opencode/next/agents/playwright-test-healer.md +36 -0
  149. package/presets/opencode/next/agents/playwright-test-planner.md +33 -0
  150. package/presets/opencode/next/agents/qa-tester.md +60 -0
  151. package/presets/opencode/next/agents/security-reviewer.md +66 -0
  152. package/presets/opencode/next/agents/solution-architect.md +80 -0
  153. package/presets/opencode/next/agents/task-analyst.md +178 -0
  154. package/presets/opencode/next/agents/task-router.md +190 -0
  155. package/presets/opencode/next/agents/tech-writer.md +69 -0
  156. package/presets/opencode/next/agents/unit-test-generator.md +39 -0
  157. package/presets/opencode/next/agents/unit-test-healer.md +40 -0
  158. package/presets/opencode/next/agents/unit-test-planner.md +66 -0
  159. package/presets/opencode/next/commands/README.md +49 -0
  160. package/presets/opencode/next/commands/spec-start.md +32 -0
  161. package/presets/opencode/next/commands/task-continue.md +58 -0
  162. package/presets/opencode/next/commands/task.md +58 -0
  163. package/presets/opencode/next/commands/technical-retro.md +88 -0
  164. package/presets/opencode/next/hooks/README.md +22 -0
  165. package/presets/opencode/next/hooks/chain-team-phases.sh +845 -0
  166. package/presets/opencode/next/opencode.json +33 -0
  167. package/presets/opencode/next/rules/README.md +20 -0
  168. package/presets/opencode/next/rules/agent-team-intake.md +11 -0
  169. package/presets/opencode/next/rules/agent-team-orchestrator.md +214 -0
  170. package/presets/opencode/next/rules/ai-local-overlay.md +37 -0
  171. package/presets/opencode/next/rules/anti-sycophancy-discipline.md +23 -0
  172. package/presets/opencode/next/rules/api-services.md +72 -0
  173. package/presets/opencode/next/rules/architecture-boundaries-ui.md +9 -0
  174. package/presets/opencode/next/rules/architecture-boundaries.md +86 -0
  175. package/presets/opencode/next/rules/arrow-functions.md +25 -0
  176. package/presets/opencode/next/rules/code-quality-and-refactoring.md +40 -0
  177. package/presets/opencode/next/rules/code-review-mr.md +40 -0
  178. package/presets/opencode/next/rules/css-property-order-stylelint.md +5 -0
  179. package/presets/opencode/next/rules/design-guidance.md +99 -0
  180. package/presets/opencode/next/rules/feature-delivery-workflow.md +49 -0
  181. package/presets/opencode/next/rules/http-client.md +55 -0
  182. package/presets/opencode/next/rules/layer-barrel-exports.md +68 -0
  183. package/presets/opencode/next/rules/mcp-usage.md +86 -0
  184. package/presets/opencode/next/rules/mockup-review.md +107 -0
  185. package/presets/opencode/next/rules/navigation-router-stack.md +56 -0
  186. package/presets/opencode/next/rules/navigation-router-ui.md +10 -0
  187. package/presets/opencode/next/rules/next-app-core.md +29 -0
  188. package/presets/opencode/next/rules/next-app-router.md +44 -0
  189. package/presets/opencode/next/rules/no-props-spread.md +56 -0
  190. package/presets/opencode/next/rules/no-type-assertion-as-import-export.md +51 -0
  191. package/presets/opencode/next/rules/package-manager.md +11 -0
  192. package/presets/opencode/next/rules/playwright-agents.md +67 -0
  193. package/presets/opencode/next/rules/post-change-lint.md +35 -0
  194. package/presets/opencode/next/rules/preset-layering.md +27 -0
  195. package/presets/opencode/next/rules/preset-no-cross-stack-leakage.md +90 -0
  196. package/presets/opencode/next/rules/preset-pr-checklist.md +36 -0
  197. package/presets/opencode/next/rules/preset-token-budget.md +39 -0
  198. package/presets/opencode/next/rules/preset-twin-sync.md +81 -0
  199. package/presets/opencode/next/rules/product-specs-authoring.md +112 -0
  200. package/presets/opencode/next/rules/product-specs.md +56 -0
  201. package/presets/opencode/next/rules/public-imports.md +50 -0
  202. package/presets/opencode/next/rules/react-a11y-coding.md +31 -0
  203. package/presets/opencode/next/rules/react-next-anti-hallucination.md +60 -0
  204. package/presets/opencode/next/rules/react-ui.md +105 -0
  205. package/presets/opencode/next/rules/reference-features.md +45 -0
  206. package/presets/opencode/next/rules/security-next.md +45 -0
  207. package/presets/opencode/next/rules/store-rtk.md +78 -0
  208. package/presets/opencode/next/rules/technical-retro.md +11 -0
  209. package/presets/opencode/next/rules/tests-e2e-structure.md +72 -0
  210. package/presets/opencode/next/rules/tests-unit.md +72 -0
  211. package/presets/opencode/next/rules/types-jsdoc.md +43 -0
  212. package/presets/opencode/next/rules/ux-design.md +99 -0
  213. package/presets/opencode/next/skills/README.md +21 -0
  214. package/presets/opencode/next/skills/ci-investigation/SKILL.md +44 -0
  215. package/presets/opencode/next/skills/code-review/SKILL.md +44 -0
  216. package/presets/opencode/next/skills/debug-investigation/SKILL.md +59 -0
  217. package/presets/opencode/next/skills/feature-delivery/SKILL.md +39 -0
  218. package/presets/opencode/next/skills/mockup-review/SKILL.md +29 -0
  219. package/presets/opencode/next/skills/playwright-e2e/SKILL.md +33 -0
  220. package/presets/opencode/next/skills/react-performance/SKILL.md +44 -0
  221. package/presets/opencode/next/skills/spec-authoring/SKILL.md +26 -0
  222. package/presets/opencode/next/skills/spec-discovery/SKILL.md +58 -0
  223. package/presets/opencode/next/skills/technical-retro/SKILL.md +54 -0
  224. package/presets/opencode/next/skills/unit-testing/SKILL.md +34 -0
  225. package/presets/opencode/next/skills/ux-design/SKILL.md +36 -0
  226. package/presets/opencode/next/skills/write-adr/SKILL.md +41 -0
  227. package/presets/opencode/next/team/README.md +148 -0
  228. package/presets/opencode/next/team/conventions.md +21 -0
  229. package/presets/opencode/next/team/fixtures/artifact-manifest.example.json +79 -0
  230. package/presets/opencode/next/team/fixtures/bugfix-standard.json +25 -0
  231. package/presets/opencode/next/team/fixtures/feature-full.json +29 -0
  232. package/presets/opencode/next/team/fixtures/feature-light.json +27 -0
  233. package/presets/opencode/next/team/fixtures/feature-with-figma-url.json +17 -0
  234. package/presets/opencode/next/team/fixtures/feature-with-ux-no-figma.json +17 -0
  235. package/presets/opencode/next/team/fixtures/memlog.example.md +9 -0
  236. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/human-gate/metrics.json +69 -0
  237. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/missing-ledger/status.json +5 -0
  238. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/parallel-receipts/metrics.json +79 -0
  239. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/serial-retry/metrics.json +124 -0
  240. package/presets/opencode/next/team/fixtures/metrics-scenarios/tasks/validation-failed/metrics.json +124 -0
  241. package/presets/opencode/next/team/fixtures/metrics.example.json +54 -0
  242. package/presets/opencode/next/team/fixtures/mockup-review-fail-embedded-status.json +8 -0
  243. package/presets/opencode/next/team/fixtures/mockup-review-standalone.json +14 -0
  244. package/presets/opencode/next/team/fixtures/ux-design-standalone.json +15 -0
  245. package/presets/opencode/next/team/tasks/.gitkeep +0 -0
  246. package/presets/opencode/platform-contract.json +131 -0
  247. package/scripts/capture-cli-golden.mjs +5 -1
  248. package/scripts/check-chain-hook-coverage.sh +1 -0
  249. package/scripts/check-opencode-adapter-map.mjs +294 -0
  250. package/scripts/check-opencode-local-overlay.mjs +56 -0
  251. package/scripts/check-opencode-platform-contract.mjs +388 -0
  252. package/scripts/check-opencode-token-budget.mjs +48 -0
  253. package/scripts/check-preset-leakage.sh +30 -1
  254. package/scripts/check-preset-structure.sh +45 -0
  255. package/scripts/check-task-router-intents.sh +4 -4
  256. package/scripts/fixtures/cli-golden/claude/next.json +8 -8
  257. package/scripts/fixtures/cli-golden/cursor/next.json +8 -8
  258. package/scripts/fixtures/cli-golden/mavis/next.json +9 -9
  259. package/scripts/fixtures/cli-golden/opencode/next.json +125 -0
  260. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/AGENTS.md +1 -0
  261. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/extra.mdc +1 -0
  262. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/cursor/rules/sample.mdc +2 -0
  263. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/OPENCODE.md +1 -0
  264. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/adapter-map.json +26 -0
  265. package/scripts/fixtures/opencode-adapter-map/invalid-unmapped/opencode/rules/sample.md +2 -0
  266. package/scripts/fixtures/opencode-adapter-map/valid/cursor/AGENTS.md +1 -0
  267. package/scripts/fixtures/opencode-adapter-map/valid/cursor/rules/sample.mdc +2 -0
  268. package/scripts/fixtures/opencode-adapter-map/valid/opencode/OPENCODE.md +1 -0
  269. package/scripts/fixtures/opencode-adapter-map/valid/opencode/adapter-map.json +26 -0
  270. package/scripts/fixtures/opencode-adapter-map/valid/opencode/rules/sample.md +2 -0
  271. package/scripts/fixtures/opencode-platform-contract/invalid-home-destination.json +131 -0
  272. package/scripts/fixtures/opencode-platform-contract/invalid-missing-capability.json +126 -0
  273. package/scripts/fixtures/opencode-platform-contract/invalid-released-unknown-stack.json +133 -0
  274. package/scripts/fixtures/opencode-platform-contract/invalid-unknown-native-path.json +131 -0
  275. package/scripts/fixtures/opencode-platform-contract/invalid-version-pin.json +132 -0
  276. package/scripts/fixtures/opencode-platform-contract/valid.json +131 -0
  277. package/scripts/generate-mavis-next.mjs +9 -7
  278. package/scripts/generate-opencode-next.mjs +831 -0
  279. package/scripts/lib/discover-hooks.mjs +23 -1
  280. package/scripts/sync-dogfood-mavis.sh +90 -0
  281. package/scripts/sync-dogfood-opencode.sh +85 -0
  282. package/scripts/test-agent-task-metrics-hooks.mjs +4 -1
  283. package/scripts/test-chain-team-phases-coverage.mjs +6 -1
  284. package/scripts/test-cli-init-clean.mjs +111 -0
  285. package/scripts/test-opencode-context-budget.mjs +53 -0
@@ -0,0 +1,831 @@
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, buildRepoAgents());
345
+ entries.push(
346
+ entry({
347
+ source,
348
+ target,
349
+ category: 'entry',
350
+ disposition: 'transform',
351
+ loadMode: 'root-entry',
352
+ reason: 'Root AGENTS.md is OpenCode native always-on plus cross-tool pointers',
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 buildRepoAgents() {
545
+ return `# Next.js — agent instructions
546
+
547
+ Cross-tool repo-root guide. Tool-specific maps stay under \`.cursor/AGENTS.md\`,
548
+ \`.claude/CLAUDE.md\`, \`.mavis/AGENTS.md\`, and \`.opencode/AGENTS.md\`.
549
+
550
+ When this OpenCode preset is installed, read \`.opencode/AGENTS.md\` on every
551
+ turn. Do not write \`~/.config/opencode/**\`. Do not mutate
552
+ \`.opencode/team/tasks/<slug>/status.json\` or \`metrics.json\` — the chain
553
+ runner owns those files.
554
+
555
+ ## Cross-tool setup
556
+
557
+ - Treat this file as the **shared** entry for any agent (Cursor, Claude Code,
558
+ Mavis, OpenCode, or other).
559
+ - Install:
560
+
561
+ \`\`\`bash
562
+ npx @bonesofspring/ai-rules init cursor --preset next
563
+ npx @bonesofspring/ai-rules init claude --preset next
564
+ npx @bonesofspring/ai-rules init mavis --preset next
565
+ npx @bonesofspring/ai-rules init opencode --preset next
566
+ \`\`\`
567
+
568
+ \`init\` overwrites this file from the preset (\`REPO_AGENTS.md\` → \`AGENTS.md\`).
569
+ Living gotchas belong in \`team/conventions.md\`, not here.
570
+
571
+ ## OpenCode load map
572
+
573
+ After install, also read:
574
+
575
+ - \`.opencode/AGENTS.md\` — OpenCode load map, roles, and procedures
576
+ - \`.opencode/rules/next-app-core.md\`
577
+ - \`.opencode/rules/package-manager.md\`
578
+ - \`.opencode/rules/code-quality-and-refactoring.md\`
579
+ - \`.opencode/rules/ai-local-overlay.md\`
580
+
581
+ Do **not** preload the full \`.opencode/rules/\` catalog. Native agents live in
582
+ \`.opencode/agents/\`; skills in \`.opencode/skills/\`; slash commands in
583
+ \`.opencode/commands/\` (\`/task\`). After a producer receipt run
584
+ \`bash .opencode/hooks/chain-team-phases.sh\`.
585
+
586
+ ## Commands (test / lint / build)
587
+
588
+ Working directory for app scripts: **\`app/\`**. Detect the package manager from
589
+ lockfile / \`packageManager\` (\`package-manager\` rule).
590
+
591
+ | Gate | Typical command |
592
+ |------|-----------------|
593
+ | Lint JS | \`yarn lint:js\` (or project equivalent) from \`app/\` |
594
+ | Lint CSS | \`yarn lint:css\` from \`app/\` |
595
+ | Unit tests | project unit script under \`app/\` |
596
+ | Build | project \`build\` under \`app/\` |
597
+
598
+ After code edits: invoke **\`post-change-lint\`** (requestable). Full gate: agent
599
+ \`build-verifier\`.
600
+
601
+ ## Security basics
602
+
603
+ - Do not commit secrets (\`.env\`, tokens, credentials).
604
+ - Review destructive git (\`reset --hard\`, force-push) before running; respect
605
+ shell hooks.
606
+ - Do not weaken CI or skip hooks unless the user explicitly asks.
607
+
608
+ ## Tool entry points
609
+
610
+ | Tool | Map |
611
+ |------|-----|
612
+ | Cursor | \`.cursor/AGENTS.md\` (optional if Cursor not installed) |
613
+ | Claude Code | \`.claude/CLAUDE.md\` (optional if Claude Code not installed) |
614
+ | Mavis | \`.mavis/AGENTS.md\` (optional if Mavis not installed) |
615
+ | OpenCode | \`.opencode/AGENTS.md\` (optional if OpenCode not installed) |
616
+
617
+ Rules: \`.cursor/rules/\`, \`.claude/rules/\`, \`.mavis/rules/\`, or \`.opencode/rules/\`.
618
+ Skills: \`.cursor/skills/\`, \`.claude/skills/\`, \`.mavis/skills/\`, or \`.opencode/skills/\`.
619
+
620
+ ## Living conventions
621
+
622
+ Gotchas, local patterns, and team decisions:
623
+ **\`.cursor/team/conventions.md\`**, **\`.claude/team/conventions.md\`**,
624
+ **\`.mavis/team/conventions.md\`**, and/or **\`.opencode/team/conventions.md\`**
625
+ (fill after init; not overwritten on every dogfood sync when already present).
626
+
627
+ ## Local overlay (\`.ai.local/\`) — MANDATORY
628
+
629
+ A \`.ai.local/\` directory MAY exist at the repo root or at any nesting depth in
630
+ monorepos. **All agents MUST read and apply it on every turn.** Rules there
631
+ override preset rules; in conflicts, the local rule wins.
632
+ \`override: kill <preset-rule-name>\` drops the named preset rule from the
633
+ agent's context entirely.
634
+
635
+ Plain markdown rules under \`.ai.local/rules/*.md\` are read by the agent with
636
+ higher priority than preset rules. Living gotchas live in
637
+ \`.ai.local/conventions.md\`.
638
+
639
+ Authoring guide: \`docs/LOCAL-OVERLAY.md\` in the \`@bonesofspring/ai-rules\`
640
+ package. The \`ai-local-overlay\` rule is the mechanism; this \`MANDATORY\`
641
+ section is the reinforcement.
642
+ `;
643
+ }
644
+
645
+ function stripInstallSection(body) {
646
+ const index = body.search(/^## Install\r?\n/m);
647
+ if (index < 0) {
648
+ return body.trimEnd();
649
+ }
650
+ return body.slice(0, index).trimEnd();
651
+ }
652
+
653
+ function stripEmbeddedCatalogLine(body) {
654
+ return body.replace(
655
+ /^See `\.opencode\/rules\/README\.md` for the full catalog[^\n]*\n+/m,
656
+ '',
657
+ );
658
+ }
659
+
660
+ function buildOpencodeEntry(sourceText) {
661
+ const body = stripEmbeddedCatalogLine(
662
+ stripInstallSection(
663
+ dropLeadingHeading(rewriteProse(stripFrontmatter(sourceText).body)),
664
+ ),
665
+ );
666
+ return `# Next.js stack — OpenCode agent team
667
+
668
+ Short OpenCode entry. Native discovery: \`.opencode/agents/\`,
669
+ \`.opencode/skills/\`, \`.opencode/commands/\`. Rule files under
670
+ \`.opencode/rules/*.md\` are **not** auto-loaded.
671
+
672
+ Root \`AGENTS.md\` is native always-on. Read it first, then this file.
673
+
674
+ ## How to load
675
+
676
+ - Essential rules: Read immediately (paths below). Do not preload the catalog.
677
+ - Other rules: lazy \`Read\` \`.opencode/rules/<stem>.md\` when the task matches.
678
+ - Agents: native \`@name\` / Task from \`.opencode/agents/\`.
679
+ - Commands: native \`/task\` from \`.opencode/commands/\`.
680
+ - After a producer receipt, run \`bash .opencode/hooks/chain-team-phases.sh\`.
681
+ Agents must not write lifecycle fields in \`status.json\` / \`metrics.json\`.
682
+
683
+ ## Essential rules (read every turn)
684
+
685
+ - \`.opencode/rules/next-app-core.md\`
686
+ - \`.opencode/rules/package-manager.md\`
687
+ - \`.opencode/rules/code-quality-and-refactoring.md\`
688
+ - \`.opencode/rules/ai-local-overlay.md\`
689
+
690
+ See \`.opencode/rules/README.md\` for the full catalog and loading strategy.
691
+
692
+ **Platform docs:** published with the \`@bonesofspring/ai-rules\` package
693
+ (\`docs/OPENCODE.md\`, \`docs/OPENCODE-COMPATIBILITY.md\`) — **not** copied into
694
+ the consumer repo. Source: https://github.com/bonesofspring/ai-rules
695
+
696
+ ${body}
697
+
698
+ ## Install
699
+
700
+ \`\`\`bash
701
+ npx @bonesofspring/ai-rules init opencode --preset next
702
+ \`\`\`
703
+
704
+ Copies rules, commands, agents, hooks, skills, team, \`OPENCODE.md\` →
705
+ \`.opencode/AGENTS.md\`, \`MCP.md\`, \`opencode.json\` → project \`opencode.json\`
706
+ (\`mcp\` merge), plus \`REPO_AGENTS.md\` → root \`AGENTS.md\`.
707
+ `;
708
+ }
709
+
710
+ function buildCommandsReadme() {
711
+ let text = rewriteProse(readSiblingReadme('commands/README.md'));
712
+ text = text.replace(
713
+ '# Cursor commands (next preset)',
714
+ '# OpenCode commands (next preset)',
715
+ );
716
+ text = text.replace(
717
+ 'Slash commands are copied to `.opencode/commands/` on `ai-rules init opencode --preset next`.',
718
+ 'Slash commands are copied to `.opencode/commands/` on `ai-rules init opencode --preset next`. OpenCode loads `/<filename>` natively.',
719
+ );
720
+ text = text.replace(
721
+ '- Orchestrator: `../rules/agent-team-orchestrator.mdc`',
722
+ '- Orchestrator: `../rules/agent-team-orchestrator.md`',
723
+ );
724
+ text = text.replace(
725
+ '- Hooks: `../hooks.json`, `../hooks/chain-team-phases.sh`',
726
+ '- Hooks: `../hooks/chain-team-phases.sh` only (semi-auto runner; no `hooks.json`)',
727
+ );
728
+ return text.endsWith('\n') ? text : `${text}\n`;
729
+ }
730
+
731
+ function buildAgentsReadme() {
732
+ let text = rewriteProse(readSiblingReadme('agents/README.md'));
733
+ text = text.replace(
734
+ 'Role-based subagents for the Cursor team pipeline.',
735
+ 'Role-based subagents for the OpenCode team pipeline (native `.opencode/agents/`).',
736
+ );
737
+ text = text.replace(
738
+ /## Frontmatter `readonly`[\s\S]*?(?=## Orchestration)/,
739
+ `## OpenCode packaging
740
+
741
+ Agents ship with OpenCode YAML frontmatter: \`description\`, \`mode: subagent\`,
742
+ and \`permission\`. Do **not** pin \`model\` in this preset.
743
+
744
+ - Implementers (\`feature-developer\`, \`debugger\`, \`ci-investigator\`, test generators/healers): \`permission.edit: allow\`.
745
+ - Reviewer / planner roles: \`edit\` deny except \`.opencode/team/**\`. Prompt bodies still forbid production edits.
746
+
747
+ **Team artifacts:** agents write \`.opencode/team/**\` deliverables. They must
748
+ **not** mutate lifecycle fields in \`status.json\` or \`metrics.json\` —
749
+ \`bash .opencode/hooks/chain-team-phases.sh\` owns those after each producer receipt.
750
+
751
+ ## Model tiers (advisory)
752
+
753
+ Optional \`steps[].model\` in \`pipeline.json\` (\`cheap\` | \`standard\` | \`strong\`)
754
+ is advisory for the operator's OpenCode provider — see \`task-router.md\`.
755
+
756
+ `,
757
+ );
758
+ return text.endsWith('\n') ? text : `${text}\n`;
759
+ }
760
+
761
+ function buildStackReadme() {
762
+ return `# Next.js preset (OpenCode)
763
+
764
+ Sibling adapter derived from \`presets/cursor/next\`. Platform id \`opencode\`;
765
+ runtime \`opencode\`. Consumer install root is \`.opencode/\` (native agents,
766
+ skills, commands) plus root \`AGENTS.md\` and \`opencode.json\`.
767
+
768
+ Mapping: \`adapter-map.json\`. Do not derive this tree from Claude.
769
+
770
+ \`\`\`bash
771
+ npx @bonesofspring/ai-rules init opencode --preset next
772
+ \`\`\`
773
+
774
+ \`releasedStacks\` currently lists **\`next\`** only.
775
+
776
+ Omitted from this pilot: \`BUGBOT.md\`, \`hooks.json\`, hook examples, and
777
+ unwired \`guard-shell-command.sh\`.
778
+ `;
779
+ }
780
+
781
+ function buildRulesReadme() {
782
+ return `# OpenCode Next rules
783
+
784
+ Plain Markdown. OpenCode does **not** auto-load these files. Root \`AGENTS.md\`
785
+ and \`.opencode/AGENTS.md\` list four essential paths to Read immediately; the
786
+ rest are lazy-loaded when the task matches.
787
+
788
+ Do not put this catalog into \`opencode.json\` \`instructions\` as the only
789
+ always-on path (V2 may ignore that field).
790
+
791
+ ## Essential (entry-critical)
792
+
793
+ \`next-app-core\`, \`package-manager\`, \`code-quality-and-refactoring\`,
794
+ \`ai-local-overlay\`.
795
+
796
+ ## Requestable
797
+
798
+ All other \`*.md\` files in this directory. Same stems as Cursor
799
+ \`rules/README.md\`.
800
+
801
+ Shared-core markers (\`<!-- shared-core: … -->\`) are preserved from Cursor.
802
+ `;
803
+ }
804
+
805
+ function buildHooksReadme() {
806
+ return `# OpenCode hooks
807
+
808
+ No Cursor \`hooks.json\` and no TypeScript plugin in v1.
809
+
810
+ ## Shipped
811
+
812
+ \`chain-team-phases.sh\` — manual/semi-auto runner. After a producer receipt:
813
+
814
+ \`\`\`bash
815
+ printf '%s\\n' '{"status":"completed","attemptId":"<runtime-attempt-id>"}' \\
816
+ | bash .opencode/hooks/chain-team-phases.sh
817
+ \`\`\`
818
+
819
+ \`<runtime-attempt-id>\` comes from \`.opencode/team/tasks/<slug>/status.json\`.
820
+ Do not invent it. Agents never write lifecycle fields in \`status.json\` or
821
+ \`metrics.json\`.
822
+
823
+ ## Omitted
824
+
825
+ - \`hooks.json\` — no native registration schema in this preset
826
+ - \`guard-shell-command.sh\` — not wired; do not describe it as enforcement
827
+ - \`hooks/examples/**\` — plugin port is a follow-up
828
+ `;
829
+ }
830
+
831
+ main();