create-rigel 0.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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/cli.js +122 -0
  4. package/package.json +47 -0
  5. package/templates/express/.claude/CLAUDE.md +114 -0
  6. package/templates/express/.claude/agents/arch-validator.md +85 -0
  7. package/templates/express/.claude/agents/db-optimizer.md +72 -0
  8. package/templates/express/.claude/agents/doc-gardener.md +67 -0
  9. package/templates/express/.claude/agents/garbage-collector.md +94 -0
  10. package/templates/express/.claude/agents/gate-checker.md +176 -0
  11. package/templates/express/.claude/agents/reviewer.md +84 -0
  12. package/templates/express/.claude/agents/security-auditor.md +82 -0
  13. package/templates/express/.claude/hooks/post-write.sh +93 -0
  14. package/templates/express/.claude/rules/api.md +149 -0
  15. package/templates/express/.claude/rules/architecture.md +78 -0
  16. package/templates/express/.claude/rules/database.md +104 -0
  17. package/templates/express/.claude/rules/jobs.md +81 -0
  18. package/templates/express/.claude/rules/security.md +92 -0
  19. package/templates/express/.claude/rules/testing.md +97 -0
  20. package/templates/express/.claude/settings.json +35 -0
  21. package/templates/express/.claude/skills/00-infra-setup/SKILL.md +479 -0
  22. package/templates/express/.claude/skills/01-write-roadmap/SKILL.md +72 -0
  23. package/templates/express/.claude/skills/02-write-spec/SKILL.md +119 -0
  24. package/templates/express/.claude/skills/03-write-plan/SKILL.md +124 -0
  25. package/templates/express/.claude/skills/04-build-layer/SKILL.md +216 -0
  26. package/templates/express/.claude/skills/05-validate-layer/SKILL.md +43 -0
  27. package/templates/express/.claude/skills/06-push-layer/SKILL.md +59 -0
  28. package/templates/express/.claude/skills/07-layer-check/SKILL.md +62 -0
  29. package/templates/express/.claude/skills/08-garbage-collect/SKILL.md +86 -0
  30. package/templates/express/.claude/skills/09-doc-garden/SKILL.md +64 -0
  31. package/templates/express/.claude/skills/10-db-optimize/SKILL.md +96 -0
  32. package/templates/express/.claude/skills/11-load-test/SKILL.md +77 -0
  33. package/templates/express/.dockerignore +43 -0
  34. package/templates/express/.env.example +42 -0
  35. package/templates/express/.github/CODEOWNERS +22 -0
  36. package/templates/express/.github/dependabot.yml +48 -0
  37. package/templates/express/.github/pull_request_template.md +58 -0
  38. package/templates/express/.github/workflows/ci.yml +278 -0
  39. package/templates/express/.github/workflows/load-test.yml +41 -0
  40. package/templates/express/.gitleaks.toml +18 -0
  41. package/templates/express/.lintstagedrc.json +3 -0
  42. package/templates/express/.nvmrc +1 -0
  43. package/templates/express/.prettierrc +8 -0
  44. package/templates/express/.sequelizerc +8 -0
  45. package/templates/express/AGENTS.md +94 -0
  46. package/templates/express/ARCHITECTURE.md +161 -0
  47. package/templates/express/Dockerfile +40 -0
  48. package/templates/express/Makefile +79 -0
  49. package/templates/express/QUICKSTART.md +131 -0
  50. package/templates/express/docker-compose.yml +114 -0
  51. package/templates/express/docs/PLANS.md +111 -0
  52. package/templates/express/docs/QUALITY_SCORE.md +66 -0
  53. package/templates/express/docs/design-docs/core-beliefs.md +89 -0
  54. package/templates/express/docs/design-docs/decisions/ADR-000-infrastructure.md +70 -0
  55. package/templates/express/docs/design-docs/decisions/ADR-001-observability.md +82 -0
  56. package/templates/express/docs/design-docs/decisions/index.md +23 -0
  57. package/templates/express/docs/design-docs/index.md +11 -0
  58. package/templates/express/docs/design-docs/observability.md +194 -0
  59. package/templates/express/docs/design-docs/team-workflow.md +64 -0
  60. package/templates/express/docs/exec-plans/active/.gitkeep +0 -0
  61. package/templates/express/docs/exec-plans/completed/.gitkeep +0 -0
  62. package/templates/express/docs/exec-plans/tech-debt-tracker.md +31 -0
  63. package/templates/express/docs/generated/.gitkeep +0 -0
  64. package/templates/express/docs/product-specs/ROADMAP.md +49 -0
  65. package/templates/express/docs/product-specs/draft/.gitkeep +0 -0
  66. package/templates/express/docs/product-specs/index.md +21 -0
  67. package/templates/express/docs/product-specs/ready/.gitkeep +0 -0
  68. package/templates/express/eslint.config.mjs +94 -0
  69. package/templates/express/gitignore +36 -0
  70. package/templates/express/infra/monitoring/alloy/config.alloy +59 -0
  71. package/templates/express/jest.config.ts +61 -0
  72. package/templates/express/package.json +45 -0
  73. package/templates/express/scripts/openapi.export.ts +77 -0
  74. package/templates/express/tests/architecture/isolation.test.ts +51 -0
  75. package/templates/express/tests/architecture/layers.test.ts +83 -0
  76. package/templates/express/tests/integration/health.test.ts +40 -0
  77. package/templates/express/tests/integration/isolation.test.template.ts +88 -0
  78. package/templates/express/tests/load/smoke.js +46 -0
  79. package/templates/express/tests/load/soak.js +45 -0
  80. package/templates/express/tests/load/stress.js +44 -0
  81. package/templates/express/tests/unit/utils/response.util.test.ts +53 -0
  82. package/templates/express/tsconfig.build.json +8 -0
  83. package/templates/express/tsconfig.json +20 -0
  84. package/templates/fastapi/.claude/CLAUDE.md +122 -0
  85. package/templates/fastapi/.claude/agents/arch-validator.md +64 -0
  86. package/templates/fastapi/.claude/agents/db-optimizer.md +59 -0
  87. package/templates/fastapi/.claude/agents/doc-gardener.md +55 -0
  88. package/templates/fastapi/.claude/agents/garbage-collector.md +74 -0
  89. package/templates/fastapi/.claude/agents/gate-checker.md +133 -0
  90. package/templates/fastapi/.claude/agents/reviewer.md +74 -0
  91. package/templates/fastapi/.claude/agents/security-auditor.md +69 -0
  92. package/templates/fastapi/.claude/hooks/post-write.sh +65 -0
  93. package/templates/fastapi/.claude/rules/api.md +119 -0
  94. package/templates/fastapi/.claude/rules/architecture.md +84 -0
  95. package/templates/fastapi/.claude/rules/database.md +153 -0
  96. package/templates/fastapi/.claude/rules/jobs.md +85 -0
  97. package/templates/fastapi/.claude/rules/security.md +102 -0
  98. package/templates/fastapi/.claude/rules/testing.md +207 -0
  99. package/templates/fastapi/.claude/settings.json +50 -0
  100. package/templates/fastapi/.claude/skills/00-infra-setup/SKILL.md +311 -0
  101. package/templates/fastapi/.claude/skills/01-write-roadmap/SKILL.md +61 -0
  102. package/templates/fastapi/.claude/skills/02-write-spec/SKILL.md +82 -0
  103. package/templates/fastapi/.claude/skills/03-write-plan/SKILL.md +83 -0
  104. package/templates/fastapi/.claude/skills/04-build-layer/SKILL.md +170 -0
  105. package/templates/fastapi/.claude/skills/05-validate-layer/SKILL.md +20 -0
  106. package/templates/fastapi/.claude/skills/06-push-layer/SKILL.md +31 -0
  107. package/templates/fastapi/.claude/skills/07-layer-check/SKILL.md +51 -0
  108. package/templates/fastapi/.claude/skills/08-garbage-collect/SKILL.md +47 -0
  109. package/templates/fastapi/.claude/skills/09-doc-garden/SKILL.md +9 -0
  110. package/templates/fastapi/.claude/skills/10-db-optimize/SKILL.md +53 -0
  111. package/templates/fastapi/.claude/skills/11-load-test/SKILL.md +81 -0
  112. package/templates/fastapi/.dockerignore +30 -0
  113. package/templates/fastapi/.github/dependabot.yml +24 -0
  114. package/templates/fastapi/AGENTS.md +87 -0
  115. package/templates/fastapi/ARCHITECTURE.md +141 -0
  116. package/templates/fastapi/Makefile +42 -0
  117. package/templates/fastapi/README.md +179 -0
  118. package/templates/fastapi/docs/PLANS.md +26 -0
  119. package/templates/fastapi/docs/QUALITY_SCORE.md +26 -0
  120. package/templates/fastapi/docs/design-docs/core-beliefs.md +46 -0
  121. package/templates/fastapi/docs/design-docs/decisions/ADR-000-infrastructure.md +79 -0
  122. package/templates/fastapi/docs/design-docs/decisions/index.md +7 -0
  123. package/templates/fastapi/docs/design-docs/index.md +9 -0
  124. package/templates/fastapi/docs/exec-plans/active/.gitkeep +0 -0
  125. package/templates/fastapi/docs/exec-plans/completed/.gitkeep +0 -0
  126. package/templates/fastapi/docs/exec-plans/tech-debt-tracker.md +19 -0
  127. package/templates/fastapi/docs/generated/.gitkeep +0 -0
  128. package/templates/fastapi/docs/product-specs/ROADMAP.md +49 -0
  129. package/templates/fastapi/docs/product-specs/draft/.gitkeep +0 -0
  130. package/templates/fastapi/docs/product-specs/index.md +18 -0
  131. package/templates/fastapi/docs/product-specs/ready/.gitkeep +0 -0
  132. package/templates/fastapi/gitignore +46 -0
  133. package/templates/fastapi/scripts/gate.sh +68 -0
  134. package/templates/nestjs/.claude/CLAUDE.md +121 -0
  135. package/templates/nestjs/.claude/agents/db-optimizer.md +51 -0
  136. package/templates/nestjs/.claude/agents/doc-gardener.md +45 -0
  137. package/templates/nestjs/.claude/agents/garbage-collector.md +50 -0
  138. package/templates/nestjs/.claude/agents/gate-checker.md +127 -0
  139. package/templates/nestjs/.claude/agents/reviewer.md +68 -0
  140. package/templates/nestjs/.claude/agents/security-auditor.md +72 -0
  141. package/templates/nestjs/.claude/hooks/post-write.sh +91 -0
  142. package/templates/nestjs/.claude/rules/api.md +87 -0
  143. package/templates/nestjs/.claude/rules/architecture.md +104 -0
  144. package/templates/nestjs/.claude/rules/database.md +111 -0
  145. package/templates/nestjs/.claude/rules/security.md +110 -0
  146. package/templates/nestjs/.claude/rules/testing.md +145 -0
  147. package/templates/nestjs/.claude/settings.json +33 -0
  148. package/templates/nestjs/.claude/skills/00-infra-setup/SKILL.md +222 -0
  149. package/templates/nestjs/.claude/skills/01-write-spec/SKILL.md +25 -0
  150. package/templates/nestjs/.claude/skills/02-write-plan/SKILL.md +18 -0
  151. package/templates/nestjs/.claude/skills/03-build-layer/SKILL.md +242 -0
  152. package/templates/nestjs/.claude/skills/04-validate-layer/SKILL.md +4 -0
  153. package/templates/nestjs/.claude/skills/05-push-layer/SKILL.md +6 -0
  154. package/templates/nestjs/.claude/skills/06-layer-check/SKILL.md +12 -0
  155. package/templates/nestjs/.claude/skills/07-garbage-collect/SKILL.md +2 -0
  156. package/templates/nestjs/.claude/skills/08-doc-garden/SKILL.md +4 -0
  157. package/templates/nestjs/.claude/skills/09-api-sync/SKILL.md +36 -0
  158. package/templates/nestjs/.claude/skills/10-load-test/SKILL.md +29 -0
  159. package/templates/nestjs/.env.example +11 -0
  160. package/templates/nestjs/AGENTS.md +88 -0
  161. package/templates/nestjs/ARCHITECTURE.md +192 -0
  162. package/templates/nestjs/docs/PLANS.md +22 -0
  163. package/templates/nestjs/docs/QUALITY_SCORE.md +27 -0
  164. package/templates/nestjs/docs/design-docs/core-beliefs.md +18 -0
  165. package/templates/nestjs/docs/design-docs/decisions/ADR-000-infrastructure.md +58 -0
  166. package/templates/nestjs/docs/design-docs/decisions/index.md +7 -0
  167. package/templates/nestjs/docs/design-docs/index.md +7 -0
  168. package/templates/nestjs/docs/exec-plans/active/.gitkeep +0 -0
  169. package/templates/nestjs/docs/exec-plans/completed/.gitkeep +0 -0
  170. package/templates/nestjs/docs/exec-plans/tech-debt-tracker.md +16 -0
  171. package/templates/nestjs/docs/generated/.gitkeep +0 -0
  172. package/templates/nestjs/docs/product-specs/draft/.gitkeep +0 -0
  173. package/templates/nestjs/docs/product-specs/index.md +7 -0
  174. package/templates/nestjs/docs/product-specs/ready/.gitkeep +0 -0
  175. package/templates/nestjs/gitignore +8 -0
  176. package/templates/nextjs/.claude/CLAUDE.md +139 -0
  177. package/templates/nextjs/.claude/agents/arch-validator.md +66 -0
  178. package/templates/nextjs/.claude/agents/contract-checker.md +78 -0
  179. package/templates/nextjs/.claude/agents/doc-gardener.md +52 -0
  180. package/templates/nextjs/.claude/agents/garbage-collector.md +61 -0
  181. package/templates/nextjs/.claude/agents/gate-checker.md +129 -0
  182. package/templates/nextjs/.claude/agents/perf-auditor.md +109 -0
  183. package/templates/nextjs/.claude/agents/reviewer.md +79 -0
  184. package/templates/nextjs/.claude/agents/security-auditor.md +50 -0
  185. package/templates/nextjs/.claude/hooks/post-write.sh +121 -0
  186. package/templates/nextjs/.claude/rules/api-contract.md +166 -0
  187. package/templates/nextjs/.claude/rules/architecture.md +99 -0
  188. package/templates/nextjs/.claude/rules/components.md +122 -0
  189. package/templates/nextjs/.claude/rules/security.md +122 -0
  190. package/templates/nextjs/.claude/rules/testing.md +187 -0
  191. package/templates/nextjs/.claude/scripts/infra-setup.sh +550 -0
  192. package/templates/nextjs/.claude/settings.json +33 -0
  193. package/templates/nextjs/.claude/skills/00-infra-setup/SKILL.md +471 -0
  194. package/templates/nextjs/.claude/skills/01-write-roadmap/SKILL.md +77 -0
  195. package/templates/nextjs/.claude/skills/02-write-spec/SKILL.md +78 -0
  196. package/templates/nextjs/.claude/skills/03-write-plan/SKILL.md +91 -0
  197. package/templates/nextjs/.claude/skills/04-build-layer/SKILL.md +141 -0
  198. package/templates/nextjs/.claude/skills/05-validate-layer/SKILL.md +13 -0
  199. package/templates/nextjs/.claude/skills/06-push-layer/SKILL.md +20 -0
  200. package/templates/nextjs/.claude/skills/07-layer-check/SKILL.md +28 -0
  201. package/templates/nextjs/.claude/skills/08-garbage-collect/SKILL.md +14 -0
  202. package/templates/nextjs/.claude/skills/09-doc-garden/SKILL.md +12 -0
  203. package/templates/nextjs/.claude/skills/10-api-sync/SKILL.md +50 -0
  204. package/templates/nextjs/.claude/skills/11-perf-budget/SKILL.md +51 -0
  205. package/templates/nextjs/.dockerignore +25 -0
  206. package/templates/nextjs/.gitattributes +10 -0
  207. package/templates/nextjs/.github/CODEOWNERS +24 -0
  208. package/templates/nextjs/.github/dependabot.yml +31 -0
  209. package/templates/nextjs/.github/pull_request_template.md +59 -0
  210. package/templates/nextjs/.github/workflows/ci.yml +143 -0
  211. package/templates/nextjs/.github/workflows/lighthouse.yml +54 -0
  212. package/templates/nextjs/.github/workflows/load-test.yml +54 -0
  213. package/templates/nextjs/.husky/pre-commit +1 -0
  214. package/templates/nextjs/.lintstagedrc.json +4 -0
  215. package/templates/nextjs/.mcp.json +8 -0
  216. package/templates/nextjs/.prettierignore +13 -0
  217. package/templates/nextjs/.prettierrc +9 -0
  218. package/templates/nextjs/AGENTS.md +88 -0
  219. package/templates/nextjs/ARCHITECTURE.md +165 -0
  220. package/templates/nextjs/Dockerfile +38 -0
  221. package/templates/nextjs/Makefile +50 -0
  222. package/templates/nextjs/QUICKSTART.md +128 -0
  223. package/templates/nextjs/docs/PLANS.md +23 -0
  224. package/templates/nextjs/docs/QUALITY_SCORE.md +27 -0
  225. package/templates/nextjs/docs/design-docs/core-beliefs.md +43 -0
  226. package/templates/nextjs/docs/design-docs/decisions/ADR-000-infrastructure.md +72 -0
  227. package/templates/nextjs/docs/design-docs/decisions/index.md +7 -0
  228. package/templates/nextjs/docs/design-docs/index.md +7 -0
  229. package/templates/nextjs/docs/design-docs/team-workflow.md +66 -0
  230. package/templates/nextjs/docs/exec-plans/active/.gitkeep +0 -0
  231. package/templates/nextjs/docs/exec-plans/completed/.gitkeep +0 -0
  232. package/templates/nextjs/docs/exec-plans/tech-debt-tracker.md +19 -0
  233. package/templates/nextjs/docs/generated/.gitkeep +0 -0
  234. package/templates/nextjs/docs/product-specs/ROADMAP.md +59 -0
  235. package/templates/nextjs/docs/product-specs/draft/.gitkeep +0 -0
  236. package/templates/nextjs/docs/product-specs/index.md +18 -0
  237. package/templates/nextjs/docs/product-specs/ready/.gitkeep +0 -0
  238. package/templates/nextjs/eslint.config.mjs +150 -0
  239. package/templates/nextjs/gitignore +35 -0
  240. package/templates/nextjs/lighthouserc.js +37 -0
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: security-auditor
3
+ description: OWASP Top 10 security review for NestJS. Run before any PR touching auth, guards, or sensitive data.
4
+ model: claude-sonnet-4-5
5
+ tools: [Read, Bash]
6
+ color: red
7
+ ---
8
+
9
+ You are a senior application security engineer reviewing NestJS code against OWASP Top 10.
10
+
11
+ ## Pre-Review
12
+ ```bash
13
+ git diff main --name-only
14
+ ```
15
+ Read each changed file.
16
+
17
+ ## Checklist
18
+
19
+ ### A01 — Broken Access Control
20
+ - [ ] Global `JwtAuthGuard` is registered in `app.module.ts` (APP_GUARD)
21
+ - [ ] All public routes have `@Public()` decorator
22
+ - [ ] Every repository query has `userId` in WHERE clause
23
+ - [ ] Admin routes have `@Roles('ADMIN')` + `RolesGuard`
24
+ - [ ] No horizontal escalation (user A can't access user B's data)
25
+
26
+ ### A02 — Cryptographic Failures
27
+ - [ ] Passwords: `argon2.hash()` — not bcrypt, not MD5
28
+ - [ ] JWT secret: `config.getOrThrow('JWT_SECRET')` — min 32 chars
29
+ - [ ] No secrets hardcoded anywhere
30
+ - [ ] HTTPS enforced (helmet HSTS header)
31
+
32
+ ### A03 — Injection
33
+ - [ ] All DB via Sequelize ORM or named replacements
34
+ - [ ] No string-interpolated `sequelize.query()`
35
+ - [ ] All inputs through ValidationPipe + class-validator DTOs
36
+
37
+ ### A04 — Insecure Design
38
+ - [ ] Auth endpoints rate-limited (10/min via @nestjs/throttler)
39
+ - [ ] UUIDs (not sequential ints) on user-facing IDs
40
+ - [ ] Duplicate detection before creation
41
+
42
+ ### A05 — Security Misconfiguration
43
+ - [ ] Helmet middleware applied globally
44
+ - [ ] CORS configured centrally with explicit origins
45
+ - [ ] No stack traces in error responses (AllExceptionsFilter)
46
+ - [ ] Swagger UI disabled in production
47
+
48
+ ### A07 — Auth Failures
49
+ - [ ] Access token: max 15 min (ConfigService value)
50
+ - [ ] Refresh token: single-use, revoked after use
51
+ - [ ] Token revocation: Redis `revoked:{jti}` checked in JwtStrategy
52
+ - [ ] Login: same error for bad user vs bad password
53
+
54
+ ### A09 — Logging Failures
55
+ - [ ] Auth events logged: login, logout, failed auth
56
+ - [ ] 401/403 logged via LoggingInterceptor
57
+ - [ ] No PII in log values (nestjs-pino redaction)
58
+
59
+ ## Verdict
60
+ ```
61
+ APPROVED — all checks pass.
62
+
63
+ OR
64
+
65
+ CHANGES REQUIRED:
66
+ CRITICAL (block merge):
67
+ 1. [file:line] [problem]
68
+ Fix: [exact code]
69
+
70
+ HIGH (fix before production):
71
+ 2. [file:line] [problem]
72
+ ```
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env bash
2
+ # Fires after every Write/Edit on TypeScript files.
3
+ # Catches NestJS-specific violations immediately.
4
+
5
+ set -euo pipefail
6
+
7
+ FILE=$(cat | python3 -c "
8
+ import sys, json
9
+ try:
10
+ data = json.load(sys.stdin)
11
+ print(data.get('file_path') or data.get('path') or '')
12
+ except:
13
+ print('')
14
+ " 2>/dev/null || echo "")
15
+
16
+ [[ -z "$FILE" ]] && exit 0
17
+ [[ ! -f "$FILE" ]] && exit 0
18
+ [[ ! "$FILE" =~ \.ts$ ]] && exit 0
19
+
20
+ WARNINGS=()
21
+
22
+ # 1. console.log — use Logger from @nestjs/common
23
+ if [[ "$FILE" =~ ^src/ ]]; then
24
+ if grep -qn "console\.\(log\|error\|warn\|info\)" "$FILE" 2>/dev/null; then
25
+ LINE=$(grep -n "console\.\(log\|error\|warn\|info\)" "$FILE" | head -1 | cut -d: -f1)
26
+ WARNINGS+=("⚠ [HOOK] console.log at $FILE:$LINE — use Logger from @nestjs/common or nestjs-pino")
27
+ fi
28
+ fi
29
+
30
+ # 2. process.env — use ConfigService
31
+ if [[ "$FILE" =~ ^src/ ]] && [[ ! "$FILE" =~ src/config/ ]]; then
32
+ if grep -qn "process\.env" "$FILE" 2>/dev/null; then
33
+ LINE=$(grep -n "process\.env" "$FILE" | head -1 | cut -d: -f1)
34
+ WARNINGS+=("⚠ [HOOK] process.env at $FILE:$LINE — inject ConfigService instead")
35
+ fi
36
+ fi
37
+
38
+ # 3. HttpException in service files — use NestJS semantic exceptions
39
+ if [[ "$FILE" =~ \.service\.ts$ ]]; then
40
+ if grep -qn "throw new HttpException\b" "$FILE" 2>/dev/null; then
41
+ LINE=$(grep -n "throw new HttpException" "$FILE" | head -1 | cut -d: -f1)
42
+ WARNINGS+=("⚠ [HOOK] HttpException in service at $FILE:$LINE — use NotFoundException, ConflictException etc.")
43
+ fi
44
+ fi
45
+
46
+ # 4. @InjectModel in service files — inject repository instead
47
+ if [[ "$FILE" =~ \.service\.ts$ ]]; then
48
+ if grep -qn "@InjectModel\b" "$FILE" 2>/dev/null; then
49
+ LINE=$(grep -n "@InjectModel" "$FILE" | head -1 | cut -d: -f1)
50
+ WARNINGS+=("⚠ [HOOK] @InjectModel in service at $FILE:$LINE — inject the repository class, not the model directly")
51
+ fi
52
+ fi
53
+
54
+ # 5. Raw .toJSON() without Zod parse in repository files
55
+ if [[ "$FILE" =~ \.repository\.ts$ ]]; then
56
+ if grep -qn "\.toJSON()" "$FILE" 2>/dev/null; then
57
+ # Check if .parse( is on the same or next line
58
+ if ! grep -qn "Schema\.parse\|\.parse(" "$FILE" 2>/dev/null; then
59
+ WARNINGS+=("⚠ [HOOK] .toJSON() without ZodSchema.parse() in $FILE — validate every DB result")
60
+ fi
61
+ fi
62
+ fi
63
+
64
+ # 6. Business logic in controller files (if/else, calculations)
65
+ if [[ "$FILE" =~ \.controller\.ts$ ]]; then
66
+ if grep -qnE "^\s+(if|else|switch|for|while|const .* = .* \? )" "$FILE" 2>/dev/null; then
67
+ LINE=$(grep -nE "^\s+(if|else|switch|for|while)" "$FILE" | head -1 | cut -d: -f1)
68
+ WARNINGS+=("⚠ [HOOK] Possible business logic in controller at $FILE:$LINE — controllers should delegate to service only")
69
+ fi
70
+ fi
71
+
72
+ # 7. File size > 400 lines
73
+ LINE_COUNT=$(wc -l < "$FILE" 2>/dev/null || echo 0)
74
+ if [[ $LINE_COUNT -gt 400 ]]; then
75
+ WARNINGS+=("⚠ [HOOK] $FILE has $LINE_COUNT lines — exceeds 400-line limit. Split into focused modules.")
76
+ fi
77
+
78
+ # 8. Missing @ApiProperty in DTO files
79
+ if [[ "$FILE" =~ \.dto\.ts$ ]]; then
80
+ PROP_COUNT=$(grep -c "^\s*\(readonly\s\+\)\?[a-zA-Z]" "$FILE" 2>/dev/null || echo 0)
81
+ API_PROP_COUNT=$(grep -c "@ApiProperty" "$FILE" 2>/dev/null || echo 0)
82
+ if [[ $PROP_COUNT -gt 0 ]] && [[ $API_PROP_COUNT -eq 0 ]]; then
83
+ WARNINGS+=("⚠ [HOOK] DTO $FILE has properties without @ApiProperty() — Swagger will be incomplete")
84
+ fi
85
+ fi
86
+
87
+ for w in "${WARNINGS[@]}"; do
88
+ echo "$w"
89
+ done
90
+
91
+ exit 0
@@ -0,0 +1,87 @@
1
+ ---
2
+ paths:
3
+ - "src/**/*.controller.ts"
4
+ ---
5
+
6
+ # API Rules — Auto-injected on controller file edits
7
+
8
+ ## Every Controller Checklist
9
+
10
+ ### Required Class Decorators
11
+ ```typescript
12
+ @ApiTags('Applications') // ← group in Swagger UI
13
+ @ApiBearerAuth() // ← shows auth in Swagger
14
+ @Controller('v1/applications') // ← always under v1/ prefix
15
+ export class ApplicationController {
16
+ constructor(private readonly appService: ApplicationService) {}
17
+ ```
18
+
19
+ ### Required Method Decorators (every route)
20
+ ```typescript
21
+ @Get(':id')
22
+ @ApiOperation({ summary: 'Get application by ID' })
23
+ @ApiOkResponse({ type: ApplicationResponseDto })
24
+ @ApiNotFoundResponse({ description: 'Application not found' })
25
+ findOne(
26
+ @Param('id', ParseUUIDPipe) id: string,
27
+ @CurrentUser() user: JwtPayload,
28
+ ) {
29
+ return this.appService.findOne(user.sub, id) // ← one-liner
30
+ }
31
+ ```
32
+
33
+ ### Rate Limiting Tiers
34
+ ```typescript
35
+ // On controller or method level:
36
+ @Throttle({ default: { limit: 10, ttl: 60000 } }) // Auth: 10/min
37
+ @Throttle({ default: { limit: 60, ttl: 60000 } }) // Public: 60/min
38
+ @Throttle({ default: { limit: 300, ttl: 60000 } }) // User: 300/min (default)
39
+ ```
40
+
41
+ ### Route Versioning (all routes under v1/)
42
+ ```typescript
43
+ // ✅ v1 prefix in controller
44
+ @Controller('v1/applications')
45
+
46
+ // OR use versioning module:
47
+ // main.ts: app.enableVersioning({ type: VersioningType.URI })
48
+ // @Controller({ path: 'applications', version: '1' })
49
+
50
+ // ❌ Never unversioned
51
+ @Controller('applications')
52
+ ```
53
+
54
+ ### Pagination Response
55
+ ```typescript
56
+ @Get()
57
+ @ApiOkResponse({ description: 'Paginated list' })
58
+ list(
59
+ @Query() query: PaginationDto,
60
+ @CurrentUser() user: JwtPayload,
61
+ ) {
62
+ return this.appService.list(user.sub, query.cursor, query.limit)
63
+ }
64
+ // Response: { items: [...], nextCursor: string | null, hasMore: boolean }
65
+ ```
66
+
67
+ ### Public Routes
68
+ ```typescript
69
+ @Public() // ← bypass JWT guard
70
+ @Throttle({ default: { limit: 10, ttl: 60000 } }) // ← strict rate limit on auth
71
+ @Post('register')
72
+ @ApiOperation({ summary: 'Register new account' })
73
+ @ApiCreatedResponse({ type: AuthResultDto })
74
+ register(@Body() dto: RegisterDto) {
75
+ return this.authService.register(dto)
76
+ }
77
+ ```
78
+
79
+ ### Admin Routes
80
+ ```typescript
81
+ @Roles('ADMIN') // ← requires ADMIN role
82
+ @Delete(':id')
83
+ @ApiOperation({ summary: 'Admin: delete any resource' })
84
+ adminDelete(@Param('id', ParseUUIDPipe) id: string) {
85
+ return this.appService.adminDelete(id)
86
+ }
87
+ ```
@@ -0,0 +1,104 @@
1
+ ---
2
+ paths:
3
+ - "src/**/*.ts"
4
+ ---
5
+
6
+ # Architecture Rules — Auto-injected on every src/ file edit
7
+
8
+ ## Import Hierarchy (one-way, enforced)
9
+
10
+ | Your file | May import from |
11
+ |---|---|
12
+ | `*.model.ts` | Types, sequelize-typescript decorators only |
13
+ | `*.dto.ts` | class-validator, @nestjs/swagger, Types only |
14
+ | `*.repository.ts` | Model, Config (ConfigService), Types, Zod |
15
+ | `*.service.ts` | Repository, Config, Types — NO HTTP imports |
16
+ | `*.controller.ts` | Service, DTOs, Common decorators, @nestjs/common |
17
+ | `*.module.ts` | All of the above |
18
+ | `src/common/**` | Config, Types only |
19
+
20
+ ## Model Rules
21
+ ```typescript
22
+ // ✅ Model = schema only
23
+ @Table({ tableName: 'applications', paranoid: true })
24
+ export class Application extends Model {
25
+ @Default(() => newId())
26
+ @Column({ type: DataType.UUID, primaryKey: true })
27
+ id!: string
28
+
29
+ // ❌ Never add methods with business logic to models
30
+ // getActiveStages() { ... } ← WRONG, belongs in service
31
+ }
32
+ ```
33
+
34
+ ## Repository Rules
35
+ ```typescript
36
+ // ✅ Always validate output with Zod
37
+ async findById(id: string, userId: string): Promise<ApplicationRecord> {
38
+ const raw = await this.appModel.findOne({ where: { id, userId } })
39
+ if (!raw) throw new NotFoundException(`Application ${id} not found`)
40
+ return ApplicationSchema.parse(raw.toJSON()) // REQUIRED
41
+ }
42
+
43
+ // ❌ Never return raw Sequelize instances
44
+ async findById(id: string): Promise<Application> {
45
+ return this.appModel.findByPk(id) // WRONG — unvalidated, exposes ORM internals
46
+ }
47
+
48
+ // ✅ Ownership in every query
49
+ where: { id, userId } // userId always from auth context, never from user input
50
+
51
+ // ✅ Cursor pagination on all list methods
52
+ async list(userId: string, cursor?: PageCursor, limit = 20) {
53
+ const where: WhereOptions = { userId }
54
+ if (cursor) {
55
+ where[Op.or] = [
56
+ { createdAt: { [Op.lt]: cursor.createdAt } },
57
+ { createdAt: cursor.createdAt, id: { [Op.lt]: cursor.id } },
58
+ ]
59
+ }
60
+ // ...
61
+ }
62
+ ```
63
+
64
+ ## Service Rules
65
+ ```typescript
66
+ // ✅ Takes typed inputs, returns typed outputs, throws semantic exceptions
67
+ async createApplication(userId: string, dto: CreateApplicationDto): Promise<ApplicationRecord> {
68
+ return this.applicationRepository.create(userId, dto)
69
+ }
70
+
71
+ // ❌ No HTTP types in service
72
+ import { Request } from 'express' // FORBIDDEN
73
+ throw new HttpException('error', 404) // FORBIDDEN — use NotFoundException
74
+ ```
75
+
76
+ ## Controller Rules
77
+ ```typescript
78
+ // ✅ One-liner handlers only
79
+ @Post()
80
+ @ApiOperation({ summary: 'Create application' })
81
+ @ApiCreatedResponse({ type: ApplicationResponseDto })
82
+ create(@Body() dto: CreateApplicationDto, @CurrentUser() user: JwtPayload) {
83
+ return this.applicationService.createApplication(user.sub, dto)
84
+ }
85
+
86
+ // ✅ Swagger on every route — no exceptions
87
+ // Every controller class: @ApiTags('Applications') @ApiBearerAuth()
88
+ // Every handler: @ApiOperation() + @ApiResponse() or @ApiOkResponse()
89
+
90
+ // ❌ Logic in controllers
91
+ create(@Body() dto: CreateApplicationDto) {
92
+ if (dto.stage === 'APPLIED') { ... } // WRONG — belongs in service
93
+ }
94
+ ```
95
+
96
+ ## ConfigService Pattern
97
+ ```typescript
98
+ // ✅ Always inject ConfigService
99
+ constructor(private config: ConfigService) {}
100
+ const jwtSecret = this.config.getOrThrow<string>('JWT_SECRET')
101
+
102
+ // ❌ Never
103
+ process.env.JWT_SECRET
104
+ ```
@@ -0,0 +1,111 @@
1
+ ---
2
+ paths:
3
+ - "src/**/*.repository.ts"
4
+ - "src/**/*.model.ts"
5
+ - "db/migrations/**/*.js"
6
+ ---
7
+
8
+ # Database Rules — Auto-injected on repository/model/migration edits
9
+
10
+ ## The 5 Checks Before Every Repository Method Ships
11
+
12
+ ### 1. Zod Parse on Every Result
13
+ ```typescript
14
+ // ✅ REQUIRED
15
+ const raw = await this.userModel.findOne({ where: { email } })
16
+ if (!raw) return null
17
+ return UserSchema.parse(raw.toJSON())
18
+
19
+ // ❌ FORBIDDEN
20
+ return raw // Sequelize instance — unvalidated
21
+ return raw.toJSON() as UserRecord // cast without validation
22
+ ```
23
+
24
+ ### 2. No N+1 — Eager Load Always
25
+ ```typescript
26
+ // ✅ Single query with include
27
+ const apps = await this.appModel.findAll({
28
+ where: { userId },
29
+ include: [
30
+ { model: Note },
31
+ { model: Contact },
32
+ ],
33
+ })
34
+
35
+ // ❌ N+1
36
+ const apps = await this.appModel.findAll({ where: { userId } })
37
+ for (const app of apps) {
38
+ app.notes = await this.noteModel.findAll({ where: { applicationId: app.id } })
39
+ }
40
+ ```
41
+
42
+ ### 3. Cursor Pagination on All List Methods
43
+ ```typescript
44
+ // ✅ Cursor-based
45
+ async list(userId: string, cursor?: PageCursor, limit = 20) {
46
+ const where: WhereOptions = { userId }
47
+ if (cursor) {
48
+ where[Op.or as symbol] = [
49
+ { createdAt: { [Op.lt]: cursor.createdAt } },
50
+ { createdAt: cursor.createdAt, id: { [Op.lt]: cursor.id } },
51
+ ]
52
+ }
53
+ const rows = await this.appModel.findAll({
54
+ where, include: [...],
55
+ order: [['createdAt', 'DESC'], ['id', 'DESC']],
56
+ limit: limit + 1,
57
+ })
58
+ const hasMore = rows.length > limit
59
+ const items = hasMore ? rows.slice(0, limit) : rows
60
+ return {
61
+ items: items.map(r => ApplicationSchema.parse(r.toJSON())),
62
+ nextCursor: hasMore ? { id: items.at(-1)!.id, createdAt: items.at(-1)!.createdAt } : null,
63
+ hasMore,
64
+ }
65
+ }
66
+
67
+ // ❌ FORBIDDEN on growing tables
68
+ findAll({ offset: page * limit, limit })
69
+ ```
70
+
71
+ ### 4. Transactions for Multi-Table Writes
72
+ ```typescript
73
+ // ✅ Inject Sequelize and use transaction
74
+ constructor(
75
+ @InjectModel(Application) private appModel: typeof Application,
76
+ @InjectConnection() private sequelize: Sequelize,
77
+ ) {}
78
+
79
+ async updateStage(id: string, stage: string, note: string) {
80
+ return this.sequelize.transaction(async (t) => {
81
+ await Application.update({ stage }, { where: { id }, transaction: t })
82
+ await Note.create({ applicationId: id, body: note, type: 'SYSTEM' }, { transaction: t })
83
+ })
84
+ }
85
+ ```
86
+
87
+ ### 5. Soft Delete — paranoid: true
88
+ ```typescript
89
+ // ✅ @Table decorator
90
+ @Table({ tableName: 'applications', paranoid: true })
91
+ export class Application extends Model {
92
+ @DeletedAt deletedAt?: Date
93
+ }
94
+
95
+ // findAll automatically excludes soft-deleted — no manual WHERE needed
96
+ // To include deleted: { paranoid: false }
97
+ // Hard delete: { force: true } — only for GDPR
98
+ ```
99
+
100
+ ## Model Checklist
101
+ - [ ] `@Table({ paranoid: true })` — soft delete
102
+ - [ ] `@Default(() => newId())` on id — UUIDv7
103
+ - [ ] `indexes` array in `@Table` — FK columns + ORDER BY columns
104
+ - [ ] Partial index for `WHERE deleted_at IS NULL` if high-volume
105
+ - [ ] `@CreatedAt`, `@UpdatedAt`, `@DeletedAt` decorators
106
+
107
+ ## Migration Checklist
108
+ - [ ] Both `up()` and `down()` implemented
109
+ - [ ] New indexes use `CONCURRENTLY` to avoid table locks
110
+ - [ ] FK constraints with explicit `ON DELETE` behaviour
111
+ - [ ] Run `npm run db:migrate` to verify before committing
@@ -0,0 +1,110 @@
1
+ ---
2
+ paths:
3
+ - "src/**/*.controller.ts"
4
+ - "src/**/*.repository.ts"
5
+ - "src/auth/**/*.ts"
6
+ ---
7
+
8
+ # Security Rules — Auto-injected on controller/repository/auth edits
9
+
10
+ ## Auth — Global Guard, Opt-Out Model
11
+
12
+ ```typescript
13
+ // ✅ Public routes must have @Public()
14
+ @Public()
15
+ @Post('login')
16
+ login(@Body() dto: LoginDto) { ... }
17
+
18
+ // ✅ Protected routes have NOTHING — global guard handles it
19
+ @Get('me')
20
+ getProfile(@CurrentUser() user: JwtPayload) { ... }
21
+
22
+ // ❌ Never add @UseGuards(JwtAuthGuard) manually — it's global
23
+ @UseGuards(JwtAuthGuard) // redundant and misleading
24
+ @Get('me')
25
+ getProfile() { ... }
26
+ ```
27
+
28
+ ## Ownership — Enforced in Repository
29
+
30
+ ```typescript
31
+ // ✅ userId ALWAYS in WHERE clause
32
+ const raw = await this.appModel.findOne({
33
+ where: { id, userId } // userId from auth context — never from user input
34
+ })
35
+
36
+ // ❌ No ownership check — any user can access any record
37
+ const raw = await this.appModel.findByPk(id)
38
+ ```
39
+
40
+ ## Input Validation — DTOs + ValidationPipe
41
+
42
+ ```typescript
43
+ // ✅ ValidationPipe global (main.ts) + class-validator on DTO
44
+ export class CreateApplicationDto {
45
+ @IsString() @MinLength(1) @MaxLength(200)
46
+ @ApiProperty({ example: 'Acme Corp' })
47
+ company: string
48
+
49
+ @IsOptional() @IsUrl()
50
+ @ApiProperty({ required: false })
51
+ postingUrl?: string
52
+ }
53
+
54
+ // ❌ Never accept raw body without DTO
55
+ @Post() create(@Body() body: any) { ... } // FORBIDDEN
56
+ ```
57
+
58
+ ## SQL Injection Prevention
59
+
60
+ ```typescript
61
+ // ✅ Sequelize ORM parameterises automatically
62
+ await this.appModel.findAll({ where: { userId, stage } })
63
+
64
+ // ✅ Raw queries with named replacements
65
+ await sequelize.query(
66
+ 'SELECT * FROM applications WHERE user_id = :userId',
67
+ { replacements: { userId }, type: QueryTypes.SELECT }
68
+ )
69
+
70
+ // ❌ String interpolation — FORBIDDEN
71
+ await sequelize.query(`SELECT * FROM applications WHERE user_id = '${userId}'`)
72
+ ```
73
+
74
+ ## Password Hashing — argon2 Only
75
+
76
+ ```typescript
77
+ import * as argon2 from 'argon2'
78
+
79
+ // Hash
80
+ const hash = await argon2.hash(password)
81
+
82
+ // Verify
83
+ const valid = await argon2.verify(hash, password)
84
+
85
+ // ❌ Never
86
+ import * as bcrypt from 'bcrypt' // GPU-vulnerable
87
+ ```
88
+
89
+ ## Error Responses — Never Expose Internals
90
+
91
+ ```typescript
92
+ // ✅ AllExceptionsFilter in common/ catches all — sanitised output
93
+ // { statusCode, message, timestamp, path } — no stack traces
94
+
95
+ // ✅ Throw semantic NestJS exceptions
96
+ throw new NotFoundException('Application not found')
97
+ throw new ConflictException('Stage transition invalid')
98
+
99
+ // ❌ Never expose internal errors
100
+ res.json({ error: err.message, stack: err.stack })
101
+ ```
102
+
103
+ ## JWT Tokens
104
+
105
+ ```typescript
106
+ // Access token: 15 minutes — from ConfigService
107
+ // Refresh token: 7 days — single use, rotated on refresh
108
+ // Revocation: Redis key `revoked:{jti}` — checked in JwtStrategy.validate()
109
+ // Tokens in response body — never in cookies (frontend handles storage)
110
+ ```