@dewtech/dare-cli 3.0.0 → 3.1.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 (453) hide show
  1. package/README.md +45 -39
  2. package/dist/bin/dare.js +1 -3
  3. package/dist/bin/dare.js.map +1 -1
  4. package/dist/commands/__tests__/init.integration.spec.d.ts +2 -0
  5. package/dist/commands/__tests__/init.integration.spec.d.ts.map +1 -0
  6. package/dist/commands/__tests__/init.integration.spec.js +134 -0
  7. package/dist/commands/__tests__/init.integration.spec.js.map +1 -0
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +84 -1
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/new.d.ts.map +1 -1
  12. package/dist/commands/new.js +2 -1
  13. package/dist/commands/new.js.map +1 -1
  14. package/dist/mcp-server/bin/server.js +0 -0
  15. package/dist/stacks/__tests__/dna-emitter.spec.d.ts +2 -0
  16. package/dist/stacks/__tests__/dna-emitter.spec.d.ts.map +1 -0
  17. package/dist/stacks/__tests__/dna-emitter.spec.js +207 -0
  18. package/dist/stacks/__tests__/dna-emitter.spec.js.map +1 -0
  19. package/dist/stacks/__tests__/dna.spec.d.ts +2 -0
  20. package/dist/stacks/__tests__/dna.spec.d.ts.map +1 -0
  21. package/dist/stacks/__tests__/dna.spec.js +211 -0
  22. package/dist/stacks/__tests__/dna.spec.js.map +1 -0
  23. package/dist/stacks/__tests__/parity-rails.fixture.json +228 -0
  24. package/dist/stacks/__tests__/parity-rails.spec.d.ts +2 -0
  25. package/dist/stacks/__tests__/parity-rails.spec.d.ts.map +1 -0
  26. package/dist/stacks/__tests__/parity-rails.spec.js +99 -0
  27. package/dist/stacks/__tests__/parity-rails.spec.js.map +1 -0
  28. package/dist/stacks/__tests__/registry.spec.d.ts +2 -0
  29. package/dist/stacks/__tests__/registry.spec.d.ts.map +1 -0
  30. package/dist/stacks/__tests__/registry.spec.js +101 -0
  31. package/dist/stacks/__tests__/registry.spec.js.map +1 -0
  32. package/dist/stacks/__tests__/template-engine.spec.d.ts +2 -0
  33. package/dist/stacks/__tests__/template-engine.spec.d.ts.map +1 -0
  34. package/dist/stacks/__tests__/template-engine.spec.js +149 -0
  35. package/dist/stacks/__tests__/template-engine.spec.js.map +1 -0
  36. package/dist/stacks/dna-emitter.d.ts +45 -0
  37. package/dist/stacks/dna-emitter.d.ts.map +1 -0
  38. package/dist/stacks/dna-emitter.js +267 -0
  39. package/dist/stacks/dna-emitter.js.map +1 -0
  40. package/dist/stacks/go-gin/__tests__/scaffold.spec.d.ts +2 -0
  41. package/dist/stacks/go-gin/__tests__/scaffold.spec.d.ts.map +1 -0
  42. package/dist/stacks/go-gin/__tests__/scaffold.spec.js +221 -0
  43. package/dist/stacks/go-gin/__tests__/scaffold.spec.js.map +1 -0
  44. package/dist/stacks/go-gin/scaffold.d.ts +3 -0
  45. package/dist/stacks/go-gin/scaffold.d.ts.map +1 -0
  46. package/dist/stacks/go-gin/scaffold.js +105 -0
  47. package/dist/stacks/go-gin/scaffold.js.map +1 -0
  48. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.d.ts +2 -0
  49. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.d.ts.map +1 -0
  50. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.js +215 -0
  51. package/dist/stacks/go-stdlib/__tests__/scaffold.spec.js.map +1 -0
  52. package/dist/stacks/go-stdlib/scaffold.d.ts +3 -0
  53. package/dist/stacks/go-stdlib/scaffold.d.ts.map +1 -0
  54. package/dist/stacks/go-stdlib/scaffold.js +106 -0
  55. package/dist/stacks/go-stdlib/scaffold.js.map +1 -0
  56. package/dist/stacks/mcp-go/__tests__/scaffold.spec.d.ts +2 -0
  57. package/dist/stacks/mcp-go/__tests__/scaffold.spec.d.ts.map +1 -0
  58. package/dist/stacks/mcp-go/__tests__/scaffold.spec.js +203 -0
  59. package/dist/stacks/mcp-go/__tests__/scaffold.spec.js.map +1 -0
  60. package/dist/stacks/mcp-go/scaffold.d.ts +3 -0
  61. package/dist/stacks/mcp-go/scaffold.d.ts.map +1 -0
  62. package/dist/stacks/mcp-go/scaffold.js +94 -0
  63. package/dist/stacks/mcp-go/scaffold.js.map +1 -0
  64. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.d.ts +2 -0
  65. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.d.ts.map +1 -0
  66. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.js +236 -0
  67. package/dist/stacks/mcp-node-ts/__tests__/scaffold.spec.js.map +1 -0
  68. package/dist/stacks/mcp-node-ts/scaffold.d.ts +3 -0
  69. package/dist/stacks/mcp-node-ts/scaffold.d.ts.map +1 -0
  70. package/dist/stacks/mcp-node-ts/scaffold.js +95 -0
  71. package/dist/stacks/mcp-node-ts/scaffold.js.map +1 -0
  72. package/dist/stacks/mcp-python/__tests__/scaffold.spec.d.ts +2 -0
  73. package/dist/stacks/mcp-python/__tests__/scaffold.spec.d.ts.map +1 -0
  74. package/dist/stacks/mcp-python/__tests__/scaffold.spec.js +228 -0
  75. package/dist/stacks/mcp-python/__tests__/scaffold.spec.js.map +1 -0
  76. package/dist/stacks/mcp-python/scaffold.d.ts +3 -0
  77. package/dist/stacks/mcp-python/scaffold.d.ts.map +1 -0
  78. package/dist/stacks/mcp-python/scaffold.js +98 -0
  79. package/dist/stacks/mcp-python/scaffold.js.map +1 -0
  80. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.d.ts +2 -0
  81. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.d.ts.map +1 -0
  82. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.js +213 -0
  83. package/dist/stacks/mcp-rust/__tests__/scaffold.spec.js.map +1 -0
  84. package/dist/stacks/mcp-rust/scaffold.d.ts +3 -0
  85. package/dist/stacks/mcp-rust/scaffold.d.ts.map +1 -0
  86. package/dist/stacks/mcp-rust/scaffold.js +98 -0
  87. package/dist/stacks/mcp-rust/scaffold.js.map +1 -0
  88. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.d.ts +2 -0
  89. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.d.ts.map +1 -0
  90. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.js +172 -0
  91. package/dist/stacks/node-nestjs/__tests__/scaffold.spec.js.map +1 -0
  92. package/dist/stacks/node-nestjs/scaffold.d.ts +3 -0
  93. package/dist/stacks/node-nestjs/scaffold.d.ts.map +1 -0
  94. package/dist/stacks/node-nestjs/scaffold.js +117 -0
  95. package/dist/stacks/node-nestjs/scaffold.js.map +1 -0
  96. package/dist/stacks/php-laravel/__tests__/scaffold.spec.d.ts +2 -0
  97. package/dist/stacks/php-laravel/__tests__/scaffold.spec.d.ts.map +1 -0
  98. package/dist/stacks/php-laravel/__tests__/scaffold.spec.js +205 -0
  99. package/dist/stacks/php-laravel/__tests__/scaffold.spec.js.map +1 -0
  100. package/dist/stacks/php-laravel/scaffold.d.ts +3 -0
  101. package/dist/stacks/php-laravel/scaffold.d.ts.map +1 -0
  102. package/dist/stacks/php-laravel/scaffold.js +109 -0
  103. package/dist/stacks/php-laravel/scaffold.js.map +1 -0
  104. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.d.ts +2 -0
  105. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.d.ts.map +1 -0
  106. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.js +168 -0
  107. package/dist/stacks/python-fastapi/__tests__/scaffold.spec.js.map +1 -0
  108. package/dist/stacks/python-fastapi/scaffold.d.ts +3 -0
  109. package/dist/stacks/python-fastapi/scaffold.d.ts.map +1 -0
  110. package/dist/stacks/python-fastapi/scaffold.js +108 -0
  111. package/dist/stacks/python-fastapi/scaffold.js.map +1 -0
  112. package/dist/stacks/registry.d.ts +38 -0
  113. package/dist/stacks/registry.d.ts.map +1 -0
  114. package/dist/stacks/registry.js +153 -0
  115. package/dist/stacks/registry.js.map +1 -0
  116. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.d.ts +6 -0
  117. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.d.ts.map +1 -0
  118. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.js +604 -0
  119. package/dist/stacks/ruby-rails-8/__tests__/scaffold.spec.js.map +1 -0
  120. package/dist/stacks/ruby-rails-8/scaffold.d.ts +91 -0
  121. package/dist/stacks/ruby-rails-8/scaffold.d.ts.map +1 -0
  122. package/dist/stacks/ruby-rails-8/scaffold.js +410 -0
  123. package/dist/stacks/ruby-rails-8/scaffold.js.map +1 -0
  124. package/dist/stacks/rust-axum/__tests__/scaffold.spec.d.ts +2 -0
  125. package/dist/stacks/rust-axum/__tests__/scaffold.spec.d.ts.map +1 -0
  126. package/dist/stacks/rust-axum/__tests__/scaffold.spec.js +203 -0
  127. package/dist/stacks/rust-axum/__tests__/scaffold.spec.js.map +1 -0
  128. package/dist/stacks/rust-axum/scaffold.d.ts +3 -0
  129. package/dist/stacks/rust-axum/scaffold.d.ts.map +1 -0
  130. package/dist/stacks/rust-axum/scaffold.js +105 -0
  131. package/dist/stacks/rust-axum/scaffold.js.map +1 -0
  132. package/dist/stacks/template-engine.d.ts +38 -0
  133. package/dist/stacks/template-engine.d.ts.map +1 -0
  134. package/dist/stacks/template-engine.js +134 -0
  135. package/dist/stacks/template-engine.js.map +1 -0
  136. package/dist/stacks/types.d.ts +69 -0
  137. package/dist/stacks/types.d.ts.map +1 -0
  138. package/dist/stacks/types.js +29 -0
  139. package/dist/stacks/types.js.map +1 -0
  140. package/dist/utils/project-generator.d.ts +1 -1
  141. package/dist/utils/project-generator.d.ts.map +1 -1
  142. package/dist/utils/project-generator.js +16 -7
  143. package/dist/utils/project-generator.js.map +1 -1
  144. package/dist/utils/stack-bootstrap.d.ts +3 -2
  145. package/dist/utils/stack-bootstrap.d.ts.map +1 -1
  146. package/dist/utils/stack-bootstrap.js +46 -16
  147. package/dist/utils/stack-bootstrap.js.map +1 -1
  148. package/package.json +91 -87
  149. package/templates/stacks/go-gin/.dare/skills.yml +11 -0
  150. package/templates/stacks/go-gin/.env.example +24 -0
  151. package/templates/stacks/go-gin/.github/workflows/dare-ci.yml +42 -0
  152. package/templates/stacks/go-gin/README.md.tpl +38 -0
  153. package/templates/stacks/go-gin/cmd/server/main.go.tpl +78 -0
  154. package/templates/stacks/go-gin/db/migrations/0001_create_users.down.sql +2 -0
  155. package/templates/stacks/go-gin/db/migrations/0001_create_users.up.sql +12 -0
  156. package/templates/stacks/go-gin/db/queries/users.sql +23 -0
  157. package/templates/stacks/go-gin/gitignore +7 -0
  158. package/templates/stacks/go-gin/go.mod.tpl +17 -0
  159. package/templates/stacks/go-gin/internal/config/config.go +41 -0
  160. package/templates/stacks/go-gin/internal/db/postgres.go.tpl +25 -0
  161. package/templates/stacks/go-gin/internal/handler/auth_handler.go.tpl +72 -0
  162. package/templates/stacks/go-gin/internal/handler/users_handler.go.tpl +72 -0
  163. package/templates/stacks/go-gin/internal/handler/ws_handler.go +37 -0
  164. package/templates/stacks/go-gin/internal/llm/dummy.go +14 -0
  165. package/templates/stacks/go-gin/internal/llm/provider.go +8 -0
  166. package/templates/stacks/go-gin/internal/middleware/jwt.go.tpl +58 -0
  167. package/templates/stacks/go-gin/internal/middleware/rate_limit.go +55 -0
  168. package/templates/stacks/go-gin/internal/model/user.go +17 -0
  169. package/templates/stacks/go-gin/internal/repository/users_repository.go.tpl +79 -0
  170. package/templates/stacks/go-gin/internal/service/auth_service.go.tpl +55 -0
  171. package/templates/stacks/go-gin/internal/service/users_service.go.tpl +53 -0
  172. package/templates/stacks/go-gin/llms.txt.tpl +54 -0
  173. package/templates/stacks/go-gin/openapi.json.tpl +46 -0
  174. package/templates/stacks/go-gin/sqlc.yaml +14 -0
  175. package/templates/stacks/go-gin/tests/smoke_test.go.tpl +22 -0
  176. package/templates/stacks/go-stdlib/.dare/skills.yml +11 -0
  177. package/templates/stacks/go-stdlib/.env.example +24 -0
  178. package/templates/stacks/go-stdlib/.github/workflows/dare-ci.yml +42 -0
  179. package/templates/stacks/go-stdlib/README.md.tpl +41 -0
  180. package/templates/stacks/go-stdlib/cmd/server/main.go.tpl +82 -0
  181. package/templates/stacks/go-stdlib/db/migrations/0001_create_users.down.sql +2 -0
  182. package/templates/stacks/go-stdlib/db/migrations/0001_create_users.up.sql +12 -0
  183. package/templates/stacks/go-stdlib/db/queries/users.sql +23 -0
  184. package/templates/stacks/go-stdlib/gitignore +6 -0
  185. package/templates/stacks/go-stdlib/go.mod.tpl +15 -0
  186. package/templates/stacks/go-stdlib/internal/config/config.go +41 -0
  187. package/templates/stacks/go-stdlib/internal/db/postgres.go.tpl +24 -0
  188. package/templates/stacks/go-stdlib/internal/handler/auth_handler.go.tpl +71 -0
  189. package/templates/stacks/go-stdlib/internal/handler/users_handler.go.tpl +84 -0
  190. package/templates/stacks/go-stdlib/internal/handler/ws_handler.go +36 -0
  191. package/templates/stacks/go-stdlib/internal/httpx/json.go +32 -0
  192. package/templates/stacks/go-stdlib/internal/llm/dummy.go +14 -0
  193. package/templates/stacks/go-stdlib/internal/llm/provider.go +8 -0
  194. package/templates/stacks/go-stdlib/internal/middleware/chain.go +21 -0
  195. package/templates/stacks/go-stdlib/internal/middleware/cors.go +27 -0
  196. package/templates/stacks/go-stdlib/internal/middleware/jwt.go.tpl +51 -0
  197. package/templates/stacks/go-stdlib/internal/middleware/rate_limit.go +81 -0
  198. package/templates/stacks/go-stdlib/internal/model/user.go +17 -0
  199. package/templates/stacks/go-stdlib/internal/repository/users_repository.go.tpl +75 -0
  200. package/templates/stacks/go-stdlib/internal/service/auth_service.go.tpl +55 -0
  201. package/templates/stacks/go-stdlib/internal/service/users_service.go.tpl +53 -0
  202. package/templates/stacks/go-stdlib/llms.txt.tpl +60 -0
  203. package/templates/stacks/go-stdlib/openapi.json.tpl +46 -0
  204. package/templates/stacks/go-stdlib/sqlc.yaml +14 -0
  205. package/templates/stacks/go-stdlib/tests/smoke_test.go.tpl +45 -0
  206. package/templates/stacks/mcp-go/.dare/skills.yml +8 -0
  207. package/templates/stacks/mcp-go/.env.example +14 -0
  208. package/templates/stacks/mcp-go/.github/workflows/dare-ci.yml +42 -0
  209. package/templates/stacks/mcp-go/README.md.tpl +50 -0
  210. package/templates/stacks/mcp-go/cmd/server/main.go.tpl +62 -0
  211. package/templates/stacks/mcp-go/gitignore +6 -0
  212. package/templates/stacks/mcp-go/go.mod.tpl +9 -0
  213. package/templates/stacks/mcp-go/internal/prompts/summarize.go +9 -0
  214. package/templates/stacks/mcp-go/internal/server/server.go.tpl +80 -0
  215. package/templates/stacks/mcp-go/internal/tools/echo.go +15 -0
  216. package/templates/stacks/mcp-go/internal/transports/http.go.tpl +21 -0
  217. package/templates/stacks/mcp-go/internal/transports/sse.go.tpl +17 -0
  218. package/templates/stacks/mcp-go/internal/transports/stdio.go.tpl +14 -0
  219. package/templates/stacks/mcp-go/llms.txt.tpl +60 -0
  220. package/templates/stacks/mcp-go/openapi.json.tpl +31 -0
  221. package/templates/stacks/mcp-go/tests/echo_test.go.tpl +37 -0
  222. package/templates/stacks/mcp-node-ts/.dare/skills.yml +8 -0
  223. package/templates/stacks/mcp-node-ts/.env.example +16 -0
  224. package/templates/stacks/mcp-node-ts/.github/workflows/dare-ci.yml +54 -0
  225. package/templates/stacks/mcp-node-ts/README.md.hbs +49 -0
  226. package/templates/stacks/mcp-node-ts/gitignore +7 -0
  227. package/templates/stacks/mcp-node-ts/llms.txt.hbs +61 -0
  228. package/templates/stacks/mcp-node-ts/openapi.json.hbs +39 -0
  229. package/templates/stacks/mcp-node-ts/package.json.hbs +35 -0
  230. package/templates/stacks/mcp-node-ts/src/cli.ts.hbs +71 -0
  231. package/templates/stacks/mcp-node-ts/src/prompts/index.ts +36 -0
  232. package/templates/stacks/mcp-node-ts/src/server.ts.hbs +45 -0
  233. package/templates/stacks/mcp-node-ts/src/tools/echo.ts +23 -0
  234. package/templates/stacks/mcp-node-ts/src/tools/index.ts +18 -0
  235. package/templates/stacks/mcp-node-ts/src/transports/http.ts +68 -0
  236. package/templates/stacks/mcp-node-ts/src/transports/sse.ts +58 -0
  237. package/templates/stacks/mcp-node-ts/src/transports/stdio.ts +5 -0
  238. package/templates/stacks/mcp-node-ts/tests/echo.test.ts +50 -0
  239. package/templates/stacks/mcp-node-ts/tsconfig.json +17 -0
  240. package/templates/stacks/mcp-python/.dare/skills.yml +8 -0
  241. package/templates/stacks/mcp-python/.env.example +14 -0
  242. package/templates/stacks/mcp-python/.github/workflows/dare-ci.yml +42 -0
  243. package/templates/stacks/mcp-python/README.md.j2 +49 -0
  244. package/templates/stacks/mcp-python/gitignore +12 -0
  245. package/templates/stacks/mcp-python/llms.txt.j2 +56 -0
  246. package/templates/stacks/mcp-python/openapi.json.j2 +33 -0
  247. package/templates/stacks/mcp-python/pyproject.toml.j2 +37 -0
  248. package/templates/stacks/mcp-python/src/__init__.py +0 -0
  249. package/templates/stacks/mcp-python/src/cli.py.j2 +68 -0
  250. package/templates/stacks/mcp-python/src/prompts/__init__.py +0 -0
  251. package/templates/stacks/mcp-python/src/prompts/summarize.py +10 -0
  252. package/templates/stacks/mcp-python/src/server.py.j2 +28 -0
  253. package/templates/stacks/mcp-python/src/tools/__init__.py +0 -0
  254. package/templates/stacks/mcp-python/src/tools/echo.py +12 -0
  255. package/templates/stacks/mcp-python/src/transports/__init__.py +0 -0
  256. package/templates/stacks/mcp-python/src/transports/http.py +12 -0
  257. package/templates/stacks/mcp-python/src/transports/sse.py +13 -0
  258. package/templates/stacks/mcp-python/src/transports/stdio.py +6 -0
  259. package/templates/stacks/mcp-python/tests/__init__.py +0 -0
  260. package/templates/stacks/mcp-python/tests/test_echo.py +28 -0
  261. package/templates/stacks/mcp-rust/.dare/skills.yml +8 -0
  262. package/templates/stacks/mcp-rust/.env.example +14 -0
  263. package/templates/stacks/mcp-rust/.github/workflows/dare-ci.yml +38 -0
  264. package/templates/stacks/mcp-rust/Cargo.toml.tera +35 -0
  265. package/templates/stacks/mcp-rust/README.md.tera +50 -0
  266. package/templates/stacks/mcp-rust/gitignore +5 -0
  267. package/templates/stacks/mcp-rust/llms.txt.tera +60 -0
  268. package/templates/stacks/mcp-rust/openapi.json.tera +31 -0
  269. package/templates/stacks/mcp-rust/src/cli.rs.tera +33 -0
  270. package/templates/stacks/mcp-rust/src/lib.rs +6 -0
  271. package/templates/stacks/mcp-rust/src/main.rs.tera +30 -0
  272. package/templates/stacks/mcp-rust/src/prompts/mod.rs +1 -0
  273. package/templates/stacks/mcp-rust/src/prompts/summarize.rs +5 -0
  274. package/templates/stacks/mcp-rust/src/server.rs.tera +38 -0
  275. package/templates/stacks/mcp-rust/src/tools/echo.rs +18 -0
  276. package/templates/stacks/mcp-rust/src/tools/mod.rs +22 -0
  277. package/templates/stacks/mcp-rust/src/transports/http.rs +27 -0
  278. package/templates/stacks/mcp-rust/src/transports/mod.rs +3 -0
  279. package/templates/stacks/mcp-rust/src/transports/sse.rs +33 -0
  280. package/templates/stacks/mcp-rust/src/transports/stdio.rs +14 -0
  281. package/templates/stacks/mcp-rust/tests/echo_test.rs.tera +27 -0
  282. package/templates/stacks/node-nestjs/.dare/skills.yml +11 -0
  283. package/templates/stacks/node-nestjs/.env.example +21 -0
  284. package/templates/stacks/node-nestjs/.github/workflows/dare-ci.yml +54 -0
  285. package/templates/stacks/node-nestjs/README.md.hbs +35 -0
  286. package/templates/stacks/node-nestjs/gitignore +7 -0
  287. package/templates/stacks/node-nestjs/llms.txt.hbs +47 -0
  288. package/templates/stacks/node-nestjs/nest-cli.json +16 -0
  289. package/templates/stacks/node-nestjs/openapi.json.hbs +75 -0
  290. package/templates/stacks/node-nestjs/package.json.hbs +57 -0
  291. package/templates/stacks/node-nestjs/prisma/schema.prisma +25 -0
  292. package/templates/stacks/node-nestjs/prisma/seed.ts.hbs +25 -0
  293. package/templates/stacks/node-nestjs/src/app.module.ts +39 -0
  294. package/templates/stacks/node-nestjs/src/auth/auth.controller.ts +29 -0
  295. package/templates/stacks/node-nestjs/src/auth/auth.module.ts +25 -0
  296. package/templates/stacks/node-nestjs/src/auth/auth.service.ts +36 -0
  297. package/templates/stacks/node-nestjs/src/auth/dto/login-response.dto.ts +9 -0
  298. package/templates/stacks/node-nestjs/src/auth/dto/login.dto.ts +17 -0
  299. package/templates/stacks/node-nestjs/src/auth/jwt.strategy.ts +25 -0
  300. package/templates/stacks/node-nestjs/src/common/filters/problem-details.filter.ts +38 -0
  301. package/templates/stacks/node-nestjs/src/common/interceptors/json-response.interceptor.ts +13 -0
  302. package/templates/stacks/node-nestjs/src/main.ts.hbs +44 -0
  303. package/templates/stacks/node-nestjs/src/prisma/prisma.module.ts +9 -0
  304. package/templates/stacks/node-nestjs/src/prisma/prisma.service.ts +9 -0
  305. package/templates/stacks/node-nestjs/src/users/dto/create-user.dto.ts +22 -0
  306. package/templates/stacks/node-nestjs/src/users/dto/user.dto.ts +15 -0
  307. package/templates/stacks/node-nestjs/src/users/users.controller.ts +41 -0
  308. package/templates/stacks/node-nestjs/src/users/users.module.ts +11 -0
  309. package/templates/stacks/node-nestjs/src/users/users.repository.ts +38 -0
  310. package/templates/stacks/node-nestjs/src/users/users.service.ts +38 -0
  311. package/templates/stacks/node-nestjs/tsconfig.build.json +4 -0
  312. package/templates/stacks/node-nestjs/tsconfig.json +28 -0
  313. package/templates/stacks/php-laravel/.dare/skills.yml +11 -0
  314. package/templates/stacks/php-laravel/.env.example +41 -0
  315. package/templates/stacks/php-laravel/.github/workflows/dare-ci.yml +43 -0
  316. package/templates/stacks/php-laravel/README.md.hbs +36 -0
  317. package/templates/stacks/php-laravel/app/Http/Controllers/Api/AuthController.php +36 -0
  318. package/templates/stacks/php-laravel/app/Http/Controllers/Api/UsersController.php +33 -0
  319. package/templates/stacks/php-laravel/app/Http/Requests/CreateUserRequest.php +26 -0
  320. package/templates/stacks/php-laravel/app/Http/Requests/LoginRequest.php +34 -0
  321. package/templates/stacks/php-laravel/app/Llm/Contracts/LlmProvider.php +12 -0
  322. package/templates/stacks/php-laravel/app/Llm/Providers/DummyProvider.php +13 -0
  323. package/templates/stacks/php-laravel/app/Llm/Providers/OpenAiProvider.php +33 -0
  324. package/templates/stacks/php-laravel/app/Models/User.php +44 -0
  325. package/templates/stacks/php-laravel/app/Repositories/UsersRepository.php +32 -0
  326. package/templates/stacks/php-laravel/app/Services/AuthService.php +37 -0
  327. package/templates/stacks/php-laravel/app/Services/UsersService.php +57 -0
  328. package/templates/stacks/php-laravel/artisan +12 -0
  329. package/templates/stacks/php-laravel/bootstrap/app.php +29 -0
  330. package/templates/stacks/php-laravel/bootstrap/providers.php +5 -0
  331. package/templates/stacks/php-laravel/composer.json.hbs +58 -0
  332. package/templates/stacks/php-laravel/config/l5-swagger.php +41 -0
  333. package/templates/stacks/php-laravel/config/reverb.php +34 -0
  334. package/templates/stacks/php-laravel/config/sanctum.php +15 -0
  335. package/templates/stacks/php-laravel/database/migrations/2026_06_01_000001_create_users_table.php +27 -0
  336. package/templates/stacks/php-laravel/database/seeders/DatabaseSeeder.php +21 -0
  337. package/templates/stacks/php-laravel/gitignore +23 -0
  338. package/templates/stacks/php-laravel/llms.txt.hbs +53 -0
  339. package/templates/stacks/php-laravel/openapi.json.hbs +43 -0
  340. package/templates/stacks/php-laravel/phpstan.neon +9 -0
  341. package/templates/stacks/php-laravel/routes/api.php +13 -0
  342. package/templates/stacks/php-laravel/routes/channels.php +7 -0
  343. package/templates/stacks/php-laravel/tests/Feature/AuthTest.php +35 -0
  344. package/templates/stacks/php-laravel/tests/Feature/UsersTest.php +30 -0
  345. package/templates/stacks/php-laravel/tests/Pest.php +5 -0
  346. package/templates/stacks/python-fastapi/.dare/skills.yml +11 -0
  347. package/templates/stacks/python-fastapi/.env.example +21 -0
  348. package/templates/stacks/python-fastapi/.github/workflows/dare-ci.yml +43 -0
  349. package/templates/stacks/python-fastapi/README.md.j2 +35 -0
  350. package/templates/stacks/python-fastapi/alembic/env.py +46 -0
  351. package/templates/stacks/python-fastapi/alembic/script.py.mako +26 -0
  352. package/templates/stacks/python-fastapi/alembic/versions/0001_create_users.py.j2 +37 -0
  353. package/templates/stacks/python-fastapi/alembic.ini.j2 +39 -0
  354. package/templates/stacks/python-fastapi/app/__init__.py +0 -0
  355. package/templates/stacks/python-fastapi/app/core/__init__.py +0 -0
  356. package/templates/stacks/python-fastapi/app/core/config.py +24 -0
  357. package/templates/stacks/python-fastapi/app/core/security.py +34 -0
  358. package/templates/stacks/python-fastapi/app/db/__init__.py +0 -0
  359. package/templates/stacks/python-fastapi/app/db/session.py +22 -0
  360. package/templates/stacks/python-fastapi/app/main.py.j2 +36 -0
  361. package/templates/stacks/python-fastapi/app/models/__init__.py +3 -0
  362. package/templates/stacks/python-fastapi/app/models/user.py +30 -0
  363. package/templates/stacks/python-fastapi/app/repositories/__init__.py +0 -0
  364. package/templates/stacks/python-fastapi/app/repositories/user_repository.py +34 -0
  365. package/templates/stacks/python-fastapi/app/routers/__init__.py +0 -0
  366. package/templates/stacks/python-fastapi/app/routers/auth.py +37 -0
  367. package/templates/stacks/python-fastapi/app/routers/users.py +46 -0
  368. package/templates/stacks/python-fastapi/app/schemas/__init__.py +0 -0
  369. package/templates/stacks/python-fastapi/app/schemas/user.py +56 -0
  370. package/templates/stacks/python-fastapi/app/services/__init__.py +0 -0
  371. package/templates/stacks/python-fastapi/app/services/auth_service.py +22 -0
  372. package/templates/stacks/python-fastapi/app/services/user_service.py +31 -0
  373. package/templates/stacks/python-fastapi/gitignore +12 -0
  374. package/templates/stacks/python-fastapi/llms.txt.j2 +53 -0
  375. package/templates/stacks/python-fastapi/openapi.json.j2 +43 -0
  376. package/templates/stacks/python-fastapi/pyproject.toml.j2 +45 -0
  377. package/templates/stacks/python-fastapi/tests/__init__.py +0 -0
  378. package/templates/stacks/python-fastapi/tests/test_auth.py +22 -0
  379. package/templates/stacks/ruby-rails-8/.dare/skills.yml +50 -0
  380. package/templates/stacks/ruby-rails-8/.env.example +20 -0
  381. package/templates/stacks/ruby-rails-8/.github/workflows/dare-ci.yml +112 -0
  382. package/templates/stacks/ruby-rails-8/Gemfile.erb +61 -0
  383. package/templates/stacks/ruby-rails-8/app/channels/application_cable/channel.rb +11 -0
  384. package/templates/stacks/ruby-rails-8/app/channels/application_cable/connection.rb +34 -0
  385. package/templates/stacks/ruby-rails-8/app/channels/dare_updates_channel.rb +18 -0
  386. package/templates/stacks/ruby-rails-8/app/channels/user_updates_channel.rb +23 -0
  387. package/templates/stacks/ruby-rails-8/app/controllers/application_controller.rb +44 -0
  388. package/templates/stacks/ruby-rails-8/app/controllers/concerns/problem_details.rb +93 -0
  389. package/templates/stacks/ruby-rails-8/app/handlers/summarize_handler.rb +33 -0
  390. package/templates/stacks/ruby-rails-8/app/handlers/users_handler.rb +68 -0
  391. package/templates/stacks/ruby-rails-8/app/llm/cache/llm_cache.rb +44 -0
  392. package/templates/stacks/ruby-rails-8/app/llm/prompts/prompt_loader.rb +54 -0
  393. package/templates/stacks/ruby-rails-8/app/llm/prompts/summarize_v1.jinja2 +12 -0
  394. package/templates/stacks/ruby-rails-8/app/llm/providers/dummy_provider.rb +35 -0
  395. package/templates/stacks/ruby-rails-8/app/llm/providers/llm_provider.rb +67 -0
  396. package/templates/stacks/ruby-rails-8/app/llm/providers/openai_provider.rb +62 -0
  397. package/templates/stacks/ruby-rails-8/app/llm/rate_limit/token_bucket.rb +82 -0
  398. package/templates/stacks/ruby-rails-8/app/llm/validators/summarize_output_schema.json +21 -0
  399. package/templates/stacks/ruby-rails-8/app/llm/validators/validator.rb +52 -0
  400. package/templates/stacks/ruby-rails-8/app/models/user.rb +36 -0
  401. package/templates/stacks/ruby-rails-8/app/presenters/user_presenter.rb +48 -0
  402. package/templates/stacks/ruby-rails-8/app/repositories/document_repository.rb +57 -0
  403. package/templates/stacks/ruby-rails-8/app/repositories/user_repository.rb +73 -0
  404. package/templates/stacks/ruby-rails-8/app/services/create_user_service.rb +67 -0
  405. package/templates/stacks/ruby-rails-8/app/services/realtime_service.rb +53 -0
  406. package/templates/stacks/ruby-rails-8/app/services/summarize_document_service.rb +57 -0
  407. package/templates/stacks/ruby-rails-8/config/dare.yml +42 -0
  408. package/templates/stacks/ruby-rails-8/config/initializers/dare.rb +31 -0
  409. package/templates/stacks/ruby-rails-8/config/initializers/rack_attack.rb +64 -0
  410. package/templates/stacks/ruby-rails-8/config/initializers/rswag_api.rb +12 -0
  411. package/templates/stacks/ruby-rails-8/lib/tasks/dare.rake +159 -0
  412. package/templates/stacks/ruby-rails-8/llms.txt.erb +69 -0
  413. package/templates/stacks/ruby-rails-8/spec/api/summarize_spec.rb +56 -0
  414. package/templates/stacks/ruby-rails-8/spec/api/users_spec.rb +72 -0
  415. package/templates/stacks/ruby-rails-8/spec/channels/dare_updates_channel_spec.rb +61 -0
  416. package/templates/stacks/ruby-rails-8/spec/channels/user_updates_channel_spec.rb +56 -0
  417. package/templates/stacks/ruby-rails-8/spec/factories/users.rb +27 -0
  418. package/templates/stacks/ruby-rails-8/spec/handlers/users_handler_spec.rb +88 -0
  419. package/templates/stacks/ruby-rails-8/spec/rails_helper.rb +31 -0
  420. package/templates/stacks/ruby-rails-8/spec/services/create_user_service_spec.rb +88 -0
  421. package/templates/stacks/ruby-rails-8/spec/services/summarize_document_service_spec.rb +142 -0
  422. package/templates/stacks/ruby-rails-8/spec/swagger_helper.rb +73 -0
  423. package/templates/stacks/rust-axum/.dare/skills.yml +11 -0
  424. package/templates/stacks/rust-axum/.env.example +26 -0
  425. package/templates/stacks/rust-axum/.github/workflows/dare-ci.yml +40 -0
  426. package/templates/stacks/rust-axum/Cargo.toml.tera +53 -0
  427. package/templates/stacks/rust-axum/README.md.tera +37 -0
  428. package/templates/stacks/rust-axum/gitignore +5 -0
  429. package/templates/stacks/rust-axum/llms.txt.tera +54 -0
  430. package/templates/stacks/rust-axum/migrations/0001_create_users.sql +13 -0
  431. package/templates/stacks/rust-axum/openapi.json.tera +46 -0
  432. package/templates/stacks/rust-axum/src/config.rs +45 -0
  433. package/templates/stacks/rust-axum/src/errors.rs +48 -0
  434. package/templates/stacks/rust-axum/src/handlers/auth.rs +48 -0
  435. package/templates/stacks/rust-axum/src/handlers/mod.rs +3 -0
  436. package/templates/stacks/rust-axum/src/handlers/users.rs +81 -0
  437. package/templates/stacks/rust-axum/src/handlers/ws.rs +24 -0
  438. package/templates/stacks/rust-axum/src/lib.rs +19 -0
  439. package/templates/stacks/rust-axum/src/llm/mod.rs +1 -0
  440. package/templates/stacks/rust-axum/src/llm/provider.rs +48 -0
  441. package/templates/stacks/rust-axum/src/main.rs.tera +64 -0
  442. package/templates/stacks/rust-axum/src/middleware/auth.rs +20 -0
  443. package/templates/stacks/rust-axum/src/middleware/mod.rs +2 -0
  444. package/templates/stacks/rust-axum/src/middleware/rate_limit.rs +27 -0
  445. package/templates/stacks/rust-axum/src/models/mod.rs +1 -0
  446. package/templates/stacks/rust-axum/src/models/user.rs +13 -0
  447. package/templates/stacks/rust-axum/src/repositories/mod.rs +1 -0
  448. package/templates/stacks/rust-axum/src/repositories/user_repository.rs +62 -0
  449. package/templates/stacks/rust-axum/src/services/auth_service.rs +50 -0
  450. package/templates/stacks/rust-axum/src/services/mod.rs +2 -0
  451. package/templates/stacks/rust-axum/src/services/user_service.rs +53 -0
  452. package/templates/stacks/rust-axum/tests/integration_test.rs.tera +13 -0
  453. package/LICENSE +0 -21
@@ -0,0 +1,46 @@
1
+ """Users router: GET /users, POST /users."""
2
+ from fastapi import APIRouter, Depends, HTTPException, Query, status
3
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
4
+ from sqlalchemy.orm import Session
5
+
6
+ from app.core.security import decode_access_token
7
+ from app.db.session import get_db
8
+ from app.repositories.user_repository import UserRepository
9
+ from app.schemas.user import UserCreate, UserOut, UserPage
10
+ from app.services.user_service import EmailAlreadyInUse, UserService
11
+
12
+ router = APIRouter()
13
+ bearer = HTTPBearer(auto_error=True)
14
+
15
+
16
+ def _require_admin(creds: HTTPAuthorizationCredentials) -> None:
17
+ payload = decode_access_token(creds.credentials)
18
+ if not payload or payload.get("role") != "ADMIN":
19
+ raise HTTPException(status.HTTP_403_FORBIDDEN, "admin role required")
20
+
21
+
22
+ @router.get("", response_model=UserPage)
23
+ def list_users(
24
+ page: int = Query(1, ge=1),
25
+ limit: int = Query(20, ge=1, le=100),
26
+ creds: HTTPAuthorizationCredentials = Depends(bearer),
27
+ db: Session = Depends(get_db),
28
+ ) -> UserPage:
29
+ payload = decode_access_token(creds.credentials)
30
+ if not payload:
31
+ raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Invalid token")
32
+ return UserService(UserRepository(db)).list(page, limit)
33
+
34
+
35
+ @router.post("", response_model=UserOut, status_code=status.HTTP_201_CREATED)
36
+ def create_user(
37
+ dto: UserCreate,
38
+ creds: HTTPAuthorizationCredentials = Depends(bearer),
39
+ db: Session = Depends(get_db),
40
+ ) -> UserOut:
41
+ _require_admin(creds)
42
+ service = UserService(UserRepository(db))
43
+ try:
44
+ return service.create(dto)
45
+ except EmailAlreadyInUse as exc:
46
+ raise HTTPException(status.HTTP_409_CONFLICT, "email already in use") from exc
@@ -0,0 +1,56 @@
1
+ """Pydantic IN/OUT schemas for user resource."""
2
+ import re
3
+ import uuid
4
+ from datetime import datetime
5
+ from typing import Annotated, Literal
6
+
7
+ from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
8
+
9
+
10
+ class LoginIn(BaseModel):
11
+ email: EmailStr
12
+ password: Annotated[str, Field(min_length=8)]
13
+
14
+ @field_validator("password")
15
+ @classmethod
16
+ def password_complexity(cls, v: str) -> str:
17
+ if not re.search(r"[A-Z]", v):
18
+ raise ValueError("password must contain an uppercase letter")
19
+ if not re.search(r"\d", v):
20
+ raise ValueError("password must contain a digit")
21
+ return v
22
+
23
+
24
+ class LoginOut(BaseModel):
25
+ access_token: str
26
+ expires_in: int
27
+ token_type: Literal["bearer"] = "bearer"
28
+
29
+
30
+ class UserOut(BaseModel):
31
+ model_config = ConfigDict(from_attributes=True)
32
+ id: uuid.UUID
33
+ email: EmailStr
34
+ role: str
35
+ created_at: datetime
36
+
37
+
38
+ class UserCreate(BaseModel):
39
+ email: EmailStr
40
+ password: Annotated[str, Field(min_length=8)]
41
+ role: Literal["USER", "ADMIN"] = "USER"
42
+
43
+ @field_validator("password")
44
+ @classmethod
45
+ def password_complexity(cls, v: str) -> str:
46
+ if not re.search(r"[A-Z]", v):
47
+ raise ValueError("password must contain an uppercase letter")
48
+ if not re.search(r"\d", v):
49
+ raise ValueError("password must contain a digit")
50
+ return v
51
+
52
+
53
+ class UserPage(BaseModel):
54
+ items: list[UserOut]
55
+ total: int
56
+ page: int
@@ -0,0 +1,22 @@
1
+ """Authentication service — login flow."""
2
+ from app.core.security import create_access_token, verify_password
3
+ from app.repositories.user_repository import UserRepository
4
+ from app.schemas.user import LoginIn, LoginOut
5
+
6
+
7
+ class InvalidCredentials(Exception):
8
+ """Raised when email or password don't match."""
9
+
10
+
11
+ class AuthService:
12
+ def __init__(self, repo: UserRepository) -> None:
13
+ self.repo = repo
14
+
15
+ def login(self, dto: LoginIn) -> LoginOut:
16
+ user = self.repo.find_by_email(dto.email.lower().strip())
17
+ if user is None or not verify_password(dto.password, user.password):
18
+ raise InvalidCredentials
19
+ token, expires_in = create_access_token(
20
+ str(user.id), claims={"email": user.email, "role": user.role}
21
+ )
22
+ return LoginOut(access_token=token, expires_in=expires_in)
@@ -0,0 +1,31 @@
1
+ """User service — create/list business logic."""
2
+ from app.core.security import hash_password
3
+ from app.repositories.user_repository import UserRepository
4
+ from app.schemas.user import UserCreate, UserOut, UserPage
5
+
6
+
7
+ class EmailAlreadyInUse(Exception):
8
+ """Raised when a user with the same email already exists."""
9
+
10
+
11
+ class UserService:
12
+ def __init__(self, repo: UserRepository) -> None:
13
+ self.repo = repo
14
+
15
+ def list(self, page: int, limit: int) -> UserPage:
16
+ items, total = self.repo.page(page, limit)
17
+ return UserPage(
18
+ items=[UserOut.model_validate(u) for u in items],
19
+ total=total,
20
+ page=max(1, page),
21
+ )
22
+
23
+ def create(self, dto: UserCreate) -> UserOut:
24
+ if self.repo.find_by_email(dto.email.lower().strip()) is not None:
25
+ raise EmailAlreadyInUse
26
+ created = self.repo.create(
27
+ email=dto.email.lower().strip(),
28
+ password=hash_password(dto.password),
29
+ role=dto.role,
30
+ )
31
+ return UserOut.model_validate(created)
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ .venv/
5
+ .env
6
+ .env.local
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ .coverage
10
+ dist/
11
+ *.egg-info/
12
+ .DS_Store
@@ -0,0 +1,53 @@
1
+ # {{ projectName }}
2
+
3
+ DARE-shaped FastAPI 0.115+ API. Layered Design: Router → Service → Repository → Model.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ cp .env.example .env
9
+ docker compose up -d postgres
10
+ python -m venv .venv
11
+ source .venv/bin/activate
12
+ pip install -e ".[dev]"
13
+ alembic upgrade head
14
+ uvicorn app.main:app --reload
15
+ ```
16
+
17
+ ## Commands
18
+
19
+ - `uvicorn app.main:app --reload` — dev server (port 8000)
20
+ - `pytest` — pytest suite
21
+ - `ruff check .` — lint
22
+ - `pip-audit` — dependency audit
23
+ - `alembic revision --autogenerate -m "..."` — new migration
24
+ - `alembic upgrade head` — apply migrations
25
+
26
+ ## Endpoints
27
+
28
+ | Method | Path | Auth | Description |
29
+ |---|---|---|---|
30
+ | POST | `/auth/login` | none | Exchange email+password for JWT |
31
+ | GET | `/auth/me` | Bearer | Current user info |
32
+ | GET | `/users` | Bearer | List users (query: page, limit) |
33
+ | POST | `/users` | Bearer (admin) | Create user |
34
+ | GET | `/openapi.json` | none | OpenAPI 3.1 document |
35
+ | GET | `/docs` | none | Swagger UI |
36
+
37
+ ## Layered Design
38
+
39
+ - `app/routers/` — HTTP layer (FastAPI APIRouter)
40
+ - `app/services/` — business logic
41
+ - `app/repositories/` — DB queries via SQLAlchemy
42
+ - `app/models/` — SQLAlchemy declarative
43
+ - `app/schemas/` — Pydantic IN/OUT
44
+
45
+ Routers never touch SQLAlchemy directly — they call services which call repositories.
46
+
47
+ ## Env vars
48
+
49
+ See `.env.example`. Required: `DATABASE_URL`, `JWT_SECRET`.
50
+
51
+ ## DARE skills
52
+
53
+ See `.dare/skills.yml`.
@@ -0,0 +1,43 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "{{ projectName }}",
5
+ "version": "0.1.0",
6
+ "description": "DARE-shaped FastAPI API"
7
+ },
8
+ "paths": {
9
+ "/auth/login": {
10
+ "post": {
11
+ "summary": "Exchange email+password for JWT",
12
+ "responses": {
13
+ "200": { "description": "JWT issued" },
14
+ "401": { "description": "Invalid credentials" }
15
+ }
16
+ }
17
+ },
18
+ "/auth/me": {
19
+ "get": {
20
+ "summary": "Current user",
21
+ "security": [{ "bearer": [] }],
22
+ "responses": { "200": { "description": "User" }, "401": { "description": "Unauthorized" } }
23
+ }
24
+ },
25
+ "/users": {
26
+ "get": {
27
+ "summary": "List users",
28
+ "security": [{ "bearer": [] }],
29
+ "responses": { "200": { "description": "Page of users" } }
30
+ },
31
+ "post": {
32
+ "summary": "Create user (admin)",
33
+ "security": [{ "bearer": [] }],
34
+ "responses": { "201": { "description": "Created" }, "403": { "description": "Forbidden" } }
35
+ }
36
+ }
37
+ },
38
+ "components": {
39
+ "securitySchemes": {
40
+ "bearer": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,45 @@
1
+ [project]
2
+ name = "{{ projectName }}"
3
+ version = "0.1.0"
4
+ description = "DARE-shaped FastAPI API"
5
+ requires-python = ">=3.11"
6
+ dependencies = [
7
+ "fastapi>=0.115,<1.0",
8
+ "uvicorn[standard]>=0.30,<1.0",
9
+ "pydantic[email]>=2.7,<3.0",
10
+ "pydantic-settings>=2.4,<3.0",
11
+ "sqlalchemy>=2.0,<3.0",
12
+ "alembic>=1.13,<2.0",
13
+ "psycopg[binary]>=3.2,<4.0",
14
+ "python-jose[cryptography]>=3.3,<4.0",
15
+ "passlib[bcrypt]>=1.7,<2.0",
16
+ "slowapi>=0.1.9,<0.2.0",
17
+ "python-multipart>=0.0.9",
18
+ ]
19
+
20
+ [project.optional-dependencies]
21
+ dev = [
22
+ "pytest>=8.2,<9.0",
23
+ "pytest-asyncio>=0.23,<1.0",
24
+ "httpx>=0.27,<1.0",
25
+ "ruff>=0.5,<1.0",
26
+ "pip-audit>=2.7,<3.0",
27
+ ]
28
+
29
+ [build-system]
30
+ requires = ["hatchling"]
31
+ build-backend = "hatchling.build"
32
+
33
+ [tool.hatch.build.targets.wheel]
34
+ packages = ["app"]
35
+
36
+ [tool.ruff]
37
+ line-length = 100
38
+ target-version = "py311"
39
+
40
+ [tool.ruff.lint]
41
+ select = ["E", "F", "I", "B", "UP"]
42
+ ignore = ["E501"]
43
+
44
+ [tool.pytest.ini_options]
45
+ asyncio_mode = "auto"
@@ -0,0 +1,22 @@
1
+ """Smoke tests for auth router using httpx + FastAPI's TestClient."""
2
+ from fastapi.testclient import TestClient
3
+
4
+ from app.main import app
5
+
6
+ client = TestClient(app)
7
+
8
+
9
+ def test_healthz_returns_ok() -> None:
10
+ res = client.get("/healthz")
11
+ assert res.status_code == 200
12
+ assert res.json() == {"status": "ok"}
13
+
14
+
15
+ def test_login_rejects_bad_email() -> None:
16
+ res = client.post("/auth/login", json={"email": "not-an-email", "password": "Str0ngPass"})
17
+ assert res.status_code == 422
18
+
19
+
20
+ def test_login_rejects_short_password() -> None:
21
+ res = client.post("/auth/login", json={"email": "u@example.com", "password": "short"})
22
+ assert res.status_code == 422
@@ -0,0 +1,50 @@
1
+ # DARE v3.0 — Skills manifest
2
+ # Lists which DARE skills are active in this project.
3
+ # Used by: dare validate, rake dare:metrics, CI checks
4
+
5
+ version: "3.0"
6
+ stack: ruby-rails-8
7
+
8
+ skills:
9
+ dare-ax:
10
+ version: "1.0"
11
+ enabled: true
12
+ artifacts:
13
+ - llms.txt
14
+ - public/openapi.json
15
+
16
+ dare-layered-design:
17
+ version: "1.0"
18
+ enabled: true
19
+ directories:
20
+ - app/handlers
21
+ - app/services
22
+ - app/repositories
23
+ - app/models
24
+ - app/presenters
25
+
26
+ dare-llm-integration:
27
+ version: "1.0"
28
+ enabled: true
29
+ directories:
30
+ - app/llm/providers
31
+ - app/llm/prompts
32
+ - app/llm/validators
33
+ - app/llm/cache
34
+ - app/llm/rate_limit
35
+
36
+ dare-realtime:
37
+ version: "1.0"
38
+ enabled: true
39
+ directories:
40
+ - app/channels
41
+
42
+ dare-quality-telemetry:
43
+ version: "1.0"
44
+ enabled: true
45
+ rake_task: dare:metrics
46
+ metrics:
47
+ - M-01 # llms.txt exists
48
+ - M-02 # OpenAPI spec exists
49
+ - M-03 # CLI --json support
50
+ - M-04 # Rate limiting configured
@@ -0,0 +1,20 @@
1
+ # Application
2
+ RAILS_ENV=development
3
+ PORT=3000
4
+ LOG_LEVEL=info
5
+
6
+ # Database
7
+ DATABASE_URL=postgresql://user:pass@localhost:5432/myapp_development
8
+
9
+ # Rails master key lives in config/master.key (NOT here) — never commit it.
10
+ # SECRET_KEY_BASE is derived from credentials in production.
11
+
12
+ # Action Cable / Redis
13
+ REDIS_URL=redis://localhost:6379/1
14
+
15
+ # Rate limit (rack-attack)
16
+ RATE_LIMIT_PER_MIN=60
17
+
18
+ # LLM provider (optional)
19
+ LLM_PROVIDER=dummy
20
+ # OPENAI_API_KEY=
@@ -0,0 +1,112 @@
1
+ # DARE v3.1 — CI/CD Pipeline for Rails 8 stack
2
+ # Runs on every push and pull_request
3
+ # Validates: bundle audit + rubocop + rspec + dare:metrics (M-01 to M-04)
4
+
5
+ name: DARE CI
6
+
7
+ on:
8
+ push:
9
+ branches: ["**"]
10
+ pull_request:
11
+ branches: ["**"]
12
+
13
+ jobs:
14
+ audit:
15
+ name: Audit dependencies
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: "3.3"
22
+ bundler-cache: true
23
+ - name: bundle audit
24
+ run: |
25
+ gem install bundler-audit
26
+ bundle audit check --update
27
+
28
+ lint:
29
+ name: Lint (RuboCop)
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ - uses: ruby/setup-ruby@v1
34
+ with:
35
+ ruby-version: "3.3"
36
+ bundler-cache: true
37
+ - name: rubocop
38
+ run: bundle exec rubocop
39
+
40
+ test:
41
+ name: Test (Ruby ${{ matrix.ruby }})
42
+ runs-on: ubuntu-latest
43
+
44
+ strategy:
45
+ fail-fast: false
46
+ matrix:
47
+ ruby: ["3.3"]
48
+
49
+ services:
50
+ postgres:
51
+ image: postgres:14
52
+ env:
53
+ POSTGRES_USER: postgres
54
+ POSTGRES_PASSWORD: postgres
55
+ POSTGRES_DB: app_test
56
+ options: >-
57
+ --health-cmd pg_isready
58
+ --health-interval 10s
59
+ --health-timeout 5s
60
+ --health-retries 5
61
+ ports:
62
+ - 5432:5432
63
+
64
+ redis:
65
+ image: redis:7
66
+ options: >-
67
+ --health-cmd "redis-cli ping"
68
+ --health-interval 10s
69
+ --health-timeout 5s
70
+ --health-retries 5
71
+ ports:
72
+ - 6379:6379
73
+
74
+ env:
75
+ RAILS_ENV: test
76
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/app_test
77
+ REDIS_URL: redis://localhost:6379/0
78
+ RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
79
+
80
+ steps:
81
+ - name: Checkout code
82
+ uses: actions/checkout@v4
83
+
84
+ - name: Set up Ruby ${{ matrix.ruby }}
85
+ uses: ruby/setup-ruby@v1
86
+ with:
87
+ ruby-version: ${{ matrix.ruby }}
88
+ bundler-cache: true
89
+
90
+ - name: Set up database
91
+ run: bundle exec rails db:create db:migrate RAILS_ENV=test
92
+
93
+ - name: Run RSpec
94
+ run: bundle exec rspec --format progress --format RspecJunitFormatter --out tmp/rspec.xml
95
+ continue-on-error: false
96
+
97
+ - name: Run DARE metrics validation
98
+ run: bundle exec rake dare:metrics
99
+
100
+ - name: Upload test results
101
+ if: always()
102
+ uses: actions/upload-artifact@v4
103
+ with:
104
+ name: rspec-results
105
+ path: tmp/rspec.xml
106
+
107
+ - name: Upload DARE metrics
108
+ if: always()
109
+ uses: actions/upload-artifact@v4
110
+ with:
111
+ name: dare-metrics
112
+ path: tmp/dare_metrics.json
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ ruby ">= 3.3.0"
6
+
7
+ # Core — Rails 8 Omakase
8
+ gem "rails", "~> 8.0"
9
+ gem "puma", ">= 5.0"
10
+ gem "bootsnap", require: false
11
+ gem "tzinfo-data", platforms: %i[windows jruby]
12
+
13
+ # Database
14
+ gem "pg", "~> 1.1"
15
+
16
+ # Solid Suite (Rails 8 Omakase)
17
+ gem "solid_cache"
18
+ gem "solid_queue"
19
+
20
+ # Real-time
21
+ gem "redis", "~> 5.0"
22
+ gem "redis-actioncable", require: false
23
+
24
+ # OpenAPI — single source of truth via rswag
25
+ gem "rswag-api"
26
+ gem "rswag-ui"
27
+
28
+ # Rate limiting
29
+ gem "rack-attack"
30
+
31
+ # JSON Schema validation
32
+ gem "json_schemer"
33
+
34
+ # HTTP client for LLM providers
35
+ gem "faraday", "~> 2.0"
36
+ gem "faraday-retry"
37
+
38
+ # Background processing
39
+ gem "solid_queue"
40
+
41
+ group :development, :test do
42
+ gem "debug", platforms: %i[mri windows linux]
43
+ gem "rspec-rails", "~> 6.0"
44
+ gem "factory_bot_rails", "~> 6.0"
45
+ gem "faker", "~> 3.0"
46
+ gem "rswag-specs"
47
+ gem "shoulda-matchers", "~> 5.0"
48
+ gem "simplecov", require: false
49
+ end
50
+
51
+ group :development do
52
+ gem "web-console"
53
+ gem "rack-mini-profiler"
54
+ gem "rubocop-rails-omakase", require: false
55
+ end
56
+
57
+ group :test do
58
+ gem "database_cleaner-active_record"
59
+ gem "webmock"
60
+ gem "vcr"
61
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — ApplicationCable::Channel base
4
+ # All channels inherit from this; subscription authorization happens here
5
+
6
+ module ApplicationCable
7
+ class Channel < ActionCable::Channel::Base
8
+ # Convenience: access current_user from connection in all channels
9
+ delegate :current_user, to: :connection
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — Action Cable Connection with cookie-based auth
4
+ # Convention (ADR-03): Every connection validates authentication
5
+ # Unauthorized connections are rejected at the connection level
6
+
7
+ module ApplicationCable
8
+ class Connection < ActionCable::Connection::Base
9
+ identified_by :current_user
10
+
11
+ def connect
12
+ self.current_user = find_verified_user
13
+ end
14
+
15
+ private
16
+
17
+ def find_verified_user
18
+ # Strategy 1: cookie-based session (most common for Rails apps)
19
+ if (user_id = cookies.signed[:user_id])
20
+ user = User.find_by(id: user_id)
21
+ return user if user&.active?
22
+ end
23
+
24
+ # Strategy 2: token-based (for mobile/API clients)
25
+ if (token = request.params[:token].presence || env["HTTP_X_ACTION_CABLE_TOKEN"])
26
+ # Implement your token lookup here:
27
+ # user = User.find_by_auth_token(token)
28
+ # return user if user&.active?
29
+ end
30
+
31
+ reject_unauthorized_connection
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — DareUpdatesChannel
4
+ # Convention (ADR-03): subscriptions authorize before streaming
5
+ # This is a generic channel for DARE system events (metrics, deploys, etc.)
6
+
7
+ class DareUpdatesChannel < ApplicationCable::Channel
8
+ def subscribed
9
+ # Reject if not authenticated (connection already checks, but belt-and-suspenders)
10
+ reject_subscription unless current_user
11
+
12
+ stream_from "dare_updates:#{current_user.id}"
13
+ end
14
+
15
+ def unsubscribed
16
+ stop_all_streams
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # DARE v3.0 — UserUpdatesChannel
4
+ # Convention (ADR-03): per-user channel with authorization check
5
+ # Frontend subscribes with: consumer.subscriptions.create({ channel: "UserUpdatesChannel", user_id: 123 })
6
+
7
+ class UserUpdatesChannel < ApplicationCable::Channel
8
+ def subscribed
9
+ user_id = params[:user_id].to_i
10
+
11
+ # Authorization: only subscribe to your own updates (or admin)
12
+ unless current_user.can_view?(user_id)
13
+ reject_subscription
14
+ return
15
+ end
16
+
17
+ stream_for user_id
18
+ end
19
+
20
+ def unsubscribed
21
+ stop_all_streams
22
+ end
23
+ end