@dewtech/dare-cli 3.10.0 → 3.12.0

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 (441) hide show
  1. package/README.md +7 -1
  2. package/dist/__tests__/ci-pr-regression.test.d.ts +2 -0
  3. package/dist/__tests__/ci-pr-regression.test.d.ts.map +1 -0
  4. package/dist/__tests__/ci-pr-regression.test.js +76 -0
  5. package/dist/__tests__/ci-pr-regression.test.js.map +1 -0
  6. package/dist/__tests__/dashboard-front.test.d.ts +2 -0
  7. package/dist/__tests__/dashboard-front.test.d.ts.map +1 -0
  8. package/dist/__tests__/dashboard-front.test.js +37 -0
  9. package/dist/__tests__/dashboard-front.test.js.map +1 -0
  10. package/dist/__tests__/dashboard-regression.test.d.ts +2 -0
  11. package/dist/__tests__/dashboard-regression.test.d.ts.map +1 -0
  12. package/dist/__tests__/dashboard-regression.test.js +68 -0
  13. package/dist/__tests__/dashboard-regression.test.js.map +1 -0
  14. package/dist/__tests__/dynamic-dag-regression.test.d.ts +2 -0
  15. package/dist/__tests__/dynamic-dag-regression.test.d.ts.map +1 -0
  16. package/dist/__tests__/dynamic-dag-regression.test.js +464 -0
  17. package/dist/__tests__/dynamic-dag-regression.test.js.map +1 -0
  18. package/dist/__tests__/ensure-skills.test.js +5 -0
  19. package/dist/__tests__/ensure-skills.test.js.map +1 -1
  20. package/dist/__tests__/ide-command-parity.test.js +2 -0
  21. package/dist/__tests__/ide-command-parity.test.js.map +1 -1
  22. package/dist/__tests__/project-generator.test.js +17 -0
  23. package/dist/__tests__/project-generator.test.js.map +1 -1
  24. package/dist/__tests__/reverse-facts.test.js +1 -0
  25. package/dist/__tests__/reverse-facts.test.js.map +1 -1
  26. package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
  27. package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
  28. package/dist/__tests__/terminal-parity-regression.test.js +116 -0
  29. package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
  30. package/dist/__tests__/terminal-parity.test.d.ts +2 -0
  31. package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
  32. package/dist/__tests__/terminal-parity.test.js +81 -0
  33. package/dist/__tests__/terminal-parity.test.js.map +1 -0
  34. package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
  35. package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
  36. package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
  37. package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
  38. package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
  39. package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
  40. package/dist/agent/__tests__/codex-driver.test.js +68 -0
  41. package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
  42. package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
  43. package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
  44. package/dist/agent/__tests__/cursor-driver.test.js +52 -0
  45. package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
  46. package/dist/agent/driver.d.ts +1 -1
  47. package/dist/agent/driver.d.ts.map +1 -1
  48. package/dist/agent/drivers/antigravity.d.ts +8 -0
  49. package/dist/agent/drivers/antigravity.d.ts.map +1 -0
  50. package/dist/agent/drivers/antigravity.js +99 -0
  51. package/dist/agent/drivers/antigravity.js.map +1 -0
  52. package/dist/agent/drivers/codex.d.ts +12 -0
  53. package/dist/agent/drivers/codex.d.ts.map +1 -0
  54. package/dist/agent/drivers/codex.js +137 -0
  55. package/dist/agent/drivers/codex.js.map +1 -0
  56. package/dist/agent/drivers/cursor.d.ts +8 -0
  57. package/dist/agent/drivers/cursor.d.ts.map +1 -0
  58. package/dist/agent/drivers/cursor.js +99 -0
  59. package/dist/agent/drivers/cursor.js.map +1 -0
  60. package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
  61. package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
  62. package/dist/ai/__tests__/ai-core.test.js +41 -0
  63. package/dist/ai/__tests__/ai-core.test.js.map +1 -0
  64. package/dist/ai/__tests__/parity.test.d.ts +2 -0
  65. package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
  66. package/dist/ai/__tests__/parity.test.js +36 -0
  67. package/dist/ai/__tests__/parity.test.js.map +1 -0
  68. package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
  69. package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
  70. package/dist/ai/__tests__/pipeline.test.js +147 -0
  71. package/dist/ai/__tests__/pipeline.test.js.map +1 -0
  72. package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
  73. package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
  74. package/dist/ai/__tests__/refine-bridge.test.js +17 -0
  75. package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
  76. package/dist/ai/__tests__/resolve.test.d.ts +2 -0
  77. package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
  78. package/dist/ai/__tests__/resolve.test.js +42 -0
  79. package/dist/ai/__tests__/resolve.test.js.map +1 -0
  80. package/dist/ai/capabilities.d.ts +3 -0
  81. package/dist/ai/capabilities.d.ts.map +1 -0
  82. package/dist/ai/capabilities.js +11 -0
  83. package/dist/ai/capabilities.js.map +1 -0
  84. package/dist/ai/command-options.d.ts +10 -0
  85. package/dist/ai/command-options.d.ts.map +1 -0
  86. package/dist/ai/command-options.js +15 -0
  87. package/dist/ai/command-options.js.map +1 -0
  88. package/dist/ai/config.d.ts +27 -0
  89. package/dist/ai/config.d.ts.map +1 -0
  90. package/dist/ai/config.js +89 -0
  91. package/dist/ai/config.js.map +1 -0
  92. package/dist/ai/parity.d.ts +13 -0
  93. package/dist/ai/parity.d.ts.map +1 -0
  94. package/dist/ai/parity.js +87 -0
  95. package/dist/ai/parity.js.map +1 -0
  96. package/dist/ai/parse-json-output.d.ts +5 -0
  97. package/dist/ai/parse-json-output.d.ts.map +1 -0
  98. package/dist/ai/parse-json-output.js +25 -0
  99. package/dist/ai/parse-json-output.js.map +1 -0
  100. package/dist/ai/pipeline.d.ts +20 -0
  101. package/dist/ai/pipeline.d.ts.map +1 -0
  102. package/dist/ai/pipeline.js +303 -0
  103. package/dist/ai/pipeline.js.map +1 -0
  104. package/dist/ai/prompts.d.ts +6 -0
  105. package/dist/ai/prompts.d.ts.map +1 -0
  106. package/dist/ai/prompts.js +49 -0
  107. package/dist/ai/prompts.js.map +1 -0
  108. package/dist/ai/providers.d.ts +63 -0
  109. package/dist/ai/providers.d.ts.map +1 -0
  110. package/dist/ai/providers.js +297 -0
  111. package/dist/ai/providers.js.map +1 -0
  112. package/dist/ai/refine-bridge.d.ts +5 -0
  113. package/dist/ai/refine-bridge.d.ts.map +1 -0
  114. package/dist/ai/refine-bridge.js +14 -0
  115. package/dist/ai/refine-bridge.js.map +1 -0
  116. package/dist/ai/registry.d.ts +12 -0
  117. package/dist/ai/registry.d.ts.map +1 -0
  118. package/dist/ai/registry.js +43 -0
  119. package/dist/ai/registry.js.map +1 -0
  120. package/dist/ai/resolve.d.ts +28 -0
  121. package/dist/ai/resolve.d.ts.map +1 -0
  122. package/dist/ai/resolve.js +83 -0
  123. package/dist/ai/resolve.js.map +1 -0
  124. package/dist/ai/schemas.d.ts +175 -0
  125. package/dist/ai/schemas.d.ts.map +1 -0
  126. package/dist/ai/schemas.js +199 -0
  127. package/dist/ai/schemas.js.map +1 -0
  128. package/dist/ai/types.d.ts +52 -0
  129. package/dist/ai/types.d.ts.map +1 -0
  130. package/dist/ai/types.js +8 -0
  131. package/dist/ai/types.js.map +1 -0
  132. package/dist/bin/dare.js +4 -0
  133. package/dist/bin/dare.js.map +1 -1
  134. package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
  135. package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
  136. package/dist/commands/__tests__/ai-command.test.js +68 -0
  137. package/dist/commands/__tests__/ai-command.test.js.map +1 -0
  138. package/dist/commands/__tests__/dashboard-command.test.d.ts +2 -0
  139. package/dist/commands/__tests__/dashboard-command.test.d.ts.map +1 -0
  140. package/dist/commands/__tests__/dashboard-command.test.js +62 -0
  141. package/dist/commands/__tests__/dashboard-command.test.js.map +1 -0
  142. package/dist/commands/__tests__/execute-agent.test.js +82 -0
  143. package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
  144. package/dist/commands/__tests__/gate-flags.test.d.ts +2 -0
  145. package/dist/commands/__tests__/gate-flags.test.d.ts.map +1 -0
  146. package/dist/commands/__tests__/gate-flags.test.js +58 -0
  147. package/dist/commands/__tests__/gate-flags.test.js.map +1 -0
  148. package/dist/commands/__tests__/persist-viz.test.d.ts +2 -0
  149. package/dist/commands/__tests__/persist-viz.test.d.ts.map +1 -0
  150. package/dist/commands/__tests__/persist-viz.test.js +178 -0
  151. package/dist/commands/__tests__/persist-viz.test.js.map +1 -0
  152. package/dist/commands/__tests__/refine-apply.test.d.ts +2 -0
  153. package/dist/commands/__tests__/refine-apply.test.d.ts.map +1 -0
  154. package/dist/commands/__tests__/refine-apply.test.js +118 -0
  155. package/dist/commands/__tests__/refine-apply.test.js.map +1 -0
  156. package/dist/commands/__tests__/replan-splice.test.d.ts +2 -0
  157. package/dist/commands/__tests__/replan-splice.test.d.ts.map +1 -0
  158. package/dist/commands/__tests__/replan-splice.test.js +295 -0
  159. package/dist/commands/__tests__/replan-splice.test.js.map +1 -0
  160. package/dist/commands/ai.d.ts +3 -0
  161. package/dist/commands/ai.d.ts.map +1 -0
  162. package/dist/commands/ai.js +141 -0
  163. package/dist/commands/ai.js.map +1 -0
  164. package/dist/commands/blueprint.d.ts.map +1 -1
  165. package/dist/commands/blueprint.js +17 -3
  166. package/dist/commands/blueprint.js.map +1 -1
  167. package/dist/commands/dag.d.ts.map +1 -1
  168. package/dist/commands/dag.js +2 -0
  169. package/dist/commands/dag.js.map +1 -1
  170. package/dist/commands/dashboard.d.ts +17 -0
  171. package/dist/commands/dashboard.d.ts.map +1 -0
  172. package/dist/commands/dashboard.js +77 -0
  173. package/dist/commands/dashboard.js.map +1 -0
  174. package/dist/commands/design.d.ts.map +1 -1
  175. package/dist/commands/design.js +21 -2
  176. package/dist/commands/design.js.map +1 -1
  177. package/dist/commands/discover.d.ts.map +1 -1
  178. package/dist/commands/discover.js +9 -1
  179. package/dist/commands/discover.js.map +1 -1
  180. package/dist/commands/dna.d.ts.map +1 -1
  181. package/dist/commands/dna.js +23 -3
  182. package/dist/commands/dna.js.map +1 -1
  183. package/dist/commands/execute.d.ts +11 -0
  184. package/dist/commands/execute.d.ts.map +1 -1
  185. package/dist/commands/execute.js +219 -6
  186. package/dist/commands/execute.js.map +1 -1
  187. package/dist/commands/graph.d.ts.map +1 -1
  188. package/dist/commands/graph.js +33 -5
  189. package/dist/commands/graph.js.map +1 -1
  190. package/dist/commands/guard.d.ts.map +1 -1
  191. package/dist/commands/guard.js +27 -3
  192. package/dist/commands/guard.js.map +1 -1
  193. package/dist/commands/init.d.ts.map +1 -1
  194. package/dist/commands/init.js +1 -0
  195. package/dist/commands/init.js.map +1 -1
  196. package/dist/commands/migrate.d.ts.map +1 -1
  197. package/dist/commands/migrate.js +14 -2
  198. package/dist/commands/migrate.js.map +1 -1
  199. package/dist/commands/patterns.d.ts.map +1 -1
  200. package/dist/commands/patterns.js +14 -2
  201. package/dist/commands/patterns.js.map +1 -1
  202. package/dist/commands/refine.d.ts +2 -0
  203. package/dist/commands/refine.d.ts.map +1 -1
  204. package/dist/commands/refine.js +94 -22
  205. package/dist/commands/refine.js.map +1 -1
  206. package/dist/commands/reverse.d.ts.map +1 -1
  207. package/dist/commands/reverse.js +28 -3
  208. package/dist/commands/reverse.js.map +1 -1
  209. package/dist/commands/review.d.ts.map +1 -1
  210. package/dist/commands/review.js +60 -7
  211. package/dist/commands/review.js.map +1 -1
  212. package/dist/core/types/project.d.ts +1 -1
  213. package/dist/core/types/project.d.ts.map +1 -1
  214. package/dist/dag-runner/__tests__/maxdepth-config.test.d.ts +2 -0
  215. package/dist/dag-runner/__tests__/maxdepth-config.test.d.ts.map +1 -0
  216. package/dist/dag-runner/__tests__/maxdepth-config.test.js +28 -0
  217. package/dist/dag-runner/__tests__/maxdepth-config.test.js.map +1 -0
  218. package/dist/dag-runner/__tests__/sub-dag.test.d.ts +2 -0
  219. package/dist/dag-runner/__tests__/sub-dag.test.d.ts.map +1 -0
  220. package/dist/dag-runner/__tests__/sub-dag.test.js +127 -0
  221. package/dist/dag-runner/__tests__/sub-dag.test.js.map +1 -0
  222. package/dist/dag-runner/run_dag.d.ts +3 -1
  223. package/dist/dag-runner/run_dag.d.ts.map +1 -1
  224. package/dist/dag-runner/run_dag.js.map +1 -1
  225. package/dist/dag-runner/state-store.d.ts +2 -0
  226. package/dist/dag-runner/state-store.d.ts.map +1 -1
  227. package/dist/dag-runner/state-store.js +44 -8
  228. package/dist/dag-runner/state-store.js.map +1 -1
  229. package/dist/dag-runner/sub-dag.d.ts +23 -0
  230. package/dist/dag-runner/sub-dag.d.ts.map +1 -0
  231. package/dist/dag-runner/sub-dag.js +117 -0
  232. package/dist/dag-runner/sub-dag.js.map +1 -0
  233. package/dist/dashboard/__tests__/dashboard-routes.test.d.ts +2 -0
  234. package/dist/dashboard/__tests__/dashboard-routes.test.d.ts.map +1 -0
  235. package/dist/dashboard/__tests__/dashboard-routes.test.js +102 -0
  236. package/dist/dashboard/__tests__/dashboard-routes.test.js.map +1 -0
  237. package/dist/dashboard/routes.d.ts +8 -0
  238. package/dist/dashboard/routes.d.ts.map +1 -0
  239. package/dist/dashboard/routes.js +70 -0
  240. package/dist/dashboard/routes.js.map +1 -0
  241. package/dist/exec/safe-spawn.d.ts.map +1 -1
  242. package/dist/exec/safe-spawn.js +6 -1
  243. package/dist/exec/safe-spawn.js.map +1 -1
  244. package/dist/graphrag/__tests__/hybrid-integration.test.js +1 -1
  245. package/dist/graphrag/__tests__/hybrid-integration.test.js.map +1 -1
  246. package/dist/graphrag/__tests__/no-heavy-dep-in-core.test.js +2 -2
  247. package/dist/graphrag/__tests__/no-heavy-dep-in-core.test.js.map +1 -1
  248. package/dist/graphrag/embeddings.js +1 -1
  249. package/dist/graphrag/embeddings.js.map +1 -1
  250. package/dist/http/__tests__/shared-app.test.d.ts +2 -0
  251. package/dist/http/__tests__/shared-app.test.d.ts.map +1 -0
  252. package/dist/http/__tests__/shared-app.test.js +57 -0
  253. package/dist/http/__tests__/shared-app.test.js.map +1 -0
  254. package/dist/http/app.d.ts +15 -0
  255. package/dist/http/app.d.ts.map +1 -0
  256. package/dist/http/app.js +32 -0
  257. package/dist/http/app.js.map +1 -0
  258. package/dist/mcp-server/server.d.ts.map +1 -1
  259. package/dist/mcp-server/server.js +5 -15
  260. package/dist/mcp-server/server.js.map +1 -1
  261. package/dist/reporters/__tests__/github-reporter.test.d.ts +2 -0
  262. package/dist/reporters/__tests__/github-reporter.test.d.ts.map +1 -0
  263. package/dist/reporters/__tests__/github-reporter.test.js +93 -0
  264. package/dist/reporters/__tests__/github-reporter.test.js.map +1 -0
  265. package/dist/reporters/ci-gate.d.ts +27 -0
  266. package/dist/reporters/ci-gate.d.ts.map +1 -0
  267. package/dist/reporters/ci-gate.js +100 -0
  268. package/dist/reporters/ci-gate.js.map +1 -0
  269. package/dist/reporters/github.d.ts +36 -0
  270. package/dist/reporters/github.d.ts.map +1 -0
  271. package/dist/reporters/github.js +142 -0
  272. package/dist/reporters/github.js.map +1 -0
  273. package/dist/skills/bundled.d.ts +5 -0
  274. package/dist/skills/bundled.d.ts.map +1 -0
  275. package/dist/skills/bundled.js +34 -0
  276. package/dist/skills/bundled.js.map +1 -0
  277. package/dist/skills/commands/add.d.ts +1 -3
  278. package/dist/skills/commands/add.d.ts.map +1 -1
  279. package/dist/skills/commands/add.js +20 -3
  280. package/dist/skills/commands/add.js.map +1 -1
  281. package/dist/skills/tests/bundled.spec.d.ts +2 -0
  282. package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
  283. package/dist/skills/tests/bundled.spec.js +24 -0
  284. package/dist/skills/tests/bundled.spec.js.map +1 -0
  285. package/dist/telemetry/__tests__/aggregator.test.d.ts +2 -0
  286. package/dist/telemetry/__tests__/aggregator.test.d.ts.map +1 -0
  287. package/dist/telemetry/__tests__/aggregator.test.js +164 -0
  288. package/dist/telemetry/__tests__/aggregator.test.js.map +1 -0
  289. package/dist/telemetry/aggregator.d.ts +43 -0
  290. package/dist/telemetry/aggregator.d.ts.map +1 -0
  291. package/dist/telemetry/aggregator.js +282 -0
  292. package/dist/telemetry/aggregator.js.map +1 -0
  293. package/dist/types/UpdateManifest.types.d.ts +1 -1
  294. package/dist/types/UpdateManifest.types.d.ts.map +1 -1
  295. package/dist/utils/dag-converter.js +1 -1
  296. package/dist/utils/dag-converter.js.map +1 -1
  297. package/dist/utils/excalidraw-renderer.d.ts.map +1 -1
  298. package/dist/utils/excalidraw-renderer.js +1 -0
  299. package/dist/utils/excalidraw-renderer.js.map +1 -1
  300. package/dist/utils/graph-renderer.d.ts +2 -0
  301. package/dist/utils/graph-renderer.d.ts.map +1 -1
  302. package/dist/utils/graph-renderer.js +188 -14
  303. package/dist/utils/graph-renderer.js.map +1 -1
  304. package/dist/utils/project-detector.d.ts +1 -0
  305. package/dist/utils/project-detector.d.ts.map +1 -1
  306. package/dist/utils/project-detector.js +8 -0
  307. package/dist/utils/project-detector.js.map +1 -1
  308. package/dist/utils/project-generator.d.ts +1 -1
  309. package/dist/utils/project-generator.d.ts.map +1 -1
  310. package/dist/utils/project-generator.js +23 -2
  311. package/dist/utils/project-generator.js.map +1 -1
  312. package/dist/utils/templates.d.ts +2 -0
  313. package/dist/utils/templates.d.ts.map +1 -1
  314. package/dist/utils/templates.js +74 -0
  315. package/dist/utils/templates.js.map +1 -1
  316. package/dist/verification/__tests__/decay-policy.test.js +1 -0
  317. package/dist/verification/__tests__/decay-policy.test.js.map +1 -1
  318. package/dist/verification/__tests__/safe-spawn.test.js +12 -0
  319. package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
  320. package/dist/verification/config.d.ts.map +1 -1
  321. package/dist/verification/config.js +2 -0
  322. package/dist/verification/config.js.map +1 -1
  323. package/dist/verification/types.d.ts +2 -0
  324. package/dist/verification/types.d.ts.map +1 -1
  325. package/package.json +3 -2
  326. package/skills/dare-ax/generator.ts +325 -0
  327. package/skills/dare-ax/index.ts +19 -0
  328. package/skills/dare-ax/metrics.ts +352 -0
  329. package/skills/dare-ax/package-lock.json +1855 -0
  330. package/skills/dare-ax/package.json +50 -0
  331. package/skills/dare-ax/secret-detector.ts +123 -0
  332. package/skills/dare-ax/skill.yml +19 -0
  333. package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
  334. package/skills/dare-ax/tests/generator.spec.ts +193 -0
  335. package/skills/dare-ax/tests/metrics.spec.ts +394 -0
  336. package/skills/dare-ax/tests/validator.spec.ts +298 -0
  337. package/skills/dare-ax/tsconfig.json +18 -0
  338. package/skills/dare-ax/types.ts +79 -0
  339. package/skills/dare-ax/validator.ts +238 -0
  340. package/skills/dare-frontend-design/generator.ts +616 -0
  341. package/skills/dare-frontend-design/index.ts +25 -0
  342. package/skills/dare-frontend-design/linter.ts +227 -0
  343. package/skills/dare-frontend-design/metrics.ts +82 -0
  344. package/skills/dare-frontend-design/package-lock.json +1855 -0
  345. package/skills/dare-frontend-design/package.json +43 -0
  346. package/skills/dare-frontend-design/skill.yml +20 -0
  347. package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
  348. package/skills/dare-frontend-design/tsconfig.json +18 -0
  349. package/skills/dare-frontend-design/types.ts +62 -0
  350. package/skills/dare-layered-design/generator.ts +740 -0
  351. package/skills/dare-layered-design/index.ts +17 -0
  352. package/skills/dare-layered-design/linter.ts +462 -0
  353. package/skills/dare-layered-design/metrics.ts +409 -0
  354. package/skills/dare-layered-design/package-lock.json +1855 -0
  355. package/skills/dare-layered-design/package.json +50 -0
  356. package/skills/dare-layered-design/skill.yml +35 -0
  357. package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
  358. package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
  359. package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
  360. package/skills/dare-layered-design/tsconfig.json +18 -0
  361. package/skills/dare-layered-design/types.ts +48 -0
  362. package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
  363. package/skills/dare-llm-integration/index.ts +49 -0
  364. package/skills/dare-llm-integration/metrics.ts +107 -0
  365. package/skills/dare-llm-integration/package-lock.json +1855 -0
  366. package/skills/dare-llm-integration/package.json +49 -0
  367. package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
  368. package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
  369. package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
  370. package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
  371. package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
  372. package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
  373. package/skills/dare-llm-integration/skill.yml +23 -0
  374. package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
  375. package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
  376. package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
  377. package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
  378. package/skills/dare-llm-integration/tsconfig.json +23 -0
  379. package/skills/dare-llm-integration/types.ts +91 -0
  380. package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
  381. package/skills/dare-quality-telemetry/collect.ts +134 -0
  382. package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
  383. package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
  384. package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
  385. package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
  386. package/skills/dare-quality-telemetry/index.ts +18 -0
  387. package/skills/dare-quality-telemetry/metrics.ts +137 -0
  388. package/skills/dare-quality-telemetry/package-lock.json +1855 -0
  389. package/skills/dare-quality-telemetry/package.json +48 -0
  390. package/skills/dare-quality-telemetry/regression.ts +60 -0
  391. package/skills/dare-quality-telemetry/reporter.ts +132 -0
  392. package/skills/dare-quality-telemetry/skill.yml +18 -0
  393. package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
  394. package/skills/dare-quality-telemetry/tsconfig.json +19 -0
  395. package/skills/dare-quality-telemetry/types.ts +41 -0
  396. package/skills/dare-realtime/event_registry.ts +101 -0
  397. package/skills/dare-realtime/index.ts +30 -0
  398. package/skills/dare-realtime/metrics.ts +84 -0
  399. package/skills/dare-realtime/package-lock.json +1855 -0
  400. package/skills/dare-realtime/package.json +43 -0
  401. package/skills/dare-realtime/reconnect_strategy.ts +85 -0
  402. package/skills/dare-realtime/schema_validator.ts +80 -0
  403. package/skills/dare-realtime/skill.yml +21 -0
  404. package/skills/dare-realtime/subscription_manager.ts +106 -0
  405. package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
  406. package/skills/dare-realtime/tsconfig.json +18 -0
  407. package/skills/dare-realtime/types.ts +51 -0
  408. package/templates/.github/workflows/dare-pr.yml +26 -0
  409. package/templates/dashboard/app.js +184 -0
  410. package/templates/dashboard/index.html +63 -0
  411. package/templates/dashboard/style.css +134 -0
  412. package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
  413. package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
  414. package/templates/ide/antigravity/.agents/skills/dare-dashboard/SKILL.md +18 -0
  415. package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
  416. package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
  417. package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
  418. package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
  419. package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
  420. package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
  421. package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
  422. package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
  423. package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
  424. package/templates/ide/claude/.claude/commands/dare-dashboard.md +18 -0
  425. package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
  426. package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
  427. package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
  428. package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
  429. package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
  430. package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
  431. package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
  432. package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
  433. package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
  434. package/templates/ide/cursor/.cursor/commands/dare-dashboard.md +18 -0
  435. package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
  436. package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
  437. package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
  438. package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
  439. package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
  440. package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
  441. package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
@@ -0,0 +1,740 @@
1
+ /**
2
+ * dare-layered-design — LayeredDesignGenerator
3
+ * Scaffolds the 5-layer directory structure for a DARE project.
4
+ * License: MIT
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { Language, ScaffoldOptions, ScaffoldResult } from './types.js';
10
+
11
+ /** Per-language configuration for directory and file naming */
12
+ interface LayerConfig {
13
+ dirName: string;
14
+ description: string;
15
+ exampleFile?: (entityName: string, ext: string) => { name: string; content: string };
16
+ }
17
+
18
+ const LAYERS: LayerConfig[] = [
19
+ {
20
+ dirName: 'handlers',
21
+ description: 'HTTP/gRPC entry points — receives requests, validates input, calls services',
22
+ exampleFile: (entity, ext) => ({
23
+ name: `${toSnakeCase(entity)}_handler${ext}`,
24
+ content: generateHandlerExample(entity, ext),
25
+ }),
26
+ },
27
+ {
28
+ dirName: 'services',
29
+ description: 'Business logic — one operation per service class, no HTTP or DB concerns',
30
+ exampleFile: (entity, ext) => ({
31
+ name: `create_${toSnakeCase(entity)}_service${ext}`,
32
+ content: generateServiceExample(entity, ext),
33
+ }),
34
+ },
35
+ {
36
+ dirName: 'repositories',
37
+ description: 'Data access — abstractions over DB/cache/external APIs',
38
+ exampleFile: (entity, ext) => ({
39
+ name: `${toSnakeCase(entity)}_repository${ext}`,
40
+ content: generateRepositoryExample(entity, ext),
41
+ }),
42
+ },
43
+ {
44
+ dirName: 'models',
45
+ description: 'Domain objects — entities and value objects, no HTTP or DB concerns',
46
+ exampleFile: (entity, ext) => ({
47
+ name: `${toSnakeCase(entity)}${ext}`,
48
+ content: generateModelExample(entity, ext),
49
+ }),
50
+ },
51
+ {
52
+ dirName: 'presenters',
53
+ description: 'Serializers — converts Models to JSON/XML/DTO for responses',
54
+ exampleFile: (entity, ext) => ({
55
+ name: `${toSnakeCase(entity)}_presenter${ext}`,
56
+ content: generatePresenterExample(entity, ext),
57
+ }),
58
+ },
59
+ ];
60
+
61
+ export class LayeredDesignGenerator {
62
+ /**
63
+ * Scaffolds the layered directory structure inside the given project path.
64
+ *
65
+ * Creates:
66
+ * - {srcDir}/handlers/
67
+ * - {srcDir}/services/
68
+ * - {srcDir}/repositories/
69
+ * - {srcDir}/models/
70
+ * - {srcDir}/presenters/
71
+ *
72
+ * Each directory gets a README.md describing its contract, plus either
73
+ * a .gitkeep (default) or example files (if withExamples: true).
74
+ *
75
+ * @param projectPath - Absolute path to the project root.
76
+ * @param options - Scaffold options.
77
+ * @returns ScaffoldResult with lists of created dirs and files.
78
+ */
79
+ scaffold(projectPath: string, options: ScaffoldOptions = {}): ScaffoldResult {
80
+ const {
81
+ srcDir = 'src',
82
+ language = 'typescript',
83
+ withExamples = false,
84
+ exampleEntity = 'example',
85
+ } = options;
86
+
87
+ const ext = languageToExtension(language);
88
+ const rootDir = path.join(projectPath, srcDir);
89
+
90
+ const createdDirs: string[] = [];
91
+ const createdFiles: string[] = [];
92
+
93
+ // Ensure src dir exists
94
+ if (!fs.existsSync(rootDir)) {
95
+ fs.mkdirSync(rootDir, { recursive: true });
96
+ createdDirs.push(rootDir);
97
+ }
98
+
99
+ for (const layer of LAYERS) {
100
+ const layerDir = path.join(rootDir, layer.dirName);
101
+
102
+ // Create directory
103
+ if (!fs.existsSync(layerDir)) {
104
+ fs.mkdirSync(layerDir, { recursive: true });
105
+ createdDirs.push(layerDir);
106
+ }
107
+
108
+ // Create README.md with layer contract
109
+ const readmePath = path.join(layerDir, 'README.md');
110
+ if (!fs.existsSync(readmePath)) {
111
+ fs.writeFileSync(readmePath, generateLayerReadme(layer.dirName, layer.description, language), 'utf-8');
112
+ createdFiles.push(readmePath);
113
+ }
114
+
115
+ if (withExamples && layer.exampleFile) {
116
+ // Create example file
117
+ const { name, content } = layer.exampleFile(exampleEntity, ext);
118
+ const examplePath = path.join(layerDir, name);
119
+ if (!fs.existsSync(examplePath)) {
120
+ fs.writeFileSync(examplePath, content, 'utf-8');
121
+ createdFiles.push(examplePath);
122
+ }
123
+ } else {
124
+ // Create .gitkeep so the directory is tracked by git
125
+ const gitkeepPath = path.join(layerDir, '.gitkeep');
126
+ if (!fs.existsSync(gitkeepPath) && !hasNonReadmeFiles(layerDir)) {
127
+ fs.writeFileSync(gitkeepPath, '', 'utf-8');
128
+ createdFiles.push(gitkeepPath);
129
+ }
130
+ }
131
+ }
132
+
133
+ // Create a top-level ARCHITECTURE.md in srcDir
134
+ const archPath = path.join(rootDir, 'ARCHITECTURE.md');
135
+ if (!fs.existsSync(archPath)) {
136
+ fs.writeFileSync(archPath, generateArchitectureDoc(srcDir, language), 'utf-8');
137
+ createdFiles.push(archPath);
138
+ }
139
+
140
+ return { createdDirs, createdFiles };
141
+ }
142
+ }
143
+
144
+ // ── Layer README generator ──────────────────────────────────────────────────
145
+
146
+ function generateLayerReadme(dirName: string, description: string, _language: Language): string {
147
+ const rules = LAYER_RULES[dirName] ?? [];
148
+ const rulesText = rules.map((r) => `- ${r}`).join('\n');
149
+
150
+ return `# ${capitalize(dirName)}
151
+
152
+ ${description}
153
+
154
+ ## Rules
155
+
156
+ ${rulesText}
157
+
158
+ ## Dependency Rule
159
+
160
+ \`\`\`
161
+ Handler → Service → Repository → Model
162
+ \`\`\`
163
+
164
+ This layer is: **${dirName.toUpperCase()}**
165
+
166
+ ${LAYER_DEPENDENCY_TEXT[dirName] ?? ''}
167
+
168
+ ---
169
+ *Generated by dare-layered-design v1.0.0 — DARE Method*
170
+ `;
171
+ }
172
+
173
+ const LAYER_RULES: Record<string, string[]> = {
174
+ handlers: [
175
+ 'May call Services only — never call Repositories or Models directly',
176
+ 'Validate HTTP input (types, required fields, format) — do NOT validate business rules',
177
+ 'Return HTTP responses (status codes, headers) — the only layer that knows about HTTP',
178
+ 'Authenticate and authorize via middleware — do NOT inline auth logic',
179
+ 'Receive services via dependency injection — do NOT instantiate with new Service()',
180
+ ],
181
+ services: [
182
+ 'May call Repositories only — never call Handlers or HTTP concerns',
183
+ 'One class/module = one business operation (CreateUser, not UserService with 10 methods)',
184
+ 'Receive repositories via dependency injection — do NOT instantiate with new Repository()',
185
+ 'Return domain Models — do NOT return HTTP-specific DTOs',
186
+ 'No knowledge of HTTP status codes, headers, or request/response objects',
187
+ ],
188
+ repositories: [
189
+ 'May call Models only — no awareness of Handlers or Services above',
190
+ 'Abstract over storage: define an interface/trait, provide multiple implementations',
191
+ 'Never throw HTTP-specific exceptions (no 404 exceptions — return null or custom errors)',
192
+ 'Parameterized queries only — never raw string concatenation in SQL',
193
+ 'InMemory implementation required for unit tests (no real DB in unit tests)',
194
+ ],
195
+ models: [
196
+ 'Pure domain objects — no HTTP imports, no database imports, no framework imports',
197
+ 'Business rules that belong to the entity only (e.g., User#full_name)',
198
+ 'No side effects (no email sending, no DB writes) — leave those to Services',
199
+ 'Immutable preferred — use value objects for IDs, money, dates',
200
+ 'Serializers/Presenters are separate — Model does not know JSON format',
201
+ ],
202
+ presenters: [
203
+ 'Converts Models to serializable format (JSON, XML, CSV, gRPC message)',
204
+ 'No business logic — only formatting and field mapping',
205
+ 'May be called from Handlers only — not from Services or Repositories',
206
+ 'One presenter per output format (UserJSONPresenter, UserCSVPresenter)',
207
+ 'Handle date formatting, currency formatting, field renaming',
208
+ ],
209
+ };
210
+
211
+ const LAYER_DEPENDENCY_TEXT: Record<string, string> = {
212
+ handlers: 'Can call: **Services** only\nCannot call: Repositories, Models directly',
213
+ services: 'Can call: **Repositories**\nCannot call: Handlers, HTTP concerns',
214
+ repositories: 'Can call: **Models**\nCannot call: Services, Handlers',
215
+ models: 'Can call: nothing external\nCannot call: any other layer',
216
+ presenters: 'Can call: **Models** (read-only)\nCannot call: Services, Repositories',
217
+ };
218
+
219
+ // ── Architecture doc ─────────────────────────────────────────────────────────
220
+
221
+ function generateArchitectureDoc(srcDir: string, _language: Language): string {
222
+ return `# Architecture — Layered Design
223
+
224
+ This project follows the **DARE Layered Design** pattern.
225
+
226
+ ## Dependency Rule
227
+
228
+ \`\`\`
229
+ Handler → Service → Repository → Model
230
+ ↑ ↑
231
+ └── Presenter ──────────┘
232
+ \`\`\`
233
+
234
+ Dependencies flow **downward only**. Upper layers call lower layers; lower layers never call up.
235
+
236
+ ## Layer Responsibilities
237
+
238
+ | Layer | Directory | Responsibility |
239
+ |-------|-----------|----------------|
240
+ | Handlers | \`${srcDir}/handlers/\` | HTTP entry points; input validation; auth |
241
+ | Services | \`${srcDir}/services/\` | Business logic; one operation per class |
242
+ | Repositories | \`${srcDir}/repositories/\` | Data access; abstract over DB/cache |
243
+ | Models | \`${srcDir}/models/\` | Domain objects; no HTTP or DB concerns |
244
+ | Presenters | \`${srcDir}/presenters/\` | Serializers; Model → JSON/XML/DTO |
245
+
246
+ ## Rules
247
+
248
+ 1. **Handlers** never call Repositories directly — always through Services
249
+ 2. **Services** never know about HTTP (status codes, request objects)
250
+ 3. **Repositories** never throw HTTP-specific exceptions
251
+ 4. **Models** are pure domain objects — no framework imports
252
+ 5. **Dependency Injection** is used at every layer boundary
253
+
254
+ ## CI Validation
255
+
256
+ Run \`dare metrics collect\` to check M-02 (0% Handler→Repository violations).
257
+
258
+ ---
259
+ *Generated by dare-layered-design v1.0.0 — DARE Method*
260
+ `;
261
+ }
262
+
263
+ // ── Example file generators ──────────────────────────────────────────────────
264
+
265
+ function generateHandlerExample(entity: string, ext: string): string {
266
+ const Name = capitalize(toCamelCase(entity));
267
+ const snake = toSnakeCase(entity);
268
+
269
+ if (ext === '.ts') {
270
+ return `/**
271
+ * ${Name}Handler — HTTP handler for ${entity} resource
272
+ * Responsibility: receive requests, validate input, call service, return response
273
+ *
274
+ * RULE: Never call ${Name}Repository directly — always through ${Name}Service
275
+ */
276
+ import { Request, Response } from 'express';
277
+
278
+ // Services injected — never instantiated here
279
+ interface ${Name}HandlerDeps {
280
+ create${Name}Service: Create${Name}Service;
281
+ }
282
+
283
+ interface Create${Name}Service {
284
+ execute(input: Create${Name}Input): Promise<${Name}>;
285
+ }
286
+
287
+ interface Create${Name}Input {
288
+ name: string;
289
+ email: string;
290
+ }
291
+
292
+ interface ${Name} {
293
+ id: string;
294
+ name: string;
295
+ email: string;
296
+ createdAt: Date;
297
+ }
298
+
299
+ export class ${Name}Handler {
300
+ constructor(private readonly deps: ${Name}HandlerDeps) {}
301
+
302
+ async create(req: Request, res: Response): Promise<void> {
303
+ // Input validation (HTTP layer responsibility)
304
+ const { name, email } = req.body as Record<string, string>;
305
+ if (!name || !email) {
306
+ res.status(422).json({
307
+ type: '/errors/validation',
308
+ title: 'Validation error',
309
+ status: 422,
310
+ detail: 'name and email are required',
311
+ });
312
+ return;
313
+ }
314
+
315
+ // Delegate to service (business logic)
316
+ const result = await this.deps.create${Name}Service.execute({ name, email });
317
+
318
+ // Return response (HTTP layer responsibility)
319
+ res.status(201).json(result);
320
+ }
321
+ }
322
+ `;
323
+ }
324
+
325
+ if (ext === '.rb') {
326
+ return `# ${Name}Controller — HTTP controller for ${snake} resource
327
+ # Responsibility: receive requests, validate input, call service, return response
328
+ #
329
+ # RULE: Never call ${Name}Repository directly — always through ${Name}Service
330
+ class ${Name}Controller < ApplicationController
331
+ # Services injected via constructor — never instantiated here
332
+ def initialize(create_${snake}_service:)
333
+ @create_${snake}_service = create_${snake}_service
334
+ end
335
+
336
+ def create
337
+ result = @create_${snake}_service.call(
338
+ name: params.require(:name),
339
+ email: params.require(:email)
340
+ )
341
+ render json: result, status: :created
342
+ rescue ActionController::ParameterMissing => e
343
+ render json: { error: e.message }, status: :unprocessable_entity
344
+ end
345
+ end
346
+ `;
347
+ }
348
+
349
+ return `# ${Name} handler — see README.md for rules\n# Language: ${ext}\n`;
350
+ }
351
+
352
+ function generateServiceExample(entity: string, ext: string): string {
353
+ const Name = capitalize(toCamelCase(entity));
354
+ const snake = toSnakeCase(entity);
355
+
356
+ if (ext === '.ts') {
357
+ return `/**
358
+ * Create${Name}Service — creates a new ${entity}
359
+ * Responsibility: business logic for ${entity} creation
360
+ *
361
+ * RULES:
362
+ * - No HTTP imports (no Request, Response, status codes)
363
+ * - Receive ${Name}Repository via dependency injection
364
+ * - Return ${Name} domain model — not HTTP DTO
365
+ */
366
+
367
+ export interface ${Name}Repository {
368
+ findByEmail(email: string): Promise<${Name} | null>;
369
+ save(${snake}: ${Name}): Promise<${Name}>;
370
+ }
371
+
372
+ export interface ${Name} {
373
+ id: string;
374
+ name: string;
375
+ email: string;
376
+ createdAt: Date;
377
+ }
378
+
379
+ export interface Create${Name}Input {
380
+ name: string;
381
+ email: string;
382
+ }
383
+
384
+ export class Create${Name}Service {
385
+ constructor(private readonly ${snake}Repository: ${Name}Repository) {}
386
+
387
+ async execute(input: Create${Name}Input): Promise<${Name}> {
388
+ // Business rule validation (not HTTP validation)
389
+ if (!input.email.includes('@')) {
390
+ throw new Error('Invalid email format');
391
+ }
392
+
393
+ // Check uniqueness (business rule)
394
+ const existing = await this.${snake}Repository.findByEmail(input.email);
395
+ if (existing) {
396
+ throw new Error('${Name} with this email already exists');
397
+ }
398
+
399
+ // Create and save domain object
400
+ const ${snake}: ${Name} = {
401
+ id: crypto.randomUUID(),
402
+ name: input.name,
403
+ email: input.email,
404
+ createdAt: new Date(),
405
+ };
406
+
407
+ return this.${snake}Repository.save(${snake});
408
+ }
409
+ }
410
+ `;
411
+ }
412
+
413
+ if (ext === '.rb') {
414
+ return `# Create${Name}Service — creates a new ${snake}
415
+ # Responsibility: business logic for ${snake} creation
416
+ #
417
+ # RULES:
418
+ # - No HTTP concerns (no status codes, request objects)
419
+ # - Receive ${Name}Repository via constructor injection
420
+ class Create${Name}Service
421
+ def initialize(${snake}_repository:)
422
+ @${snake}_repository = ${snake}_repository
423
+ end
424
+
425
+ def call(name:, email:)
426
+ raise ArgumentError, "Invalid email" unless email.include?("@")
427
+ raise "${Name}AlreadyExists" if @${snake}_repository.find_by_email(email)
428
+
429
+ @${snake}_repository.save(
430
+ id: SecureRandom.uuid,
431
+ name: name,
432
+ email: email,
433
+ created_at: Time.now
434
+ )
435
+ end
436
+ end
437
+ `;
438
+ }
439
+
440
+ return `# Create${Name} service — see README.md for rules\n# Language: ${ext}\n`;
441
+ }
442
+
443
+ function generateRepositoryExample(entity: string, ext: string): string {
444
+ const Name = capitalize(toCamelCase(entity));
445
+ const snake = toSnakeCase(entity);
446
+
447
+ if (ext === '.ts') {
448
+ return `/**
449
+ * ${Name}Repository — interface and implementations for ${entity} data access
450
+ * Responsibility: abstract over storage; multiple implementations
451
+ *
452
+ * RULES:
453
+ * - Never throw HTTP-specific exceptions (no 404 — return null)
454
+ * - Parameterized queries only (no raw string SQL concatenation)
455
+ * - InMemory implementation for unit tests
456
+ */
457
+
458
+ export interface ${Name} {
459
+ id: string;
460
+ name: string;
461
+ email: string;
462
+ createdAt: Date;
463
+ }
464
+
465
+ // Repository interface (contract)
466
+ export interface ${Name}Repository {
467
+ findById(id: string): Promise<${Name} | null>;
468
+ findByEmail(email: string): Promise<${Name} | null>;
469
+ save(${snake}: ${Name}): Promise<${Name}>;
470
+ delete(id: string): Promise<void>;
471
+ }
472
+
473
+ // In-memory implementation (for unit tests)
474
+ export class InMemory${Name}Repository implements ${Name}Repository {
475
+ private readonly store = new Map<string, ${Name}>();
476
+
477
+ async findById(id: string): Promise<${Name} | null> {
478
+ return this.store.get(id) ?? null;
479
+ }
480
+
481
+ async findByEmail(email: string): Promise<${Name} | null> {
482
+ for (const ${snake} of this.store.values()) {
483
+ if (${snake}.email === email) return ${snake};
484
+ }
485
+ return null;
486
+ }
487
+
488
+ async save(${snake}: ${Name}): Promise<${Name}> {
489
+ this.store.set(${snake}.id, ${snake});
490
+ return ${snake};
491
+ }
492
+
493
+ async delete(id: string): Promise<void> {
494
+ this.store.delete(id);
495
+ }
496
+ }
497
+
498
+ // Database implementation stub
499
+ export class Postgres${Name}Repository implements ${Name}Repository {
500
+ // TODO: inject db client via constructor
501
+ async findById(_id: string): Promise<${Name} | null> {
502
+ throw new Error('Not implemented — add your DB client');
503
+ }
504
+
505
+ async findByEmail(_email: string): Promise<${Name} | null> {
506
+ throw new Error('Not implemented — add your DB client');
507
+ }
508
+
509
+ async save(_${snake}: ${Name}): Promise<${Name}> {
510
+ throw new Error('Not implemented — add your DB client');
511
+ }
512
+
513
+ async delete(_id: string): Promise<void> {
514
+ throw new Error('Not implemented — add your DB client');
515
+ }
516
+ }
517
+ `;
518
+ }
519
+
520
+ if (ext === '.rb') {
521
+ return `# ${Name}Repository — interface and implementations for ${snake} data access
522
+ # RULES:
523
+ # - Never raise HTTP-specific errors (no Not Found with status code)
524
+ # - Return nil for missing records, not exceptions
525
+ module ${Name}Repository
526
+ def find_by_id(id)
527
+ raise NotImplementedError
528
+ end
529
+
530
+ def find_by_email(email)
531
+ raise NotImplementedError
532
+ end
533
+
534
+ def save(${snake})
535
+ raise NotImplementedError
536
+ end
537
+ end
538
+
539
+ # In-memory implementation (for unit tests)
540
+ class InMemory${Name}Repository
541
+ include ${Name}Repository
542
+
543
+ def initialize
544
+ @store = {}
545
+ end
546
+
547
+ def find_by_id(id)
548
+ @store[id]
549
+ end
550
+
551
+ def find_by_email(email)
552
+ @store.values.find { |u| u[:email] == email }
553
+ end
554
+
555
+ def save(${snake})
556
+ @store[${snake}[:id]] = ${snake}
557
+ end
558
+ end
559
+ `;
560
+ }
561
+
562
+ return `# ${Name}Repository — see README.md for rules\n# Language: ${ext}\n`;
563
+ }
564
+
565
+ function generateModelExample(entity: string, ext: string): string {
566
+ const Name = capitalize(toCamelCase(entity));
567
+
568
+ if (ext === '.ts') {
569
+ return `/**
570
+ * ${Name} — domain model
571
+ * Responsibility: represent the ${entity} entity with its business rules
572
+ *
573
+ * RULES:
574
+ * - No HTTP imports (no Request, Response)
575
+ * - No database imports (no ORM, no query builders)
576
+ * - No framework imports
577
+ * - Pure TypeScript data structure + business methods
578
+ */
579
+
580
+ export interface ${Name} {
581
+ readonly id: string;
582
+ readonly name: string;
583
+ readonly email: string;
584
+ readonly createdAt: Date;
585
+ }
586
+
587
+ // Factory function for creating new instances
588
+ export function create${Name}(params: Omit<${Name}, 'createdAt'>): ${Name} {
589
+ if (!params.email.includes('@')) {
590
+ throw new Error('${Name}: invalid email format');
591
+ }
592
+ if (!params.name.trim()) {
593
+ throw new Error('${Name}: name cannot be empty');
594
+ }
595
+ return {
596
+ ...params,
597
+ createdAt: new Date(),
598
+ };
599
+ }
600
+
601
+ // Business rule: computed property
602
+ export function ${toCamelCase(entity)}DisplayName(${toCamelCase(entity)}: ${Name}): string {
603
+ return \`\${${toCamelCase(entity)}.name} <\${${toCamelCase(entity)}.email}>\`;
604
+ }
605
+ `;
606
+ }
607
+
608
+ if (ext === '.rb') {
609
+ return `# ${Name} — domain model
610
+ # RULES:
611
+ # - No ActiveRecord concerns — pure Ruby
612
+ # - No HTTP imports
613
+ # - Business rules only (not validation framework)
614
+ ${Name} = Struct.new(:id, :name, :email, :created_at, keyword_init: true) do
615
+ def display_name
616
+ "\#{name} <\#{email}>"
617
+ end
618
+
619
+ def valid_email?
620
+ email.include?("@")
621
+ end
622
+ end
623
+ `;
624
+ }
625
+
626
+ return `# ${Name} model — see README.md for rules\n# Language: ${ext}\n`;
627
+ }
628
+
629
+ function generatePresenterExample(entity: string, ext: string): string {
630
+ const Name = capitalize(toCamelCase(entity));
631
+ const snake = toSnakeCase(entity);
632
+
633
+ if (ext === '.ts') {
634
+ return `/**
635
+ * ${Name}Presenter — serializes ${Name} domain model to JSON
636
+ * Responsibility: convert ${entity} to response format (no business logic)
637
+ *
638
+ * RULES:
639
+ * - No business logic — only formatting and field mapping
640
+ * - Called from Handlers only (not Services, not Repositories)
641
+ */
642
+
643
+ export interface ${Name} {
644
+ id: string;
645
+ name: string;
646
+ email: string;
647
+ createdAt: Date;
648
+ }
649
+
650
+ export interface ${Name}JSON {
651
+ id: string;
652
+ name: string;
653
+ email: string;
654
+ created_at: string; // ISO 8601
655
+ }
656
+
657
+ export class ${Name}Presenter {
658
+ static toJSON(${snake}: ${Name}): ${Name}JSON {
659
+ return {
660
+ id: ${snake}.id,
661
+ name: ${snake}.name,
662
+ email: ${snake}.email,
663
+ created_at: ${snake}.createdAt.toISOString(),
664
+ };
665
+ }
666
+
667
+ static toJSONList(${snake}s: ${Name}[]): ${Name}JSON[] {
668
+ return ${snake}s.map((u) => this.toJSON(u));
669
+ }
670
+ }
671
+ `;
672
+ }
673
+
674
+ if (ext === '.rb') {
675
+ return `# ${Name}Presenter — serializes ${snake} domain model to JSON
676
+ # RULES:
677
+ # - No business logic — only formatting
678
+ # - Called from controllers/handlers only
679
+ class ${Name}Presenter
680
+ def initialize(${snake})
681
+ @${snake} = ${snake}
682
+ end
683
+
684
+ def as_json(*)
685
+ {
686
+ id: @${snake}.id,
687
+ name: @${snake}.name,
688
+ email: @${snake}.email,
689
+ created_at: @${snake}.created_at.iso8601
690
+ }
691
+ end
692
+ end
693
+ `;
694
+ }
695
+
696
+ return `# ${Name}Presenter — see README.md for rules\n# Language: ${ext}\n`;
697
+ }
698
+
699
+ // ── String utilities ─────────────────────────────────────────────────────────
700
+
701
+ function capitalize(str: string): string {
702
+ return str.charAt(0).toUpperCase() + str.slice(1);
703
+ }
704
+
705
+ function toCamelCase(str: string): string {
706
+ return str
707
+ .replace(/[-_\s]+(.)/g, (_, c: string) => c.toUpperCase())
708
+ .replace(/^(.)/, (c: string) => c.toLowerCase());
709
+ }
710
+
711
+ function toSnakeCase(str: string): string {
712
+ return str
713
+ .replace(/([A-Z])/g, '_$1')
714
+ .replace(/[-\s]+/g, '_')
715
+ .toLowerCase()
716
+ .replace(/^_/, '');
717
+ }
718
+
719
+ function languageToExtension(language: Language): string {
720
+ const map: Record<Language, string> = {
721
+ typescript: '.ts',
722
+ javascript: '.js',
723
+ ruby: '.rb',
724
+ rust: '.rs',
725
+ python: '.py',
726
+ go: '.go',
727
+ php: '.php',
728
+ unknown: '.ts',
729
+ };
730
+ return map[language] ?? '.ts';
731
+ }
732
+
733
+ function hasNonReadmeFiles(dir: string): boolean {
734
+ try {
735
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
736
+ return entries.some((e) => e.isFile() && e.name !== 'README.md');
737
+ } catch {
738
+ return false;
739
+ }
740
+ }