@dewtech/dare-cli 3.11.0 → 3.13.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 (316) hide show
  1. package/README.md +2 -0
  2. package/dist/__tests__/cli-only-invariants.test.d.ts +2 -0
  3. package/dist/__tests__/cli-only-invariants.test.d.ts.map +1 -0
  4. package/dist/__tests__/cli-only-invariants.test.js +100 -0
  5. package/dist/__tests__/cli-only-invariants.test.js.map +1 -0
  6. package/dist/__tests__/cli-only-regression.test.d.ts +2 -0
  7. package/dist/__tests__/cli-only-regression.test.d.ts.map +1 -0
  8. package/dist/__tests__/cli-only-regression.test.js +29 -0
  9. package/dist/__tests__/cli-only-regression.test.js.map +1 -0
  10. package/dist/__tests__/ensure-skills.test.js +5 -0
  11. package/dist/__tests__/ensure-skills.test.js.map +1 -1
  12. package/dist/__tests__/ide-command-parity.test.js +1 -0
  13. package/dist/__tests__/ide-command-parity.test.js.map +1 -1
  14. package/dist/__tests__/project-generator.test.js +17 -0
  15. package/dist/__tests__/project-generator.test.js.map +1 -1
  16. package/dist/__tests__/reverse-facts.test.js +1 -0
  17. package/dist/__tests__/reverse-facts.test.js.map +1 -1
  18. package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
  19. package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
  20. package/dist/__tests__/terminal-parity-regression.test.js +116 -0
  21. package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
  22. package/dist/__tests__/terminal-parity.test.d.ts +2 -0
  23. package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
  24. package/dist/__tests__/terminal-parity.test.js +81 -0
  25. package/dist/__tests__/terminal-parity.test.js.map +1 -0
  26. package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
  27. package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
  28. package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
  29. package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
  30. package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
  31. package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
  32. package/dist/agent/__tests__/codex-driver.test.js +68 -0
  33. package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
  34. package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
  35. package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
  36. package/dist/agent/__tests__/cursor-driver.test.js +52 -0
  37. package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
  38. package/dist/agent/driver.d.ts +1 -1
  39. package/dist/agent/driver.d.ts.map +1 -1
  40. package/dist/agent/drivers/antigravity.d.ts +8 -0
  41. package/dist/agent/drivers/antigravity.d.ts.map +1 -0
  42. package/dist/agent/drivers/antigravity.js +99 -0
  43. package/dist/agent/drivers/antigravity.js.map +1 -0
  44. package/dist/agent/drivers/codex.d.ts +12 -0
  45. package/dist/agent/drivers/codex.d.ts.map +1 -0
  46. package/dist/agent/drivers/codex.js +137 -0
  47. package/dist/agent/drivers/codex.js.map +1 -0
  48. package/dist/agent/drivers/cursor.d.ts +8 -0
  49. package/dist/agent/drivers/cursor.d.ts.map +1 -0
  50. package/dist/agent/drivers/cursor.js +99 -0
  51. package/dist/agent/drivers/cursor.js.map +1 -0
  52. package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
  53. package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
  54. package/dist/ai/__tests__/ai-core.test.js +41 -0
  55. package/dist/ai/__tests__/ai-core.test.js.map +1 -0
  56. package/dist/ai/__tests__/parity.test.d.ts +2 -0
  57. package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
  58. package/dist/ai/__tests__/parity.test.js +36 -0
  59. package/dist/ai/__tests__/parity.test.js.map +1 -0
  60. package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
  61. package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
  62. package/dist/ai/__tests__/pipeline.test.js +147 -0
  63. package/dist/ai/__tests__/pipeline.test.js.map +1 -0
  64. package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
  65. package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
  66. package/dist/ai/__tests__/refine-bridge.test.js +17 -0
  67. package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
  68. package/dist/ai/__tests__/resolve.test.d.ts +2 -0
  69. package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
  70. package/dist/ai/__tests__/resolve.test.js +42 -0
  71. package/dist/ai/__tests__/resolve.test.js.map +1 -0
  72. package/dist/ai/capabilities.d.ts +3 -0
  73. package/dist/ai/capabilities.d.ts.map +1 -0
  74. package/dist/ai/capabilities.js +11 -0
  75. package/dist/ai/capabilities.js.map +1 -0
  76. package/dist/ai/command-options.d.ts +10 -0
  77. package/dist/ai/command-options.d.ts.map +1 -0
  78. package/dist/ai/command-options.js +15 -0
  79. package/dist/ai/command-options.js.map +1 -0
  80. package/dist/ai/config.d.ts +27 -0
  81. package/dist/ai/config.d.ts.map +1 -0
  82. package/dist/ai/config.js +89 -0
  83. package/dist/ai/config.js.map +1 -0
  84. package/dist/ai/parity.d.ts +13 -0
  85. package/dist/ai/parity.d.ts.map +1 -0
  86. package/dist/ai/parity.js +87 -0
  87. package/dist/ai/parity.js.map +1 -0
  88. package/dist/ai/parse-json-output.d.ts +5 -0
  89. package/dist/ai/parse-json-output.d.ts.map +1 -0
  90. package/dist/ai/parse-json-output.js +25 -0
  91. package/dist/ai/parse-json-output.js.map +1 -0
  92. package/dist/ai/pipeline.d.ts +20 -0
  93. package/dist/ai/pipeline.d.ts.map +1 -0
  94. package/dist/ai/pipeline.js +303 -0
  95. package/dist/ai/pipeline.js.map +1 -0
  96. package/dist/ai/prompts.d.ts +6 -0
  97. package/dist/ai/prompts.d.ts.map +1 -0
  98. package/dist/ai/prompts.js +49 -0
  99. package/dist/ai/prompts.js.map +1 -0
  100. package/dist/ai/providers.d.ts +63 -0
  101. package/dist/ai/providers.d.ts.map +1 -0
  102. package/dist/ai/providers.js +297 -0
  103. package/dist/ai/providers.js.map +1 -0
  104. package/dist/ai/refine-bridge.d.ts +5 -0
  105. package/dist/ai/refine-bridge.d.ts.map +1 -0
  106. package/dist/ai/refine-bridge.js +14 -0
  107. package/dist/ai/refine-bridge.js.map +1 -0
  108. package/dist/ai/registry.d.ts +12 -0
  109. package/dist/ai/registry.d.ts.map +1 -0
  110. package/dist/ai/registry.js +43 -0
  111. package/dist/ai/registry.js.map +1 -0
  112. package/dist/ai/resolve.d.ts +28 -0
  113. package/dist/ai/resolve.d.ts.map +1 -0
  114. package/dist/ai/resolve.js +83 -0
  115. package/dist/ai/resolve.js.map +1 -0
  116. package/dist/ai/schemas.d.ts +175 -0
  117. package/dist/ai/schemas.d.ts.map +1 -0
  118. package/dist/ai/schemas.js +199 -0
  119. package/dist/ai/schemas.js.map +1 -0
  120. package/dist/ai/types.d.ts +52 -0
  121. package/dist/ai/types.d.ts.map +1 -0
  122. package/dist/ai/types.js +8 -0
  123. package/dist/ai/types.js.map +1 -0
  124. package/dist/bin/dare.js +2 -0
  125. package/dist/bin/dare.js.map +1 -1
  126. package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
  127. package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
  128. package/dist/commands/__tests__/ai-command.test.js +68 -0
  129. package/dist/commands/__tests__/ai-command.test.js.map +1 -0
  130. package/dist/commands/__tests__/execute-agent.test.js +82 -0
  131. package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
  132. package/dist/commands/ai.d.ts +3 -0
  133. package/dist/commands/ai.d.ts.map +1 -0
  134. package/dist/commands/ai.js +141 -0
  135. package/dist/commands/ai.js.map +1 -0
  136. package/dist/commands/blueprint.d.ts.map +1 -1
  137. package/dist/commands/blueprint.js +17 -3
  138. package/dist/commands/blueprint.js.map +1 -1
  139. package/dist/commands/design.d.ts.map +1 -1
  140. package/dist/commands/design.js +21 -2
  141. package/dist/commands/design.js.map +1 -1
  142. package/dist/commands/discover.d.ts.map +1 -1
  143. package/dist/commands/discover.js +9 -1
  144. package/dist/commands/discover.js.map +1 -1
  145. package/dist/commands/dna.d.ts.map +1 -1
  146. package/dist/commands/dna.js +23 -3
  147. package/dist/commands/dna.js.map +1 -1
  148. package/dist/commands/execute.d.ts +11 -0
  149. package/dist/commands/execute.d.ts.map +1 -1
  150. package/dist/commands/execute.js +111 -4
  151. package/dist/commands/execute.js.map +1 -1
  152. package/dist/commands/init.d.ts.map +1 -1
  153. package/dist/commands/init.js +1 -0
  154. package/dist/commands/init.js.map +1 -1
  155. package/dist/commands/migrate.d.ts.map +1 -1
  156. package/dist/commands/migrate.js +14 -2
  157. package/dist/commands/migrate.js.map +1 -1
  158. package/dist/commands/patterns.d.ts.map +1 -1
  159. package/dist/commands/patterns.js +14 -2
  160. package/dist/commands/patterns.js.map +1 -1
  161. package/dist/commands/refine.d.ts.map +1 -1
  162. package/dist/commands/refine.js +23 -2
  163. package/dist/commands/refine.js.map +1 -1
  164. package/dist/commands/reverse.d.ts.map +1 -1
  165. package/dist/commands/reverse.js +28 -3
  166. package/dist/commands/reverse.js.map +1 -1
  167. package/dist/commands/review.d.ts.map +1 -1
  168. package/dist/commands/review.js +25 -3
  169. package/dist/commands/review.js.map +1 -1
  170. package/dist/core/types/project.d.ts +1 -1
  171. package/dist/core/types/project.d.ts.map +1 -1
  172. package/dist/dag-runner/run_dag.d.ts +1 -1
  173. package/dist/dag-runner/run_dag.d.ts.map +1 -1
  174. package/dist/exec/safe-spawn.d.ts.map +1 -1
  175. package/dist/exec/safe-spawn.js +6 -1
  176. package/dist/exec/safe-spawn.js.map +1 -1
  177. package/dist/skills/bundled.d.ts +5 -0
  178. package/dist/skills/bundled.d.ts.map +1 -0
  179. package/dist/skills/bundled.js +34 -0
  180. package/dist/skills/bundled.js.map +1 -0
  181. package/dist/skills/commands/add.d.ts +1 -3
  182. package/dist/skills/commands/add.d.ts.map +1 -1
  183. package/dist/skills/commands/add.js +20 -3
  184. package/dist/skills/commands/add.js.map +1 -1
  185. package/dist/skills/tests/bundled.spec.d.ts +2 -0
  186. package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
  187. package/dist/skills/tests/bundled.spec.js +24 -0
  188. package/dist/skills/tests/bundled.spec.js.map +1 -0
  189. package/dist/types/UpdateManifest.types.d.ts +1 -1
  190. package/dist/types/UpdateManifest.types.d.ts.map +1 -1
  191. package/dist/utils/dag-converter.js +1 -1
  192. package/dist/utils/dag-converter.js.map +1 -1
  193. package/dist/utils/project-detector.d.ts +1 -0
  194. package/dist/utils/project-detector.d.ts.map +1 -1
  195. package/dist/utils/project-detector.js +8 -0
  196. package/dist/utils/project-detector.js.map +1 -1
  197. package/dist/utils/project-generator.d.ts +1 -1
  198. package/dist/utils/project-generator.d.ts.map +1 -1
  199. package/dist/utils/project-generator.js +23 -2
  200. package/dist/utils/project-generator.js.map +1 -1
  201. package/dist/utils/templates.d.ts +2 -0
  202. package/dist/utils/templates.d.ts.map +1 -1
  203. package/dist/utils/templates.js +74 -0
  204. package/dist/utils/templates.js.map +1 -1
  205. package/dist/verification/__tests__/safe-spawn.test.js +12 -0
  206. package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
  207. package/package.json +2 -1
  208. package/skills/dare-ax/generator.ts +325 -0
  209. package/skills/dare-ax/index.ts +19 -0
  210. package/skills/dare-ax/metrics.ts +352 -0
  211. package/skills/dare-ax/package-lock.json +1855 -0
  212. package/skills/dare-ax/package.json +50 -0
  213. package/skills/dare-ax/secret-detector.ts +123 -0
  214. package/skills/dare-ax/skill.yml +19 -0
  215. package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
  216. package/skills/dare-ax/tests/generator.spec.ts +193 -0
  217. package/skills/dare-ax/tests/metrics.spec.ts +394 -0
  218. package/skills/dare-ax/tests/validator.spec.ts +298 -0
  219. package/skills/dare-ax/tsconfig.json +18 -0
  220. package/skills/dare-ax/types.ts +79 -0
  221. package/skills/dare-ax/validator.ts +238 -0
  222. package/skills/dare-frontend-design/generator.ts +616 -0
  223. package/skills/dare-frontend-design/index.ts +25 -0
  224. package/skills/dare-frontend-design/linter.ts +227 -0
  225. package/skills/dare-frontend-design/metrics.ts +82 -0
  226. package/skills/dare-frontend-design/package-lock.json +1855 -0
  227. package/skills/dare-frontend-design/package.json +43 -0
  228. package/skills/dare-frontend-design/skill.yml +20 -0
  229. package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
  230. package/skills/dare-frontend-design/tsconfig.json +18 -0
  231. package/skills/dare-frontend-design/types.ts +62 -0
  232. package/skills/dare-layered-design/generator.ts +740 -0
  233. package/skills/dare-layered-design/index.ts +17 -0
  234. package/skills/dare-layered-design/linter.ts +462 -0
  235. package/skills/dare-layered-design/metrics.ts +409 -0
  236. package/skills/dare-layered-design/package-lock.json +1855 -0
  237. package/skills/dare-layered-design/package.json +50 -0
  238. package/skills/dare-layered-design/skill.yml +35 -0
  239. package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
  240. package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
  241. package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
  242. package/skills/dare-layered-design/tsconfig.json +18 -0
  243. package/skills/dare-layered-design/types.ts +48 -0
  244. package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
  245. package/skills/dare-llm-integration/index.ts +49 -0
  246. package/skills/dare-llm-integration/metrics.ts +107 -0
  247. package/skills/dare-llm-integration/package-lock.json +1855 -0
  248. package/skills/dare-llm-integration/package.json +49 -0
  249. package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
  250. package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
  251. package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
  252. package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
  253. package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
  254. package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
  255. package/skills/dare-llm-integration/skill.yml +23 -0
  256. package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
  257. package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
  258. package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
  259. package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
  260. package/skills/dare-llm-integration/tsconfig.json +23 -0
  261. package/skills/dare-llm-integration/types.ts +91 -0
  262. package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
  263. package/skills/dare-quality-telemetry/collect.ts +134 -0
  264. package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
  265. package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
  266. package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
  267. package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
  268. package/skills/dare-quality-telemetry/index.ts +18 -0
  269. package/skills/dare-quality-telemetry/metrics.ts +137 -0
  270. package/skills/dare-quality-telemetry/package-lock.json +1855 -0
  271. package/skills/dare-quality-telemetry/package.json +48 -0
  272. package/skills/dare-quality-telemetry/regression.ts +60 -0
  273. package/skills/dare-quality-telemetry/reporter.ts +132 -0
  274. package/skills/dare-quality-telemetry/skill.yml +18 -0
  275. package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
  276. package/skills/dare-quality-telemetry/tsconfig.json +19 -0
  277. package/skills/dare-quality-telemetry/types.ts +41 -0
  278. package/skills/dare-realtime/event_registry.ts +101 -0
  279. package/skills/dare-realtime/index.ts +30 -0
  280. package/skills/dare-realtime/metrics.ts +84 -0
  281. package/skills/dare-realtime/package-lock.json +1855 -0
  282. package/skills/dare-realtime/package.json +43 -0
  283. package/skills/dare-realtime/reconnect_strategy.ts +85 -0
  284. package/skills/dare-realtime/schema_validator.ts +80 -0
  285. package/skills/dare-realtime/skill.yml +21 -0
  286. package/skills/dare-realtime/subscription_manager.ts +106 -0
  287. package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
  288. package/skills/dare-realtime/tsconfig.json +18 -0
  289. package/skills/dare-realtime/types.ts +51 -0
  290. package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
  291. package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
  292. package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
  293. package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
  294. package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
  295. package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
  296. package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
  297. package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
  298. package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
  299. package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
  300. package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
  301. package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
  302. package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
  303. package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
  304. package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
  305. package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
  306. package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
  307. package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
  308. package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
  309. package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
  310. package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
  311. package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
  312. package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
  313. package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
  314. package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
  315. package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
  316. package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@dewtech/dare-layered-design",
3
+ "version": "1.0.0",
4
+ "description": "dare-layered-design — 4-layer architecture skill for DARE Method v3.0",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "templates",
17
+ "skill.yml"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "test": "vitest run",
22
+ "test:watch": "vitest",
23
+ "typecheck": "tsc --noEmit"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.19.41",
27
+ "typescript": "^5.9.3",
28
+ "vitest": "^1.6.1"
29
+ },
30
+ "keywords": [
31
+ "dare",
32
+ "dare-method",
33
+ "dare-layered-design",
34
+ "layered-architecture",
35
+ "clean-architecture",
36
+ "handlers",
37
+ "services",
38
+ "repositories"
39
+ ],
40
+ "author": "Dewtech Technologies",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/dewtech-technologies/dare-method.git",
45
+ "directory": "packages/skills/dare-layered-design"
46
+ },
47
+ "engines": {
48
+ "node": ">=18.0.0"
49
+ }
50
+ }
@@ -0,0 +1,35 @@
1
+ name: dare-layered-design
2
+ version: 1.0.0
3
+ description: >
4
+ Layered Design skill — enforces a strict 4-layer architecture (Handlers → Services →
5
+ Repositories → Models) across all DARE projects, regardless of language or framework.
6
+ Inspired by Vladimir Dementyev's "Layered Design for Ruby on Rails Applications" and
7
+ Evil Martians methodology.
8
+ author: Dewtech Technologies
9
+ license: MIT
10
+ dependencies: {}
11
+ metrics:
12
+ - id: M-01
13
+ description: 100% of Services have unit tests (no real DB or HTTP)
14
+ - id: M-02
15
+ description: 0% Handler→Repository direct calls in codebase
16
+ - id: M-03
17
+ description: 100% of Handlers use dependency injection (no new Service() inside)
18
+ - id: M-04
19
+ description: 100% of Repositories are agnostic to upper layers (no HTTP status codes returned)
20
+ layers:
21
+ - name: handlers
22
+ description: HTTP/gRPC entry points — no business logic
23
+ aliases: [controllers, routers, routes]
24
+ - name: services
25
+ description: Business logic — one operation per class/module
26
+ aliases: [use_cases, interactors, commands]
27
+ - name: repositories
28
+ description: Data access — abstractions over DB/cache/external APIs
29
+ aliases: [repos, data_access, stores]
30
+ - name: models
31
+ description: Domain objects — no HTTP or DB concerns
32
+ aliases: [entities, domain, structs]
33
+ - name: presenters
34
+ description: Serializers — converts Models to JSON/XML/etc
35
+ aliases: [serializers, formatters, views]
@@ -0,0 +1,156 @@
1
+ /**
2
+ * dare-layered-design — generator tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import fs from 'fs';
7
+ import os from 'os';
8
+ import path from 'path';
9
+ import { LayeredDesignGenerator } from '../generator.js';
10
+
11
+ describe('LayeredDesignGenerator', () => {
12
+ let tmpDir: string;
13
+ let generator: LayeredDesignGenerator;
14
+
15
+ beforeEach(() => {
16
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dare-scaffold-test-'));
17
+ generator = new LayeredDesignGenerator();
18
+ });
19
+
20
+ afterEach(() => {
21
+ fs.rmSync(tmpDir, { recursive: true, force: true });
22
+ });
23
+
24
+ describe('scaffold()', () => {
25
+ it('creates all 5 layer directories', () => {
26
+ const result = generator.scaffold(tmpDir);
27
+
28
+ const expectedDirs = ['handlers', 'services', 'repositories', 'models', 'presenters'];
29
+ for (const dir of expectedDirs) {
30
+ const fullPath = path.join(tmpDir, 'src', dir);
31
+ expect(fs.existsSync(fullPath), `Expected ${dir} to exist`).toBe(true);
32
+ expect(result.createdDirs).toContain(fullPath);
33
+ }
34
+ });
35
+
36
+ it('creates README.md in each layer directory', () => {
37
+ generator.scaffold(tmpDir);
38
+
39
+ const layers = ['handlers', 'services', 'repositories', 'models', 'presenters'];
40
+ for (const layer of layers) {
41
+ const readmePath = path.join(tmpDir, 'src', layer, 'README.md');
42
+ expect(fs.existsSync(readmePath), `Expected README.md in ${layer}`).toBe(true);
43
+ }
44
+ });
45
+
46
+ it('creates .gitkeep in each layer when withExamples=false', () => {
47
+ generator.scaffold(tmpDir, { withExamples: false });
48
+
49
+ const layers = ['handlers', 'services', 'repositories', 'models', 'presenters'];
50
+ for (const layer of layers) {
51
+ const gitkeepPath = path.join(tmpDir, 'src', layer, '.gitkeep');
52
+ expect(fs.existsSync(gitkeepPath), `Expected .gitkeep in ${layer}`).toBe(true);
53
+ }
54
+ });
55
+
56
+ it('uses custom srcDir', () => {
57
+ generator.scaffold(tmpDir, { srcDir: 'app' });
58
+
59
+ expect(fs.existsSync(path.join(tmpDir, 'app', 'handlers'))).toBe(true);
60
+ expect(fs.existsSync(path.join(tmpDir, 'app', 'services'))).toBe(true);
61
+ expect(fs.existsSync(path.join(tmpDir, 'src'))).toBe(false);
62
+ });
63
+
64
+ it('creates ARCHITECTURE.md in srcDir', () => {
65
+ generator.scaffold(tmpDir);
66
+
67
+ const archPath = path.join(tmpDir, 'src', 'ARCHITECTURE.md');
68
+ expect(fs.existsSync(archPath)).toBe(true);
69
+ const content = fs.readFileSync(archPath, 'utf-8');
70
+ expect(content).toContain('Handler → Service → Repository → Model');
71
+ });
72
+
73
+ it('creates TypeScript example files when withExamples=true', () => {
74
+ generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'typescript' });
75
+
76
+ expect(fs.existsSync(path.join(tmpDir, 'src', 'handlers', 'user_handler.ts'))).toBe(true);
77
+ expect(fs.existsSync(path.join(tmpDir, 'src', 'services', 'create_user_service.ts'))).toBe(true);
78
+ expect(fs.existsSync(path.join(tmpDir, 'src', 'repositories', 'user_repository.ts'))).toBe(true);
79
+ expect(fs.existsSync(path.join(tmpDir, 'src', 'models', 'user.ts'))).toBe(true);
80
+ expect(fs.existsSync(path.join(tmpDir, 'src', 'presenters', 'user_presenter.ts'))).toBe(true);
81
+ });
82
+
83
+ it('creates Ruby example files when withExamples=true and language=ruby', () => {
84
+ generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'ruby', srcDir: 'app' });
85
+
86
+ expect(fs.existsSync(path.join(tmpDir, 'app', 'handlers', 'user_handler.rb'))).toBe(true);
87
+ expect(fs.existsSync(path.join(tmpDir, 'app', 'services', 'create_user_service.rb'))).toBe(true);
88
+ expect(fs.existsSync(path.join(tmpDir, 'app', 'repositories', 'user_repository.rb'))).toBe(true);
89
+ expect(fs.existsSync(path.join(tmpDir, 'app', 'models', 'user.rb'))).toBe(true);
90
+ expect(fs.existsSync(path.join(tmpDir, 'app', 'presenters', 'user_presenter.rb'))).toBe(true);
91
+ });
92
+
93
+ it('TypeScript handler example does not import Repository', () => {
94
+ generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'typescript' });
95
+
96
+ const content = fs.readFileSync(
97
+ path.join(tmpDir, 'src', 'handlers', 'user_handler.ts'),
98
+ 'utf-8'
99
+ );
100
+ expect(content).not.toContain('import.*Repository');
101
+ expect(content).toContain('Service');
102
+ });
103
+
104
+ it('TypeScript service example mentions DI (no new Repository)', () => {
105
+ generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'product', language: 'typescript' });
106
+
107
+ const content = fs.readFileSync(
108
+ path.join(tmpDir, 'src', 'services', 'create_product_service.ts'),
109
+ 'utf-8'
110
+ );
111
+ // Should receive repository via constructor, not instantiate it
112
+ expect(content).toContain('constructor');
113
+ expect(content).not.toMatch(/new\s+\w+Repository\s*\(/);
114
+ });
115
+
116
+ it('TypeScript repository example includes InMemory implementation', () => {
117
+ generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'typescript' });
118
+
119
+ const content = fs.readFileSync(
120
+ path.join(tmpDir, 'src', 'repositories', 'user_repository.ts'),
121
+ 'utf-8'
122
+ );
123
+ expect(content).toContain('InMemory');
124
+ expect(content).toContain('interface UserRepository');
125
+ });
126
+
127
+ it('does not overwrite existing files on second scaffold', () => {
128
+ generator.scaffold(tmpDir);
129
+
130
+ // Manually modify a README
131
+ const readmePath = path.join(tmpDir, 'src', 'handlers', 'README.md');
132
+ fs.writeFileSync(readmePath, '# Custom README\n', 'utf-8');
133
+
134
+ // Scaffold again
135
+ generator.scaffold(tmpDir);
136
+
137
+ // Custom README should be preserved
138
+ const content = fs.readFileSync(readmePath, 'utf-8');
139
+ expect(content).toContain('Custom README');
140
+ });
141
+
142
+ it('returns createdFiles list with README.md paths', () => {
143
+ const result = generator.scaffold(tmpDir);
144
+
145
+ expect(result.createdFiles.some((f) => f.endsWith('README.md'))).toBe(true);
146
+ expect(result.createdFiles.filter((f) => f.endsWith('README.md'))).toHaveLength(5);
147
+ });
148
+
149
+ it('handles multi-word entity names with snake_case convention', () => {
150
+ generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user-profile', language: 'typescript' });
151
+
152
+ expect(fs.existsSync(path.join(tmpDir, 'src', 'handlers', 'user_profile_handler.ts'))).toBe(true);
153
+ expect(fs.existsSync(path.join(tmpDir, 'src', 'models', 'user_profile.ts'))).toBe(true);
154
+ });
155
+ });
156
+ });
@@ -0,0 +1,255 @@
1
+ /**
2
+ * dare-layered-design — linter tests
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
6
+ import fs from 'fs';
7
+ import os from 'os';
8
+ import path from 'path';
9
+ import { LayeredDesignLinter } from '../linter.js';
10
+
11
+ describe('LayeredDesignLinter', () => {
12
+ let tmpDir: string;
13
+ let linter: LayeredDesignLinter;
14
+
15
+ beforeEach(() => {
16
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dare-linter-test-'));
17
+ linter = new LayeredDesignLinter();
18
+ });
19
+
20
+ afterEach(() => {
21
+ fs.rmSync(tmpDir, { recursive: true, force: true });
22
+ });
23
+
24
+ // ── TypeScript / JavaScript ──────────────────────────────────────────────
25
+
26
+ describe('TypeScript handler violations', () => {
27
+ function createHandlerFile(name: string, content: string): string {
28
+ const handlersDir = path.join(tmpDir, 'src', 'handlers');
29
+ fs.mkdirSync(handlersDir, { recursive: true });
30
+ const file = path.join(handlersDir, name);
31
+ fs.writeFileSync(file, content, 'utf-8');
32
+ // Create tsconfig.json so language is detected
33
+ fs.writeFileSync(path.join(tmpDir, 'tsconfig.json'), '{}', 'utf-8');
34
+ return file;
35
+ }
36
+
37
+ it('detects import of Repository in handler (TS)', () => {
38
+ createHandlerFile(
39
+ 'user_handler.ts',
40
+ `import { UserRepository } from '../repositories/user_repository';
41
+ export class UserHandler {
42
+ async create(req: Request, res: Response) {
43
+ const repo = new UserRepository();
44
+ res.json(await repo.save(req.body));
45
+ }
46
+ }
47
+ `
48
+ );
49
+
50
+ const result = linter.lint(tmpDir, 'typescript');
51
+ expect(result.pass).toBe(false);
52
+ expect(result.violations.length).toBeGreaterThan(0);
53
+ expect(result.violations[0].message).toContain('Repository');
54
+ });
55
+
56
+ it('detects direct instantiation of Repository in handler (TS)', () => {
57
+ createHandlerFile(
58
+ 'order_handler.ts',
59
+ `export class OrderHandler {
60
+ async create(req: Request, res: Response) {
61
+ const repo = new OrderRepository(); // VIOLATION
62
+ const order = await repo.save(req.body);
63
+ res.json(order);
64
+ }
65
+ }
66
+ `
67
+ );
68
+
69
+ const result = linter.lint(tmpDir, 'typescript');
70
+ expect(result.pass).toBe(false);
71
+ expect(result.violations.some((v) => v.message.includes('instantiates'))).toBe(true);
72
+ });
73
+
74
+ it('detects direct repository method call in handler (TS)', () => {
75
+ createHandlerFile(
76
+ 'product_handler.ts',
77
+ `const userRepository = new UserRepository();
78
+
79
+ export async function getUser(req: Request, res: Response) {
80
+ const user = await userRepository.findById(req.params.id); // VIOLATION
81
+ res.json(user);
82
+ }
83
+ `
84
+ );
85
+
86
+ const result = linter.lint(tmpDir, 'typescript');
87
+ expect(result.pass).toBe(false);
88
+ });
89
+
90
+ it('does not flag a handler that only uses services (no violation)', () => {
91
+ createHandlerFile(
92
+ 'user_handler.ts',
93
+ `export class UserHandler {
94
+ constructor(private readonly createUserService: CreateUserService) {}
95
+
96
+ async create(req: Request, res: Response) {
97
+ const user = await this.createUserService.execute(req.body);
98
+ res.status(201).json(user);
99
+ }
100
+ }
101
+ `
102
+ );
103
+
104
+ const result = linter.lint(tmpDir, 'typescript');
105
+ expect(result.pass).toBe(true);
106
+ expect(result.violations).toHaveLength(0);
107
+ });
108
+
109
+ it('does not flag import of Service in handler', () => {
110
+ createHandlerFile(
111
+ 'user_handler.ts',
112
+ `import { CreateUserService } from '../services/create_user_service';
113
+
114
+ export class UserHandler {
115
+ constructor(private readonly service: CreateUserService) {}
116
+
117
+ async create(req: Request, res: Response) {
118
+ const user = await this.service.execute(req.body);
119
+ res.json(user);
120
+ }
121
+ }
122
+ `
123
+ );
124
+
125
+ const result = linter.lint(tmpDir, 'typescript');
126
+ expect(result.pass).toBe(true);
127
+ });
128
+
129
+ it('skips commented-out violations', () => {
130
+ createHandlerFile(
131
+ 'user_handler.ts',
132
+ `export class UserHandler {
133
+ async create(req: Request, res: Response) {
134
+ // const repo = new UserRepository(); // This would be a violation if uncommented
135
+ const user = await this.service.execute(req.body);
136
+ res.json(user);
137
+ }
138
+ }
139
+ `
140
+ );
141
+
142
+ const result = linter.lint(tmpDir, 'typescript');
143
+ expect(result.pass).toBe(true);
144
+ });
145
+ });
146
+
147
+ // ── Ruby ──────────────────────────────────────────────────────────────────
148
+
149
+ describe('Ruby handler violations', () => {
150
+ function createRubyHandlerFile(name: string, content: string): string {
151
+ const controllersDir = path.join(tmpDir, 'app', 'controllers');
152
+ fs.mkdirSync(controllersDir, { recursive: true });
153
+ const file = path.join(controllersDir, name);
154
+ fs.writeFileSync(file, content, 'utf-8');
155
+ fs.writeFileSync(path.join(tmpDir, 'Gemfile'), 'source "https://rubygems.org"', 'utf-8');
156
+ return file;
157
+ }
158
+
159
+ it('detects Repository call in Rails controller', () => {
160
+ createRubyHandlerFile(
161
+ 'users_controller.rb',
162
+ `class UsersController < ApplicationController
163
+ def create
164
+ user = UserRepository.save(user_params) # VIOLATION
165
+ render json: user
166
+ end
167
+ end
168
+ `
169
+ );
170
+
171
+ const result = linter.lint(tmpDir, 'ruby');
172
+ expect(result.pass).toBe(false);
173
+ expect(result.violations.length).toBeGreaterThan(0);
174
+ });
175
+
176
+ it('passes when controller only calls service', () => {
177
+ createRubyHandlerFile(
178
+ 'users_controller.rb',
179
+ `class UsersController < ApplicationController
180
+ def initialize(create_user_service:)
181
+ @create_user_service = create_user_service
182
+ end
183
+
184
+ def create
185
+ user = @create_user_service.call(user_params)
186
+ render json: user, status: :created
187
+ end
188
+ end
189
+ `
190
+ );
191
+
192
+ const result = linter.lint(tmpDir, 'ruby');
193
+ expect(result.pass).toBe(true);
194
+ });
195
+ });
196
+
197
+ // ── lintFile() ────────────────────────────────────────────────────────────
198
+
199
+ describe('lintFile()', () => {
200
+ it('lints a single TypeScript handler file', () => {
201
+ const file = path.join(tmpDir, 'user_handler.ts');
202
+ fs.writeFileSync(
203
+ file,
204
+ `import { UserRepository } from '../repositories/user_repository';\n`,
205
+ 'utf-8'
206
+ );
207
+
208
+ const result = linter.lintFile(file, 'typescript');
209
+ expect(result.pass).toBe(false);
210
+ expect(result.violations).toHaveLength(1);
211
+ });
212
+
213
+ it('returns pass=true for clean handler file', () => {
214
+ const file = path.join(tmpDir, 'user_handler.ts');
215
+ fs.writeFileSync(
216
+ file,
217
+ `import { CreateUserService } from '../services/create_user_service';\n`,
218
+ 'utf-8'
219
+ );
220
+
221
+ const result = linter.lintFile(file, 'typescript');
222
+ expect(result.pass).toBe(true);
223
+ });
224
+
225
+ it('returns filesScanned=1 for single file lint', () => {
226
+ const file = path.join(tmpDir, 'user_handler.ts');
227
+ fs.writeFileSync(file, 'export const x = 1;\n', 'utf-8');
228
+
229
+ const result = linter.lintFile(file, 'typescript');
230
+ expect(result.filesScanned).toBe(1);
231
+ });
232
+ });
233
+
234
+ // ── Empty project ─────────────────────────────────────────────────────────
235
+
236
+ it('returns pass=true with 0 violations for empty project', () => {
237
+ const result = linter.lint(tmpDir);
238
+ expect(result.pass).toBe(true);
239
+ expect(result.violations).toHaveLength(0);
240
+ });
241
+
242
+ it('reports filesScanned > 0 when handler files exist', () => {
243
+ const handlersDir = path.join(tmpDir, 'src', 'handlers');
244
+ fs.mkdirSync(handlersDir, { recursive: true });
245
+ fs.writeFileSync(
246
+ path.join(handlersDir, 'clean_handler.ts'),
247
+ 'export const handler = () => {};\n',
248
+ 'utf-8'
249
+ );
250
+ fs.writeFileSync(path.join(tmpDir, 'tsconfig.json'), '{}', 'utf-8');
251
+
252
+ const result = linter.lint(tmpDir, 'typescript');
253
+ expect(result.filesScanned).toBeGreaterThan(0);
254
+ });
255
+ });