@xenos1996/usat 1.0.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 (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +335 -0
  3. package/USAT.md +596 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +438 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/config.d.ts +7 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +72 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/detect/index.d.ts +58 -0
  13. package/dist/detect/index.d.ts.map +1 -0
  14. package/dist/detect/index.js +329 -0
  15. package/dist/detect/index.js.map +1 -0
  16. package/dist/engine/audit.d.ts +21 -0
  17. package/dist/engine/audit.d.ts.map +1 -0
  18. package/dist/engine/audit.js +295 -0
  19. package/dist/engine/audit.js.map +1 -0
  20. package/dist/engine/diff.d.ts +24 -0
  21. package/dist/engine/diff.d.ts.map +1 -0
  22. package/dist/engine/diff.js +136 -0
  23. package/dist/engine/diff.js.map +1 -0
  24. package/dist/engine/evaluate.d.ts +19 -0
  25. package/dist/engine/evaluate.d.ts.map +1 -0
  26. package/dist/engine/evaluate.js +377 -0
  27. package/dist/engine/evaluate.js.map +1 -0
  28. package/dist/engine/gate.d.ts +19 -0
  29. package/dist/engine/gate.d.ts.map +1 -0
  30. package/dist/engine/gate.js +47 -0
  31. package/dist/engine/gate.js.map +1 -0
  32. package/dist/engine/loader.d.ts +22 -0
  33. package/dist/engine/loader.d.ts.map +1 -0
  34. package/dist/engine/loader.js +360 -0
  35. package/dist/engine/loader.js.map +1 -0
  36. package/dist/engine/maturity.d.ts +20 -0
  37. package/dist/engine/maturity.d.ts.map +1 -0
  38. package/dist/engine/maturity.js +157 -0
  39. package/dist/engine/maturity.js.map +1 -0
  40. package/dist/engine/score.d.ts +19 -0
  41. package/dist/engine/score.d.ts.map +1 -0
  42. package/dist/engine/score.js +141 -0
  43. package/dist/engine/score.js.map +1 -0
  44. package/dist/engine/sections.d.ts +14 -0
  45. package/dist/engine/sections.d.ts.map +1 -0
  46. package/dist/engine/sections.js +50 -0
  47. package/dist/engine/sections.js.map +1 -0
  48. package/dist/index.d.ts +23 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +17 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/report/markdown.d.ts +16 -0
  53. package/dist/report/markdown.d.ts.map +1 -0
  54. package/dist/report/markdown.js +488 -0
  55. package/dist/report/markdown.js.map +1 -0
  56. package/dist/types.d.ts +282 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +16 -0
  59. package/dist/types.js.map +1 -0
  60. package/dist/util/glob.d.ts +11 -0
  61. package/dist/util/glob.d.ts.map +1 -0
  62. package/dist/util/glob.js +86 -0
  63. package/dist/util/glob.js.map +1 -0
  64. package/dist/util/project.d.ts +72 -0
  65. package/dist/util/project.d.ts.map +1 -0
  66. package/dist/util/project.js +586 -0
  67. package/dist/util/project.js.map +1 -0
  68. package/dist/util/yaml.d.ts +28 -0
  69. package/dist/util/yaml.d.ts.map +1 -0
  70. package/dist/util/yaml.js +48 -0
  71. package/dist/util/yaml.js.map +1 -0
  72. package/docs/ARCHITECTURE.md +119 -0
  73. package/docs/README.md +27 -0
  74. package/docs/adr/0001-record-architecture-decisions.md +35 -0
  75. package/docs/adr/0002-typescript-engine-over-python.md +49 -0
  76. package/docs/adr/0003-rules-are-data-not-code.md +50 -0
  77. package/docs/adr/0004-markdown-only-output.md +41 -0
  78. package/docs/adr/0005-maturity-dampens-severity.md +56 -0
  79. package/docs/adr/0006-severity-status-two-axes.md +48 -0
  80. package/docs/adr/0007-suppressions-are-visible-and-expiring.md +46 -0
  81. package/docs/adr/0008-report-trailer-and-diff.md +48 -0
  82. package/docs/adr/0009-fail-closed-on-malformed-input.md +56 -0
  83. package/docs/adr/0010-detector-design.md +54 -0
  84. package/docs/adr/0011-coexist-with-deep-scanners.md +52 -0
  85. package/docs/adr/README.md +32 -0
  86. package/docs/agent-integration.md +129 -0
  87. package/docs/ci-integration.md +163 -0
  88. package/docs/concepts.md +196 -0
  89. package/docs/configuration.md +105 -0
  90. package/docs/detectors.md +186 -0
  91. package/docs/getting-started.md +99 -0
  92. package/docs/maturity-profiles.md +156 -0
  93. package/docs/reference/api.md +193 -0
  94. package/docs/reference/cli.md +412 -0
  95. package/docs/rule-packs.md +278 -0
  96. package/docs/standards-mapping.md +171 -0
  97. package/package.json +96 -0
  98. package/rules/core/architecture.yaml +190 -0
  99. package/rules/core/cicd.yaml +212 -0
  100. package/rules/core/code-quality.yaml +261 -0
  101. package/rules/core/dependencies.yaml +132 -0
  102. package/rules/core/documentation.yaml +119 -0
  103. package/rules/core/future-readiness.yaml +91 -0
  104. package/rules/core/release.yaml +131 -0
  105. package/rules/core/repo.yaml +303 -0
  106. package/rules/core/security.yaml +494 -0
  107. package/rules/core/supply-chain.yaml +357 -0
  108. package/rules/core/testing.yaml +260 -0
  109. package/rules/detectors.yaml +2255 -0
  110. package/rules/index.yaml +41 -0
  111. package/rules/profiles/maturity.yaml +135 -0
  112. package/rules/stacks/ai-era.yaml +193 -0
  113. package/rules/stacks/api-backend.yaml +121 -0
  114. package/rules/stacks/cli.yaml +105 -0
  115. package/rules/stacks/compliance.yaml +157 -0
  116. package/rules/stacks/containers.yaml +124 -0
  117. package/rules/stacks/data.yaml +165 -0
  118. package/rules/stacks/go.yaml +134 -0
  119. package/rules/stacks/iac.yaml +128 -0
  120. package/rules/stacks/jvm.yaml +150 -0
  121. package/rules/stacks/ml-ai.yaml +140 -0
  122. package/rules/stacks/mobile.yaml +121 -0
  123. package/rules/stacks/node-typescript.yaml +166 -0
  124. package/rules/stacks/python.yaml +175 -0
  125. package/rules/stacks/rust.yaml +129 -0
  126. package/rules/stacks/solidity.yaml +139 -0
  127. package/rules/stacks/web-frontend.yaml +159 -0
  128. package/templates/AGENTS.audit.md +135 -0
  129. package/templates/AUDIT_REPORT.md +155 -0
@@ -0,0 +1,2255 @@
1
+ # =============================================================================
2
+ # USAT — Auto-detection signal registry (schema: usat-detectors-v1)
3
+ # -----------------------------------------------------------------------------
4
+ # Every rule pack declares `applies_when` over the FACTS produced here, so
5
+ # USAT "adapts to the project" purely by data. Add a signal, and every pack
6
+ # that cares about it starts applying — no code change required.
7
+ #
8
+ # Fact namespaces
9
+ # lang:* programming language present
10
+ # pm:* package manager
11
+ # fw:* framework / library
12
+ # project:* project archetype
13
+ # platform:* runtime platform
14
+ # db:* datastore
15
+ # orm:* data-access layer
16
+ # auth:* authentication mechanism
17
+ # test:* test tooling
18
+ # ci:* CI provider
19
+ # infra:* infrastructure tooling
20
+ # api:* API style
21
+ # obs:* observability
22
+ # ai:* AI/LLM stack
23
+ # has:* cross-cutting capability
24
+ # doc:* documentation artefact
25
+ # practice:* team practice
26
+ # maturity:* lifecycle stage (computed)
27
+ #
28
+ # Match primitives (inside `match`)
29
+ # any_file: [glob] at least one file matches
30
+ # file_exists:[path/glob] path present
31
+ # dir_exists: [path] directory present
32
+ # content: {include: [glob], pattern} regex found in matching files
33
+ # manifest: {file, key, contains?} dotted key resolves in JSON/TOML/YAML
34
+ # metric: {name, min|max} numeric fact comparison
35
+ # any_of / all_of boolean composition
36
+ # implies: [fact] derived: all listed facts must hold
37
+ # =============================================================================
38
+
39
+ detectors:
40
+ # ------------------------------------------------------------------ language
41
+ - {
42
+ fact: 'lang:typescript',
43
+ category: language,
44
+ title: TypeScript,
45
+ match: { any_file: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'] },
46
+ }
47
+ - {
48
+ fact: 'lang:javascript',
49
+ category: language,
50
+ title: JavaScript,
51
+ match: { any_file: ['**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'] },
52
+ }
53
+ - {
54
+ fact: 'lang:python',
55
+ category: language,
56
+ title: Python,
57
+ match: { any_file: ['**/*.py', '**/*.pyi'] },
58
+ }
59
+ - { fact: 'lang:go', category: language, title: Go, match: { any_file: ['**/*.go'] } }
60
+ - { fact: 'lang:rust', category: language, title: Rust, match: { any_file: ['**/*.rs'] } }
61
+ - { fact: 'lang:java', category: language, title: Java, match: { any_file: ['**/*.java'] } }
62
+ - {
63
+ fact: 'lang:kotlin',
64
+ category: language,
65
+ title: Kotlin,
66
+ match: { any_file: ['**/*.kt', '**/*.kts'] },
67
+ }
68
+ - { fact: 'lang:swift', category: language, title: Swift, match: { any_file: ['**/*.swift'] } }
69
+ - { fact: 'lang:ruby', category: language, title: Ruby, match: { any_file: ['**/*.rb'] } }
70
+ - { fact: 'lang:php', category: language, title: PHP, match: { any_file: ['**/*.php'] } }
71
+ - { fact: 'lang:csharp', category: language, title: 'C#', match: { any_file: ['**/*.cs'] } }
72
+ - {
73
+ fact: 'lang:cpp',
74
+ category: language,
75
+ title: 'C/C++',
76
+ match: { any_file: ['**/*.c', '**/*.h', '**/*.cc', '**/*.cpp', '**/*.hpp'] },
77
+ }
78
+ - { fact: 'lang:scala', category: language, title: Scala, match: { any_file: ['**/*.scala'] } }
79
+ - { fact: 'lang:dart', category: language, title: Dart, match: { any_file: ['**/*.dart'] } }
80
+ - {
81
+ fact: 'lang:elixir',
82
+ category: language,
83
+ title: Elixir,
84
+ match: { any_file: ['**/*.ex', '**/*.exs'] },
85
+ }
86
+ - { fact: 'lang:lua', category: language, title: Lua, match: { any_file: ['**/*.lua'] } }
87
+ - { fact: 'lang:zig', category: language, title: Zig, match: { any_file: ['**/*.zig'] } }
88
+ - {
89
+ fact: 'lang:solidity',
90
+ category: language,
91
+ title: Solidity,
92
+ match: { any_file: ['**/*.sol'] },
93
+ }
94
+ - {
95
+ fact: 'lang:shell',
96
+ category: language,
97
+ title: Shell,
98
+ match: { any_file: ['**/*.sh', '**/*.bash'] },
99
+ }
100
+ - { fact: 'lang:haskell', category: language, title: Haskell, match: { any_file: ['**/*.hs'] } }
101
+
102
+ # ------------------------------------------------------------ package manager
103
+ - {
104
+ fact: 'pm:node',
105
+ category: pm,
106
+ title: 'Node (package.json)',
107
+ match: { file_exists: ['package.json'] },
108
+ }
109
+ - { fact: 'pm:npm', category: pm, title: npm, match: { file_exists: ['package-lock.json'] } }
110
+ - {
111
+ fact: 'pm:pnpm',
112
+ category: pm,
113
+ title: pnpm,
114
+ match:
115
+ {
116
+ any_of:
117
+ [
118
+ { file_exists: ['pnpm-lock.yaml', 'pnpm-workspace.yaml'] },
119
+ { content: { include: ['package.json'], pattern: '"packageManager"\\s*:\\s*"pnpm' } },
120
+ ],
121
+ },
122
+ }
123
+ - {
124
+ fact: 'pm:yarn',
125
+ category: pm,
126
+ title: Yarn,
127
+ match:
128
+ {
129
+ any_of:
130
+ [
131
+ { file_exists: ['yarn.lock', '.yarnrc.yml'] },
132
+ { content: { include: ['package.json'], pattern: '"packageManager"\\s*:\\s*"yarn' } },
133
+ ],
134
+ },
135
+ }
136
+ - {
137
+ fact: 'pm:bun',
138
+ category: pm,
139
+ title: Bun,
140
+ match:
141
+ {
142
+ any_of:
143
+ [
144
+ { file_exists: ['bun.lockb', 'bun.lock'] },
145
+ { content: { include: ['package.json'], pattern: '"packageManager"\\s*:\\s*"bun' } },
146
+ ],
147
+ },
148
+ }
149
+ - {
150
+ fact: 'pm:pip',
151
+ category: pm,
152
+ title: pip,
153
+ match: { file_exists: ['requirements.txt', 'requirements-dev.txt'] },
154
+ }
155
+ - {
156
+ fact: 'pm:poetry',
157
+ category: pm,
158
+ title: Poetry,
159
+ match:
160
+ {
161
+ any_of:
162
+ [
163
+ { file_exists: ['poetry.lock'] },
164
+ { content: { include: ['pyproject.toml'], pattern: '\\[tool\\.poetry\\]' } },
165
+ ],
166
+ },
167
+ }
168
+ - {
169
+ fact: 'pm:uv',
170
+ category: pm,
171
+ title: uv,
172
+ match:
173
+ {
174
+ any_of:
175
+ [
176
+ { file_exists: ['uv.lock'] },
177
+ { content: { include: ['pyproject.toml'], pattern: '\\[tool\\.uv\\]' } },
178
+ ],
179
+ },
180
+ }
181
+ - { fact: 'pm:pipenv', category: pm, title: Pipenv, match: { file_exists: ['Pipfile'] } }
182
+ - { fact: 'pm:cargo', category: pm, title: Cargo, match: { file_exists: ['Cargo.toml'] } }
183
+ - { fact: 'pm:go-mod', category: pm, title: 'Go modules', match: { file_exists: ['go.mod'] } }
184
+ - { fact: 'pm:maven', category: pm, title: Maven, match: { file_exists: ['pom.xml'] } }
185
+ - {
186
+ fact: 'pm:gradle',
187
+ category: pm,
188
+ title: Gradle,
189
+ match:
190
+ {
191
+ any_file: ['build.gradle', 'build.gradle.kts', 'settings.gradle', 'settings.gradle.kts'],
192
+ },
193
+ }
194
+ - {
195
+ fact: 'pm:composer',
196
+ category: pm,
197
+ title: Composer,
198
+ match: { file_exists: ['composer.json'] },
199
+ }
200
+ - {
201
+ fact: 'pm:nuget',
202
+ category: pm,
203
+ title: NuGet,
204
+ match: { any_file: ['*.csproj', '*.fsproj', 'packages.config'] },
205
+ }
206
+ - {
207
+ fact: 'pm:swiftpm',
208
+ category: pm,
209
+ title: 'SwiftPM',
210
+ match: { file_exists: ['Package.swift'] },
211
+ }
212
+ - { fact: 'pm:pub', category: pm, title: Pub, match: { file_exists: ['pubspec.yaml'] } }
213
+ - { fact: 'pm:mix', category: pm, title: Mix, match: { file_exists: ['mix.exs'] } }
214
+ - { fact: 'pm:foundry', category: pm, title: Foundry, match: { file_exists: ['foundry.toml'] } }
215
+ - { fact: 'pm:hardhat', category: pm, title: Hardhat, match: { any_file: ['hardhat.config.*'] } }
216
+
217
+ # --------------------------------------------------------------- web frontend
218
+ - {
219
+ fact: 'fw:react',
220
+ category: framework,
221
+ title: React,
222
+ match:
223
+ {
224
+ any_of:
225
+ [
226
+ { manifest: { file: 'package.json', key: 'dependencies.react' } },
227
+ { manifest: { file: 'package.json', key: 'devDependencies.react' } },
228
+ ],
229
+ },
230
+ }
231
+ - {
232
+ fact: 'fw:vue',
233
+ category: framework,
234
+ title: Vue,
235
+ match: { manifest: { file: 'package.json', key: 'dependencies.vue' } },
236
+ }
237
+ - {
238
+ fact: 'fw:angular',
239
+ category: framework,
240
+ title: Angular,
241
+ match:
242
+ {
243
+ any_of:
244
+ [
245
+ { manifest: { file: 'package.json', key: 'dependencies.@angular/core' } },
246
+ { file_exists: ['angular.json'] },
247
+ ],
248
+ },
249
+ }
250
+ - {
251
+ fact: 'fw:svelte',
252
+ category: framework,
253
+ title: Svelte,
254
+ match: { manifest: { file: 'package.json', key: 'dependencies.svelte' } },
255
+ }
256
+ - {
257
+ fact: 'fw:solid',
258
+ category: framework,
259
+ title: 'SolidJS',
260
+ match: { manifest: { file: 'package.json', key: 'dependencies.solid-js' } },
261
+ }
262
+ - {
263
+ fact: 'fw:next',
264
+ category: framework,
265
+ title: 'Next.js',
266
+ match:
267
+ {
268
+ any_of:
269
+ [
270
+ {
271
+ file_exists:
272
+ ['next.config.js', 'next.config.mjs', 'next.config.ts', 'next.config.cjs'],
273
+ },
274
+ { manifest: { file: 'package.json', key: 'dependencies.next' } },
275
+ ],
276
+ },
277
+ }
278
+ - {
279
+ fact: 'fw:nuxt',
280
+ category: framework,
281
+ title: Nuxt,
282
+ match:
283
+ {
284
+ any_of:
285
+ [
286
+ { file_exists: ['nuxt.config.ts', 'nuxt.config.js'] },
287
+ { manifest: { file: 'package.json', key: 'dependencies.nuxt' } },
288
+ ],
289
+ },
290
+ }
291
+ - {
292
+ fact: 'fw:remix',
293
+ category: framework,
294
+ title: Remix,
295
+ match: { manifest: { file: 'package.json', key: 'dependencies.@remix-run/node' } },
296
+ }
297
+ - {
298
+ fact: 'fw:sveltekit',
299
+ category: framework,
300
+ title: 'SvelteKit',
301
+ match:
302
+ {
303
+ any_of:
304
+ [
305
+ { file_exists: ['svelte.config.js'] },
306
+ { manifest: { file: 'package.json', key: 'dependencies.@sveltejs/kit' } },
307
+ ],
308
+ },
309
+ }
310
+ - {
311
+ fact: 'fw:astro',
312
+ category: framework,
313
+ title: Astro,
314
+ match: { manifest: { file: 'package.json', key: 'dependencies.astro' } },
315
+ }
316
+ - {
317
+ fact: 'fw:vite',
318
+ category: framework,
319
+ title: Vite,
320
+ match:
321
+ {
322
+ any_of:
323
+ [
324
+ { file_exists: ['vite.config.ts', 'vite.config.js', 'vite.config.mts'] },
325
+ { manifest: { file: 'package.json', key: 'devDependencies.vite' } },
326
+ ],
327
+ },
328
+ }
329
+ - {
330
+ fact: 'fw:tailwind',
331
+ category: framework,
332
+ title: Tailwind CSS,
333
+ match:
334
+ {
335
+ any_of:
336
+ [
337
+ { file_exists: ['tailwind.config.js', 'tailwind.config.ts'] },
338
+ { manifest: { file: 'package.json', key: 'devDependencies.tailwindcss' } },
339
+ ],
340
+ },
341
+ }
342
+ - {
343
+ fact: 'fw:electron',
344
+ category: framework,
345
+ title: Electron,
346
+ match: { manifest: { file: 'package.json', key: 'devDependencies.electron' } },
347
+ }
348
+ - {
349
+ fact: 'fw:tauri',
350
+ category: framework,
351
+ title: Tauri,
352
+ match:
353
+ {
354
+ any_of:
355
+ [
356
+ { dir_exists: ['src-tauri'] },
357
+ { manifest: { file: 'package.json', key: 'dependencies.@tauri-apps/api' } },
358
+ ],
359
+ },
360
+ }
361
+
362
+ # ---------------------------------------------------------------- node backend
363
+ - {
364
+ fact: 'fw:express',
365
+ category: framework,
366
+ title: Express,
367
+ match: { manifest: { file: 'package.json', key: 'dependencies.express' } },
368
+ }
369
+ - {
370
+ fact: 'fw:fastify',
371
+ category: framework,
372
+ title: Fastify,
373
+ match: { manifest: { file: 'package.json', key: 'dependencies.fastify' } },
374
+ }
375
+ - {
376
+ fact: 'fw:nest',
377
+ category: framework,
378
+ title: NestJS,
379
+ match: { manifest: { file: 'package.json', key: 'dependencies.@nestjs/core' } },
380
+ }
381
+ - {
382
+ fact: 'fw:hono',
383
+ category: framework,
384
+ title: Hono,
385
+ match: { manifest: { file: 'package.json', key: 'dependencies.hono' } },
386
+ }
387
+ - {
388
+ fact: 'fw:koa',
389
+ category: framework,
390
+ title: Koa,
391
+ match: { manifest: { file: 'package.json', key: 'dependencies.koa' } },
392
+ }
393
+
394
+ # --------------------------------------------------------------------- python
395
+ - {
396
+ fact: 'fw:django',
397
+ category: framework,
398
+ title: Django,
399
+ match:
400
+ {
401
+ any_of:
402
+ [
403
+ { file_exists: ['manage.py'] },
404
+ {
405
+ content:
406
+ { include: ['requirements*.txt', 'pyproject.toml'], pattern: '^django[=<>~]' },
407
+ },
408
+ ],
409
+ },
410
+ }
411
+ - {
412
+ fact: 'fw:flask',
413
+ category: framework,
414
+ title: Flask,
415
+ match:
416
+ { content: { include: ['requirements*.txt', 'pyproject.toml'], pattern: '^flask[=<>~]' } },
417
+ }
418
+ - {
419
+ fact: 'fw:fastapi',
420
+ category: framework,
421
+ title: FastAPI,
422
+ match:
423
+ {
424
+ content: { include: ['requirements*.txt', 'pyproject.toml'], pattern: '^fastapi[=<>~]' },
425
+ },
426
+ }
427
+ - {
428
+ fact: 'fw:celery',
429
+ category: framework,
430
+ title: Celery,
431
+ match:
432
+ { content: { include: ['requirements*.txt', 'pyproject.toml'], pattern: '^celery[=<>~]' } },
433
+ }
434
+ - {
435
+ fact: 'fw:airflow',
436
+ category: framework,
437
+ title: Airflow,
438
+ match:
439
+ {
440
+ content:
441
+ { include: ['requirements*.txt', 'pyproject.toml'], pattern: '^apache-airflow[=<>~]' },
442
+ },
443
+ }
444
+ - {
445
+ fact: 'fw:streamlit',
446
+ category: framework,
447
+ title: Streamlit,
448
+ match:
449
+ {
450
+ content:
451
+ { include: ['requirements*.txt', 'pyproject.toml'], pattern: '^streamlit[=<>~]' },
452
+ },
453
+ }
454
+
455
+ # ------------------------------------------------------------------------ go
456
+ - {
457
+ fact: 'fw:gin',
458
+ category: framework,
459
+ title: Gin,
460
+ match: { content: { include: ['go.mod'], pattern: 'gin-gonic/gin' } },
461
+ }
462
+ - {
463
+ fact: 'fw:echo',
464
+ category: framework,
465
+ title: Echo,
466
+ match: { content: { include: ['go.mod'], pattern: 'labstack/echo' } },
467
+ }
468
+ - {
469
+ fact: 'fw:fiber',
470
+ category: framework,
471
+ title: Fiber,
472
+ match: { content: { include: ['go.mod'], pattern: 'gofiber/fiber' } },
473
+ }
474
+ - {
475
+ fact: 'fw:chi',
476
+ category: framework,
477
+ title: Chi,
478
+ match: { content: { include: ['go.mod'], pattern: 'go-chi/chi' } },
479
+ }
480
+
481
+ # ---------------------------------------------------------------------- rust
482
+ - {
483
+ fact: 'fw:axum',
484
+ category: framework,
485
+ title: Axum,
486
+ match: { content: { include: ['Cargo.toml'], pattern: '^axum\\s*=' } },
487
+ }
488
+ - {
489
+ fact: 'fw:actix',
490
+ category: framework,
491
+ title: Actix,
492
+ match: { content: { include: ['Cargo.toml'], pattern: '^actix-web\\s*=' } },
493
+ }
494
+ - {
495
+ fact: 'fw:rocket',
496
+ category: framework,
497
+ title: Rocket,
498
+ match: { content: { include: ['Cargo.toml'], pattern: '^rocket\\s*=' } },
499
+ }
500
+
501
+ # ----------------------------------------------------------------- jvm / other
502
+ - {
503
+ fact: 'fw:spring',
504
+ category: framework,
505
+ title: Spring,
506
+ match:
507
+ {
508
+ any_of:
509
+ [
510
+ { content: { include: ['pom.xml', 'build.gradle*'], pattern: 'spring-boot' } },
511
+ { content: { include: ['**/*.java'], pattern: 'org\\.springframework' } },
512
+ ],
513
+ },
514
+ }
515
+ - {
516
+ fact: 'fw:quarkus',
517
+ category: framework,
518
+ title: Quarkus,
519
+ match: { content: { include: ['pom.xml', 'build.gradle*'], pattern: 'quarkus' } },
520
+ }
521
+ - {
522
+ fact: 'fw:rails',
523
+ category: framework,
524
+ title: Rails,
525
+ match:
526
+ {
527
+ any_of:
528
+ [
529
+ { file_exists: ['config/application.rb'] },
530
+ { content: { include: ['Gemfile'], pattern: 'gem [''"]rails[''"]' } },
531
+ ],
532
+ },
533
+ }
534
+ - {
535
+ fact: 'fw:laravel',
536
+ category: framework,
537
+ title: Laravel,
538
+ match:
539
+ {
540
+ any_of:
541
+ [
542
+ { file_exists: ['artisan'] },
543
+ { content: { include: ['composer.json'], pattern: 'laravel/framework' } },
544
+ ],
545
+ },
546
+ }
547
+ - {
548
+ fact: 'fw:symfony',
549
+ category: framework,
550
+ title: Symfony,
551
+ match: { content: { include: ['composer.json'], pattern: 'symfony/framework-bundle' } },
552
+ }
553
+ - {
554
+ fact: 'fw:dotnet',
555
+ category: framework,
556
+ title: '.NET / ASP.NET',
557
+ match: { content: { include: ['**/*.csproj'], pattern: 'Microsoft\\.(NET|AspNetCore)' } },
558
+ }
559
+
560
+ # --------------------------------------------------------------------- mobile
561
+ - {
562
+ fact: 'fw:react-native',
563
+ category: framework,
564
+ title: 'React Native',
565
+ match:
566
+ {
567
+ any_of:
568
+ [
569
+ { manifest: { file: 'package.json', key: 'dependencies.react-native' } },
570
+ { file_exists: ['app.json'] },
571
+ ],
572
+ },
573
+ }
574
+ - {
575
+ fact: 'fw:expo',
576
+ category: framework,
577
+ title: Expo,
578
+ match:
579
+ {
580
+ any_of:
581
+ [
582
+ { manifest: { file: 'package.json', key: 'dependencies.expo' } },
583
+ { file_exists: ['app.config.js', 'app.config.ts', 'expo.json'] },
584
+ ],
585
+ },
586
+ }
587
+ - {
588
+ fact: 'fw:flutter',
589
+ category: framework,
590
+ title: Flutter,
591
+ match: { any_of: [{ file_exists: ['pubspec.yaml'] }, { any_file: ['**/*.dart'] }] },
592
+ }
593
+ - {
594
+ fact: 'platform:ios',
595
+ category: platform,
596
+ title: iOS,
597
+ match:
598
+ {
599
+ any_of:
600
+ [
601
+ { dir_exists: ['ios', '*.xcodeproj'] },
602
+ { any_file: ['**/*.xcworkspace/**', '**/Info.plist', '**/*.pbxproj'] },
603
+ ],
604
+ },
605
+ }
606
+ - {
607
+ fact: 'platform:android',
608
+ category: platform,
609
+ title: Android,
610
+ match:
611
+ {
612
+ any_of:
613
+ [{ dir_exists: ['android'] }, { any_file: ['**/AndroidManifest.xml', '**/*.gradle'] }],
614
+ },
615
+ }
616
+ - { fact: 'platform:mobile', category: platform, title: Mobile, implies: ['platform:ios'] }
617
+ - { fact: 'platform:mobile', category: platform, title: Mobile, implies: ['platform:android'] }
618
+ - { fact: 'platform:mobile', category: platform, title: Mobile, implies: ['fw:react-native'] }
619
+ - { fact: 'platform:mobile', category: platform, title: Mobile, implies: ['fw:flutter'] }
620
+ - { fact: 'platform:mobile', category: platform, title: Mobile, implies: ['fw:expo'] }
621
+
622
+ # ------------------------------------------------------------------- platform
623
+ - {
624
+ fact: 'platform:web',
625
+ category: platform,
626
+ title: Web,
627
+ match:
628
+ {
629
+ any_of:
630
+ [
631
+ { any_file: ['**/*.html', '**/*.jsx', '**/*.tsx', '**/*.vue', '**/*.svelte'] },
632
+ { manifest: { file: 'package.json', key: 'dependencies.react' } },
633
+ { manifest: { file: 'package.json', key: 'dependencies.vue' } },
634
+ ],
635
+ },
636
+ }
637
+ - {
638
+ fact: 'platform:server',
639
+ category: platform,
640
+ title: Server,
641
+ match:
642
+ {
643
+ any_of:
644
+ [
645
+ {
646
+ file_exists:
647
+ ['Dockerfile', 'Procfile', 'fly.toml', 'vercel.json', 'serverless.yml'],
648
+ },
649
+ { dir_exists: ['server', 'api', 'backend'] },
650
+ ],
651
+ },
652
+ }
653
+ - { fact: 'platform:desktop', category: platform, title: Desktop, implies: ['fw:electron'] }
654
+ - { fact: 'platform:desktop', category: platform, title: Desktop, implies: ['fw:tauri'] }
655
+ # A request-serving framework IS a server, with or without a Dockerfile:
656
+ # without these, a bare Express/FastAPI/Rails app skipped every
657
+ # platform-gated rule (SEC-003 et al.) for lack of platform:server.
658
+ # Deliberately request handlers only — task queues (celery, airflow),
659
+ # static builders (astro, vite), and desktop/mobile packs imply nothing.
660
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:express'] }
661
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:fastify'] }
662
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:nest'] }
663
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:hono'] }
664
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:koa'] }
665
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:django'] }
666
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:flask'] }
667
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:fastapi'] }
668
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:gin'] }
669
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:echo'] }
670
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:fiber'] }
671
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:chi'] }
672
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:axum'] }
673
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:actix'] }
674
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:rocket'] }
675
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:spring'] }
676
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:quarkus'] }
677
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:rails'] }
678
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:laravel'] }
679
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:symfony'] }
680
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:next'] }
681
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:nuxt'] }
682
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:remix'] }
683
+ - { fact: 'platform:server', category: platform, title: Server, implies: ['fw:sveltekit'] }
684
+ - {
685
+ fact: 'platform:evm',
686
+ category: platform,
687
+ title: 'EVM / Blockchain',
688
+ match:
689
+ {
690
+ any_of:
691
+ [
692
+ { any_file: ['**/*.sol', '**/*.vy'] },
693
+ {
694
+ file_exists:
695
+ ['foundry.toml', 'hardhat.config.js', 'hardhat.config.ts', 'truffle-config.js'],
696
+ },
697
+ ],
698
+ },
699
+ }
700
+ - {
701
+ fact: 'platform:embedded',
702
+ category: platform,
703
+ title: Embedded,
704
+ match:
705
+ { any_of: [{ any_file: ['**/*.ino', '**/*.hex'] }, { file_exists: ['platformio.ini'] }] },
706
+ }
707
+
708
+ # ---------------------------------------------------------------------- types
709
+ - {
710
+ fact: 'project:library',
711
+ category: type,
712
+ title: 'Library / SDK',
713
+ match:
714
+ {
715
+ any_of:
716
+ [
717
+ { manifest: { file: 'package.json', key: 'main' } },
718
+ { manifest: { file: 'package.json', key: 'exports' } },
719
+ { manifest: { file: 'pyproject.toml', key: 'project.name' } },
720
+ { file_exists: ['setup.py', 'setup.cfg'] },
721
+ ],
722
+ },
723
+ }
724
+ - {
725
+ fact: 'project:cli',
726
+ category: type,
727
+ title: 'CLI tool',
728
+ match:
729
+ {
730
+ any_of:
731
+ [
732
+ { manifest: { file: 'package.json', key: 'bin' } },
733
+ { content: { include: ['pyproject.toml'], pattern: '\\[project\\.scripts\\]' } },
734
+ { dir_exists: ['cmd'] },
735
+ ],
736
+ },
737
+ }
738
+ - {
739
+ fact: 'project:monorepo',
740
+ category: type,
741
+ title: Monorepo,
742
+ match:
743
+ {
744
+ any_of:
745
+ [
746
+ { file_exists: ['pnpm-workspace.yaml', 'turbo.json', 'nx.json', 'lerna.json'] },
747
+ { content: { include: ['package.json'], pattern: '"workspaces"\\s*:' } },
748
+ { dir_exists: ['packages', 'apps'] },
749
+ ],
750
+ },
751
+ }
752
+ - {
753
+ fact: 'project:api',
754
+ category: type,
755
+ title: 'API / service',
756
+ match:
757
+ {
758
+ any_of:
759
+ [
760
+ {
761
+ any_file: ['**/openapi.yaml', '**/openapi.json', '**/swagger.json', '**/*.graphql'],
762
+ },
763
+ { dir_exists: ['api', 'routes', 'handlers', 'controllers'] },
764
+ ],
765
+ },
766
+ }
767
+ - {
768
+ fact: 'project:ml',
769
+ category: type,
770
+ title: 'ML / AI system',
771
+ match:
772
+ {
773
+ any_of:
774
+ [
775
+ {
776
+ content:
777
+ {
778
+ include: ['requirements*.txt', 'pyproject.toml', 'environment.yml'],
779
+ pattern: '^(torch|tensorflow|scikit-learn|transformers|jax|keras|pandas|numpy)[=<>~]',
780
+ },
781
+ },
782
+ { dir_exists: ['notebooks', 'models', 'checkpoints'] },
783
+ { any_file: ['**/*.ipynb'] },
784
+ ],
785
+ },
786
+ }
787
+ - {
788
+ fact: 'project:blockchain',
789
+ category: type,
790
+ title: 'Smart contracts',
791
+ implies: ['platform:evm'],
792
+ }
793
+ - {
794
+ fact: 'project:iac',
795
+ category: type,
796
+ title: 'IaC / DevOps tooling',
797
+ match:
798
+ {
799
+ any_of:
800
+ [
801
+ { any_file: ['**/*.tf'] },
802
+ {
803
+ dir_exists: ['terraform', 'infra', 'infrastructure', 'charts', 'manifests', 'k8s'],
804
+ },
805
+ ],
806
+ },
807
+ }
808
+ - {
809
+ fact: 'project:data-pipeline',
810
+ category: type,
811
+ title: 'Data pipeline / ETL',
812
+ match:
813
+ {
814
+ any_of:
815
+ [
816
+ { dir_exists: ['dags', 'pipelines', 'etl'] },
817
+ {
818
+ content:
819
+ {
820
+ include: ['requirements*.txt', 'pyproject.toml'],
821
+ pattern: '^(apache-airflow|dagster|prefect|luigi|dbt-core)[=<>~]',
822
+ },
823
+ },
824
+ ],
825
+ },
826
+ }
827
+ - {
828
+ fact: 'project:game',
829
+ category: type,
830
+ title: Game,
831
+ match:
832
+ {
833
+ any_of:
834
+ [
835
+ { any_file: ['**/*.unity', '**/*.godot', '**/*.tscn'] },
836
+ {
837
+ content:
838
+ {
839
+ include: ['package.json'],
840
+ pattern: '"(phaser|three|babylonjs|pixi\\.js)"\\s*:',
841
+ },
842
+ },
843
+ ],
844
+ },
845
+ }
846
+ - {
847
+ fact: 'project:docs',
848
+ category: type,
849
+ title: 'Docs site',
850
+ match:
851
+ {
852
+ any_of:
853
+ [
854
+ { file_exists: ['docusaurus.config.js', 'mkdocs.yml', 'book.toml'] },
855
+ { dir_exists: ['docs'] },
856
+ ],
857
+ },
858
+ }
859
+
860
+ # ----------------------------------------------------------------- databases
861
+ - {
862
+ fact: 'db:postgres',
863
+ category: db,
864
+ title: PostgreSQL,
865
+ match:
866
+ {
867
+ any_of:
868
+ [
869
+ {
870
+ content:
871
+ {
872
+ include: ['**/*.{env,example,yml,yaml,toml,json,ts,js,py,go,rs}'],
873
+ pattern: 'postgres(ql)?://',
874
+ },
875
+ },
876
+ {
877
+ content:
878
+ {
879
+ include:
880
+ [
881
+ 'requirements*.txt',
882
+ 'pyproject.toml',
883
+ 'package.json',
884
+ 'go.mod',
885
+ 'Cargo.toml',
886
+ ],
887
+ pattern: '(psycopg|pg|postgres|postgresql)',
888
+ },
889
+ },
890
+ ],
891
+ },
892
+ }
893
+ - {
894
+ fact: 'db:mysql',
895
+ category: db,
896
+ title: MySQL/MariaDB,
897
+ match:
898
+ {
899
+ any_of:
900
+ [
901
+ {
902
+ content:
903
+ {
904
+ include: ['**/*.{env,example,yml,yaml,toml,json,ts,js,py,go}'],
905
+ pattern: 'mysql://',
906
+ },
907
+ },
908
+ {
909
+ content:
910
+ {
911
+ include: ['requirements*.txt', 'package.json', 'go.mod'],
912
+ pattern: '(mysql2?|pymysql|mariadb)',
913
+ },
914
+ },
915
+ ],
916
+ },
917
+ }
918
+ - {
919
+ fact: 'db:sqlite',
920
+ category: db,
921
+ title: SQLite,
922
+ match:
923
+ {
924
+ any_of:
925
+ [
926
+ { any_file: ['**/*.sqlite', '**/*.sqlite3', '**/*.db'] },
927
+ {
928
+ content:
929
+ {
930
+ include: ['requirements*.txt', 'package.json', 'go.mod', 'Cargo.toml'],
931
+ pattern: '(sqlite3|better-sqlite3|sqlx.*sqlite)',
932
+ },
933
+ },
934
+ ],
935
+ },
936
+ }
937
+ - {
938
+ fact: 'db:mongodb',
939
+ category: db,
940
+ title: MongoDB,
941
+ match:
942
+ {
943
+ any_of:
944
+ [
945
+ {
946
+ content:
947
+ {
948
+ include: ['**/*.{env,example,yml,yaml,ts,js,py}'],
949
+ pattern: 'mongodb(\\+srv)?://',
950
+ },
951
+ },
952
+ {
953
+ content:
954
+ {
955
+ include: ['package.json', 'requirements*.txt', 'go.mod'],
956
+ pattern: '(mongoose|pymongo|mongo-driver)',
957
+ },
958
+ },
959
+ ],
960
+ },
961
+ }
962
+ - {
963
+ fact: 'db:redis',
964
+ category: db,
965
+ title: Redis,
966
+ match:
967
+ {
968
+ any_of:
969
+ [
970
+ {
971
+ content:
972
+ { include: ['**/*.{env,example,yml,yaml,ts,js,py,go}'], pattern: 'redis://' },
973
+ },
974
+ {
975
+ content:
976
+ {
977
+ include: ['package.json', 'requirements*.txt', 'go.mod'],
978
+ pattern: '(^|")(ioredis|redis|go-redis)',
979
+ },
980
+ },
981
+ ],
982
+ },
983
+ }
984
+ - {
985
+ fact: 'db:clickhouse',
986
+ category: db,
987
+ title: ClickHouse,
988
+ match: { content: { include: ['**/*.{env,example,yml,yaml,py,go}'], pattern: 'clickhouse' } },
989
+ }
990
+ - {
991
+ fact: 'db:elasticsearch',
992
+ category: db,
993
+ title: Elasticsearch,
994
+ match:
995
+ {
996
+ content:
997
+ {
998
+ include: ['**/*.{env,example,yml,yaml,py,go,ts,js}'],
999
+ pattern: '(elasticsearch|opensearch)',
1000
+ },
1001
+ },
1002
+ }
1003
+ - {
1004
+ fact: 'db:vector',
1005
+ category: db,
1006
+ title: 'Vector store',
1007
+ match:
1008
+ {
1009
+ content:
1010
+ {
1011
+ include: ['package.json', 'requirements*.txt', 'pyproject.toml', 'go.mod'],
1012
+ pattern: '(pgvector|pinecone|weaviate|qdrant|chromadb|milvus|faiss|lancedb)',
1013
+ },
1014
+ },
1015
+ }
1016
+ - {
1017
+ fact: 'db:dynamodb',
1018
+ category: db,
1019
+ title: DynamoDB,
1020
+ match: { content: { include: ['**/*.{ts,js,py,go,yml,yaml,json}'], pattern: 'dynamodb' } },
1021
+ }
1022
+ - {
1023
+ fact: 'has:database',
1024
+ category: db,
1025
+ title: 'Uses a database',
1026
+ match:
1027
+ {
1028
+ any_of:
1029
+ [{ dir_exists: ['migrations', 'prisma', 'db', 'models'] }, { any_file: ['**/*.sql'] }],
1030
+ },
1031
+ }
1032
+
1033
+ - {
1034
+ fact: 'orm:prisma',
1035
+ category: orm,
1036
+ title: Prisma,
1037
+ match:
1038
+ {
1039
+ any_of:
1040
+ [
1041
+ { dir_exists: ['prisma'] },
1042
+ { manifest: { file: 'package.json', key: 'dependencies.@prisma/client' } },
1043
+ ],
1044
+ },
1045
+ }
1046
+ - {
1047
+ fact: 'orm:drizzle',
1048
+ category: orm,
1049
+ title: Drizzle,
1050
+ match: { manifest: { file: 'package.json', key: 'dependencies.drizzle-orm' } },
1051
+ }
1052
+ - {
1053
+ fact: 'orm:typeorm',
1054
+ category: orm,
1055
+ title: TypeORM,
1056
+ match: { manifest: { file: 'package.json', key: 'dependencies.typeorm' } },
1057
+ }
1058
+ - {
1059
+ fact: 'orm:sequelize',
1060
+ category: orm,
1061
+ title: Sequelize,
1062
+ match: { manifest: { file: 'package.json', key: 'dependencies.sequelize' } },
1063
+ }
1064
+ - {
1065
+ fact: 'orm:mongoose',
1066
+ category: orm,
1067
+ title: Mongoose,
1068
+ match: { manifest: { file: 'package.json', key: 'dependencies.mongoose' } },
1069
+ }
1070
+ - {
1071
+ fact: 'orm:sqlalchemy',
1072
+ category: orm,
1073
+ title: SQLAlchemy,
1074
+ match:
1075
+ {
1076
+ content:
1077
+ { include: ['requirements*.txt', 'pyproject.toml'], pattern: '^sqlalchemy[=<>~]' },
1078
+ },
1079
+ }
1080
+ - {
1081
+ fact: 'orm:gorm',
1082
+ category: orm,
1083
+ title: GORM,
1084
+ match: { content: { include: ['go.mod'], pattern: 'gorm\\.io/gorm' } },
1085
+ }
1086
+ - {
1087
+ fact: 'orm:sqlx',
1088
+ category: orm,
1089
+ title: SQLx,
1090
+ match: { content: { include: ['Cargo.toml'], pattern: '^sqlx\\s*=' } },
1091
+ }
1092
+ - {
1093
+ fact: 'orm:diesel',
1094
+ category: orm,
1095
+ title: Diesel,
1096
+ match: { content: { include: ['Cargo.toml'], pattern: '^diesel\\s*=' } },
1097
+ }
1098
+ - {
1099
+ fact: 'orm:hibernate',
1100
+ category: orm,
1101
+ title: Hibernate/JPA,
1102
+ match:
1103
+ {
1104
+ content: { include: ['**/*.java'], pattern: 'javax\\.persistence|jakarta\\.persistence' },
1105
+ },
1106
+ }
1107
+ - {
1108
+ fact: 'orm:activerecord',
1109
+ category: orm,
1110
+ title: ActiveRecord,
1111
+ match: { content: { include: ['**/*.rb'], pattern: 'ApplicationRecord' } },
1112
+ }
1113
+
1114
+ # ------------------------------------------------------------------- auth
1115
+ - {
1116
+ fact: 'auth:jwt',
1117
+ category: auth,
1118
+ title: JWT,
1119
+ match:
1120
+ {
1121
+ any_of:
1122
+ [
1123
+ {
1124
+ content:
1125
+ {
1126
+ include: ['**/*.{ts,js,py,go,rs,java,kt}'],
1127
+ pattern: 'jsonwebtoken|jose|PyJWT|golang-jwt|json_web_token',
1128
+ },
1129
+ },
1130
+ { content: { include: ['**/*.{ts,js,py,go,rs}'], pattern: 'Bearer\\s' } },
1131
+ ],
1132
+ },
1133
+ }
1134
+ - {
1135
+ fact: 'auth:oauth',
1136
+ category: auth,
1137
+ title: OAuth / OIDC,
1138
+ match:
1139
+ {
1140
+ content:
1141
+ {
1142
+ include: ['**/*.{ts,js,py,go,rs,java,yaml,yml,json}'],
1143
+ pattern: 'oauth2?|openid|oidc',
1144
+ },
1145
+ },
1146
+ }
1147
+ - {
1148
+ fact: 'auth:session',
1149
+ category: auth,
1150
+ title: Sessions / cookies,
1151
+ match:
1152
+ {
1153
+ content:
1154
+ {
1155
+ include: ['**/*.{ts,js,py,go,rs}'],
1156
+ pattern: 'express-session|cookie-session|sessionMiddleware|SESSION_SECRET',
1157
+ },
1158
+ },
1159
+ }
1160
+ - {
1161
+ fact: 'auth:apikey',
1162
+ category: auth,
1163
+ title: 'API keys',
1164
+ match:
1165
+ {
1166
+ content:
1167
+ { include: ['**/*.{ts,js,py,go,rs,env,example}'], pattern: 'API_KEY|X-API-Key|apiKey' },
1168
+ },
1169
+ }
1170
+ - {
1171
+ fact: 'auth:passport',
1172
+ category: auth,
1173
+ title: Passport.js,
1174
+ match: { manifest: { file: 'package.json', key: 'dependencies.passport' } },
1175
+ }
1176
+ - {
1177
+ fact: 'auth:nextauth',
1178
+ category: auth,
1179
+ title: Auth.js / NextAuth,
1180
+ match:
1181
+ {
1182
+ any_of:
1183
+ [
1184
+ { manifest: { file: 'package.json', key: 'dependencies.next-auth' } },
1185
+ { manifest: { file: 'package.json', key: 'dependencies.@auth/core' } },
1186
+ ],
1187
+ },
1188
+ }
1189
+ - {
1190
+ fact: 'auth:supabase',
1191
+ category: auth,
1192
+ title: Supabase,
1193
+ match: { manifest: { file: 'package.json', key: 'dependencies.@supabase/supabase-js' } },
1194
+ }
1195
+ - {
1196
+ fact: 'auth:firebase',
1197
+ category: auth,
1198
+ title: Firebase Auth,
1199
+ match:
1200
+ {
1201
+ content:
1202
+ {
1203
+ include: ['package.json', '**/*.{ts,js,py}'],
1204
+ pattern: 'firebase/auth|firebase-admin',
1205
+ },
1206
+ },
1207
+ }
1208
+ - {
1209
+ fact: 'auth:auth0',
1210
+ category: auth,
1211
+ title: Auth0,
1212
+ match: { content: { include: ['**/*.{ts,js,py,go,json,yml,yaml}'], pattern: 'auth0' } },
1213
+ }
1214
+
1215
+ # ----------------------------------------------------------------- api style
1216
+ - {
1217
+ fact: 'api:graphql',
1218
+ category: api,
1219
+ title: GraphQL,
1220
+ match:
1221
+ {
1222
+ any_of:
1223
+ [
1224
+ { any_file: ['**/*.graphql', '**/*.gql'] },
1225
+ {
1226
+ content:
1227
+ {
1228
+ include: ['package.json'],
1229
+ pattern: '"(graphql|apollo-server|@apollo/server)"\\s*:',
1230
+ },
1231
+ },
1232
+ ],
1233
+ },
1234
+ }
1235
+ - {
1236
+ fact: 'api:grpc',
1237
+ category: api,
1238
+ title: gRPC,
1239
+ match:
1240
+ {
1241
+ any_of:
1242
+ [
1243
+ { any_file: ['**/*.proto'] },
1244
+ {
1245
+ content:
1246
+ {
1247
+ include: ['package.json', 'go.mod', 'Cargo.toml', 'requirements*.txt'],
1248
+ pattern: '(grpc|tonic)',
1249
+ },
1250
+ },
1251
+ ],
1252
+ },
1253
+ }
1254
+ - {
1255
+ fact: 'api:trpc',
1256
+ category: api,
1257
+ title: tRPC,
1258
+ match: { manifest: { file: 'package.json', key: 'dependencies.@trpc/server' } },
1259
+ }
1260
+ - {
1261
+ fact: 'api:websocket',
1262
+ category: api,
1263
+ title: WebSockets,
1264
+ match:
1265
+ {
1266
+ content:
1267
+ {
1268
+ include: ['package.json', '**/*.{ts,js,py,go}'],
1269
+ pattern: '(socket\\.io|\\bws\\b|websocket)',
1270
+ },
1271
+ },
1272
+ }
1273
+
1274
+ # ------------------------------------------------------------------- testing
1275
+ - {
1276
+ fact: 'test:vitest',
1277
+ category: test,
1278
+ title: Vitest,
1279
+ match: { manifest: { file: 'package.json', key: 'devDependencies.vitest' } },
1280
+ }
1281
+ - {
1282
+ fact: 'test:jest',
1283
+ category: test,
1284
+ title: Jest,
1285
+ match: { manifest: { file: 'package.json', key: 'devDependencies.jest' } },
1286
+ }
1287
+ - {
1288
+ fact: 'test:mocha',
1289
+ category: test,
1290
+ title: Mocha,
1291
+ match: { manifest: { file: 'package.json', key: 'devDependencies.mocha' } },
1292
+ }
1293
+ - {
1294
+ fact: 'test:pytest',
1295
+ category: test,
1296
+ title: pytest,
1297
+ match:
1298
+ {
1299
+ any_of:
1300
+ [
1301
+ {
1302
+ content:
1303
+ { include: ['pyproject.toml', 'requirements*.txt'], pattern: '^pytest[=<>~]' },
1304
+ },
1305
+ { any_file: ['**/test_*.py', '**/*_test.py'] },
1306
+ ],
1307
+ },
1308
+ }
1309
+ - { fact: 'test:gotest', category: test, title: 'go test', match: { any_file: ['**/*_test.go'] } }
1310
+ - {
1311
+ fact: 'test:cargotest',
1312
+ category: test,
1313
+ title: 'cargo test',
1314
+ match:
1315
+ {
1316
+ any_of:
1317
+ [
1318
+ { any_file: ['**/*.rs'] },
1319
+ { content: { include: ['**/*.rs'], pattern: '#\\[test\\]' } },
1320
+ ],
1321
+ },
1322
+ }
1323
+ - {
1324
+ fact: 'test:junit',
1325
+ category: test,
1326
+ title: JUnit,
1327
+ match:
1328
+ {
1329
+ any_of:
1330
+ [
1331
+ { content: { include: ['**/*.java'], pattern: 'org\\.junit' } },
1332
+ { content: { include: ['**/*.kt'], pattern: 'org\\.junit|io\\.kotest' } },
1333
+ ],
1334
+ },
1335
+ }
1336
+ - {
1337
+ fact: 'test:rspec',
1338
+ category: test,
1339
+ title: RSpec,
1340
+ match:
1341
+ {
1342
+ any_of:
1343
+ [
1344
+ { any_file: ['**/*_spec.rb'] },
1345
+ { content: { include: ['Gemfile'], pattern: 'gem [''"]rspec' } },
1346
+ ],
1347
+ },
1348
+ }
1349
+ - {
1350
+ fact: 'test:phpunit',
1351
+ category: test,
1352
+ title: PHPUnit,
1353
+ match: { content: { include: ['**/*.php'], pattern: 'PHPUnit' } },
1354
+ }
1355
+ - {
1356
+ fact: 'test:playwright',
1357
+ category: test,
1358
+ title: Playwright,
1359
+ match: { manifest: { file: 'package.json', key: 'devDependencies.@playwright/test' } },
1360
+ }
1361
+ - {
1362
+ fact: 'test:cypress',
1363
+ category: test,
1364
+ title: Cypress,
1365
+ match: { manifest: { file: 'package.json', key: 'devDependencies.cypress' } },
1366
+ }
1367
+ - {
1368
+ fact: 'has:tests',
1369
+ category: test,
1370
+ title: 'Has tests',
1371
+ match:
1372
+ {
1373
+ any_of:
1374
+ [
1375
+ {
1376
+ any_file:
1377
+ [
1378
+ '**/*.test.ts',
1379
+ '**/*.test.js',
1380
+ '**/*.test.tsx',
1381
+ '**/*.test.jsx',
1382
+ '**/*.spec.ts',
1383
+ '**/*.spec.js',
1384
+ '**/test_*.py',
1385
+ '**/*_test.py',
1386
+ '**/*_test.go',
1387
+ '**/*_spec.rb',
1388
+ '**/*Test.java',
1389
+ '**/*Test.kt',
1390
+ ],
1391
+ },
1392
+ { dir_exists: ['tests', 'test', '__tests__', 'spec'] },
1393
+ ],
1394
+ },
1395
+ }
1396
+ - {
1397
+ fact: 'has:e2e',
1398
+ category: test,
1399
+ title: 'Has E2E tests',
1400
+ match:
1401
+ {
1402
+ any_of:
1403
+ [
1404
+ { dir_exists: ['e2e', 'cypress', 'playwright'] },
1405
+ { any_file: ['**/*.e2e.*', '**/*.cy.*', '**/*.spec.ts', '**/*.spec.js'] },
1406
+ ],
1407
+ },
1408
+ }
1409
+ - {
1410
+ fact: 'has:perf-tests',
1411
+ category: test,
1412
+ title: 'Has performance tests',
1413
+ match:
1414
+ {
1415
+ any_of:
1416
+ [
1417
+ { any_file: ['**/*.perf.*', '**/*.bench.*', '**/*.load.*', '**/k6/**'] },
1418
+ { dir_exists: ['benchmarks', 'perf', 'load-tests'] },
1419
+ ],
1420
+ },
1421
+ }
1422
+
1423
+ # ----------------------------------------------------------------- tooling
1424
+ - {
1425
+ fact: 'has:lint',
1426
+ category: tooling,
1427
+ title: Linter configured,
1428
+ match:
1429
+ {
1430
+ any_of:
1431
+ [
1432
+ {
1433
+ any_file:
1434
+ [
1435
+ '**/.eslintrc*',
1436
+ 'eslint.config.*',
1437
+ '.ruff.toml',
1438
+ '.flake8',
1439
+ '.pylintrc',
1440
+ 'clippy.toml',
1441
+ '.golangci*',
1442
+ ],
1443
+ },
1444
+ {
1445
+ content:
1446
+ {
1447
+ include: ['package.json', 'pyproject.toml'],
1448
+ pattern: '"(eslint|ruff|biome)"\\s*:|^\\[tool\\.(ruff|pylint)\\]',
1449
+ },
1450
+ },
1451
+ ],
1452
+ },
1453
+ }
1454
+ - {
1455
+ fact: 'has:format',
1456
+ category: tooling,
1457
+ title: Formatter configured,
1458
+ match:
1459
+ {
1460
+ any_of:
1461
+ [
1462
+ {
1463
+ any_file:
1464
+ [
1465
+ '.prettierrc*',
1466
+ 'prettier.config.*',
1467
+ '.rustfmt.toml',
1468
+ 'rustfmt.toml',
1469
+ '.clang-format',
1470
+ ],
1471
+ },
1472
+ {
1473
+ content:
1474
+ {
1475
+ include: ['package.json', 'pyproject.toml'],
1476
+ pattern: '"prettier"\\s*:|^\\[tool\\.black\\]',
1477
+ },
1478
+ },
1479
+ ],
1480
+ },
1481
+ }
1482
+ - {
1483
+ fact: 'has:typecheck',
1484
+ category: tooling,
1485
+ title: Type checking,
1486
+ match:
1487
+ {
1488
+ any_of:
1489
+ [
1490
+ { file_exists: ['tsconfig.json'] },
1491
+ { content: { include: ['package.json'], pattern: '"typescript"\\s*:' } },
1492
+ { content: { include: ['pyproject.toml'], pattern: '^\\[tool\\.mypy\\]' } },
1493
+ ],
1494
+ },
1495
+ }
1496
+ - {
1497
+ fact: 'has:precommit',
1498
+ category: tooling,
1499
+ title: Pre-commit hooks,
1500
+ match:
1501
+ {
1502
+ any_of:
1503
+ [
1504
+ { file_exists: ['.pre-commit-config.yaml', '.husky/pre-commit', 'lefthook.yml'] },
1505
+ { manifest: { file: 'package.json', key: 'devDependencies.husky' } },
1506
+ ],
1507
+ },
1508
+ }
1509
+ - {
1510
+ fact: 'has:dependabot',
1511
+ category: tooling,
1512
+ title: Dependabot,
1513
+ match: { file_exists: ['.github/dependabot.yml'] },
1514
+ }
1515
+ - {
1516
+ fact: 'has:renovate',
1517
+ category: tooling,
1518
+ title: Renovate,
1519
+ match: { any_file: ['renovate.json*', '.github/renovate.json*', '.renovaterc*'] },
1520
+ }
1521
+ - {
1522
+ fact: 'has:changesets',
1523
+ category: tooling,
1524
+ title: Changesets,
1525
+ match: { dir_exists: ['.changeset'] },
1526
+ }
1527
+ - {
1528
+ fact: 'has:release-automation',
1529
+ category: tooling,
1530
+ title: Release automation,
1531
+ match:
1532
+ {
1533
+ any_of:
1534
+ [
1535
+ { dir_exists: ['.changeset'] },
1536
+ {
1537
+ content:
1538
+ {
1539
+ include: ['package.json', '.github/workflows/*.yml'],
1540
+ pattern: 'semantic-release',
1541
+ },
1542
+ },
1543
+ ],
1544
+ },
1545
+ }
1546
+ - {
1547
+ fact: 'has:editorconfig',
1548
+ category: tooling,
1549
+ title: EditorConfig,
1550
+ match: { file_exists: ['.editorconfig'] },
1551
+ }
1552
+ - {
1553
+ fact: 'has:containers',
1554
+ category: tooling,
1555
+ title: Containers,
1556
+ match:
1557
+ {
1558
+ any_of:
1559
+ [
1560
+ {
1561
+ any_file:
1562
+ ['Dockerfile*', 'docker-compose*.yml', 'docker-compose*.yaml', 'compose.yaml'],
1563
+ },
1564
+ { dir_exists: ['.devcontainer'] },
1565
+ ],
1566
+ },
1567
+ }
1568
+ - {
1569
+ fact: 'has:kubernetes',
1570
+ category: tooling,
1571
+ title: Kubernetes,
1572
+ match:
1573
+ {
1574
+ any_of:
1575
+ [
1576
+ {
1577
+ content:
1578
+ {
1579
+ include: ['**/*.yaml', '**/*.yml'],
1580
+ pattern: 'kind:\\s*(Deployment|Service|StatefulSet|CronJob)',
1581
+ },
1582
+ },
1583
+ { dir_exists: ['k8s', 'kube', 'manifests', 'charts'] },
1584
+ ],
1585
+ },
1586
+ }
1587
+ - {
1588
+ fact: 'has:terraform',
1589
+ category: tooling,
1590
+ title: Terraform/OpenTofu,
1591
+ match: { any_file: ['**/*.tf'] },
1592
+ }
1593
+ - {
1594
+ fact: 'has:serverless',
1595
+ category: tooling,
1596
+ title: Serverless,
1597
+ match:
1598
+ {
1599
+ any_file: ['serverless.yml', 'serverless.ts', 'vercel.json', 'netlify.toml', 'fly.toml'],
1600
+ },
1601
+ }
1602
+ - {
1603
+ fact: 'has:sbom',
1604
+ category: tooling,
1605
+ title: SBOM published,
1606
+ match:
1607
+ { any_file: ['**/sbom.*', '**/*sbom*.json', '**/*sbom*.xml', '.github/workflows/*sbom*'] },
1608
+ }
1609
+ - {
1610
+ fact: 'has:secrets-scan',
1611
+ category: tooling,
1612
+ title: Secret scanning in CI,
1613
+ match:
1614
+ {
1615
+ any_of:
1616
+ [
1617
+ { file_exists: ['.github/workflows/*secret*'] },
1618
+ {
1619
+ content:
1620
+ {
1621
+ include: ['.github/workflows/*.yml'],
1622
+ pattern: '(gitleaks|trufflehog|secret-scanning|detect-secrets)',
1623
+ },
1624
+ },
1625
+ ],
1626
+ },
1627
+ }
1628
+ - {
1629
+ fact: 'has:sast',
1630
+ category: tooling,
1631
+ title: SAST in CI,
1632
+ match:
1633
+ {
1634
+ content:
1635
+ {
1636
+ include: ['.github/workflows/*.yml'],
1637
+ pattern: '(codeql|semgrep|snyk|sonar|bandit|gosec|clippy-sarif|trivy)',
1638
+ },
1639
+ },
1640
+ }
1641
+ - {
1642
+ fact: 'has:dep-scan',
1643
+ category: tooling,
1644
+ title: Dependency scanning,
1645
+ match:
1646
+ {
1647
+ content:
1648
+ {
1649
+ include: ['.github/workflows/*.yml'],
1650
+ pattern: '(npm audit|pnpm audit|pip-audit|osv-scanner|snyk test|trivy fs|dependabot|renovate|grype)',
1651
+ },
1652
+ },
1653
+ }
1654
+ - {
1655
+ fact: 'has:codecov',
1656
+ category: tooling,
1657
+ title: Coverage tracking,
1658
+ match:
1659
+ {
1660
+ any_of:
1661
+ [
1662
+ { any_file: ['.codecov.yml', 'codecov.yml', '.coveragerc'] },
1663
+ {
1664
+ content:
1665
+ { include: ['.github/workflows/*.yml'], pattern: 'codecov|coveralls|coverage' },
1666
+ },
1667
+ ],
1668
+ },
1669
+ }
1670
+
1671
+ # ------------------------------------------------------------------------ ci
1672
+ - {
1673
+ fact: 'ci:github-actions',
1674
+ category: ci,
1675
+ title: 'GitHub Actions',
1676
+ match: { dir_exists: ['.github/workflows'] },
1677
+ }
1678
+ - {
1679
+ fact: 'ci:gitlab',
1680
+ category: ci,
1681
+ title: 'GitLab CI',
1682
+ match: { file_exists: ['.gitlab-ci.yml'] },
1683
+ }
1684
+ - { fact: 'ci:jenkins', category: ci, title: Jenkins, match: { file_exists: ['Jenkinsfile'] } }
1685
+ - { fact: 'ci:circleci', category: ci, title: CircleCI, match: { dir_exists: ['.circleci'] } }
1686
+ - {
1687
+ fact: 'ci:buildkite',
1688
+ category: ci,
1689
+ title: Buildkite,
1690
+ match: { file_exists: ['.buildkite/pipeline.yml'] },
1691
+ }
1692
+ - {
1693
+ fact: 'ci:azure',
1694
+ category: ci,
1695
+ title: 'Azure Pipelines',
1696
+ match: { file_exists: ['azure-pipelines.yml'] },
1697
+ }
1698
+ - { fact: 'has:ci', category: ci, title: 'Has CI', implies: ['ci:github-actions'] }
1699
+ - { fact: 'has:ci', category: ci, title: 'Has CI', implies: ['ci:gitlab'] }
1700
+ - { fact: 'has:ci', category: ci, title: 'Has CI', implies: ['ci:jenkins'] }
1701
+ - { fact: 'has:ci', category: ci, title: 'Has CI', implies: ['ci:circleci'] }
1702
+ - { fact: 'has:ci', category: ci, title: 'Has CI', implies: ['ci:buildkite'] }
1703
+ - { fact: 'has:ci', category: ci, title: 'Has CI', implies: ['ci:azure'] }
1704
+
1705
+ # ----------------------------------------------------------- observability
1706
+ - {
1707
+ fact: 'obs:sentry',
1708
+ category: obs,
1709
+ title: Sentry,
1710
+ match:
1711
+ {
1712
+ content:
1713
+ {
1714
+ include: ['package.json', 'requirements*.txt', '**/*.{ts,js,py}'],
1715
+ pattern: '@sentry/|sentry-sdk',
1716
+ },
1717
+ },
1718
+ }
1719
+ - {
1720
+ fact: 'obs:otel',
1721
+ category: obs,
1722
+ title: OpenTelemetry,
1723
+ match:
1724
+ {
1725
+ content:
1726
+ {
1727
+ include:
1728
+ ['package.json', 'requirements*.txt', 'go.mod', 'Cargo.toml', '**/*.{ts,js,py,go}'],
1729
+ pattern: 'opentelemetry',
1730
+ },
1731
+ },
1732
+ }
1733
+ - {
1734
+ fact: 'obs:prometheus',
1735
+ category: obs,
1736
+ title: Prometheus,
1737
+ match:
1738
+ {
1739
+ any_of:
1740
+ [
1741
+ { any_file: ['**/prometheus*.yml'] },
1742
+ {
1743
+ content:
1744
+ {
1745
+ include: ['package.json', 'go.mod'],
1746
+ pattern: 'prom-client|prometheus/client_golang',
1747
+ },
1748
+ },
1749
+ ],
1750
+ },
1751
+ }
1752
+ - {
1753
+ fact: 'obs:datadog',
1754
+ category: obs,
1755
+ title: Datadog,
1756
+ match:
1757
+ { content: { include: ['**/*.{yml,yaml,json,ts,js,py}'], pattern: 'datadog|dd-trace' } },
1758
+ }
1759
+ - {
1760
+ fact: 'obs:grafana',
1761
+ category: obs,
1762
+ title: Grafana,
1763
+ match: { any_of: [{ dir_exists: ['grafana'] }, { any_file: ['**/grafana/**/*.json'] }] },
1764
+ }
1765
+ - {
1766
+ fact: 'has:monitoring',
1767
+ category: obs,
1768
+ title: Monitoring configured,
1769
+ match:
1770
+ {
1771
+ any_of:
1772
+ [
1773
+ { dir_exists: ['monitoring', 'observability', 'grafana'] },
1774
+ {
1775
+ content:
1776
+ {
1777
+ include: ['**/*.{yml,yaml,json}'],
1778
+ pattern: 'datadog|newrelic|grafana|prometheus',
1779
+ },
1780
+ },
1781
+ ],
1782
+ },
1783
+ }
1784
+
1785
+ # ---------------------------------------------------------------------- ai
1786
+ - {
1787
+ fact: 'ai:llm-sdk',
1788
+ category: ai,
1789
+ title: LLM SDK,
1790
+ match:
1791
+ {
1792
+ content:
1793
+ {
1794
+ include: ['package.json', 'requirements*.txt', 'pyproject.toml', 'go.mod'],
1795
+ pattern: '(openai|anthropic|@anthropic-ai|ai-sdk|langchain|llamaindex|google-generativeai|cohere|mistralai|bedrock|vertexai)',
1796
+ },
1797
+ },
1798
+ }
1799
+ - {
1800
+ fact: 'ai:prompts',
1801
+ category: ai,
1802
+ title: Prompt assets,
1803
+ match:
1804
+ {
1805
+ any_of:
1806
+ [
1807
+ { dir_exists: ['prompts'] },
1808
+ {
1809
+ any_file: ['**/*.prompt', '**/*.prompt.md', '**/prompts/*.md', '**/prompts/*.yaml'],
1810
+ },
1811
+ ],
1812
+ },
1813
+ }
1814
+ - {
1815
+ fact: 'ai:agents',
1816
+ category: ai,
1817
+ title: 'Agent / agentic code',
1818
+ match:
1819
+ {
1820
+ any_of:
1821
+ [
1822
+ {
1823
+ file_exists:
1824
+ [
1825
+ 'AGENTS.md',
1826
+ 'CLAUDE.md',
1827
+ '.cursorrules',
1828
+ '.github/copilot-instructions.md',
1829
+ 'codex.md',
1830
+ '.windsurfrules',
1831
+ ],
1832
+ },
1833
+ { dir_exists: ['agents', '.claude/agents'] },
1834
+ {
1835
+ content:
1836
+ {
1837
+ include: ['package.json', 'requirements*.txt', 'pyproject.toml'],
1838
+ pattern: '(langgraph|crewai|autogen|@openai/agents|mastra|smolagents|pydantic-ai|llamaindex)',
1839
+ },
1840
+ },
1841
+ ],
1842
+ },
1843
+ }
1844
+ - {
1845
+ fact: 'ai:mcp',
1846
+ category: ai,
1847
+ title: 'MCP servers / tool use',
1848
+ match:
1849
+ {
1850
+ any_of:
1851
+ [
1852
+ {
1853
+ content:
1854
+ {
1855
+ include: ['**/*.{json,jsonc}', '.mcp.json', '**/.mcp.json', '**/mcp.json'],
1856
+ pattern: '"mcpServers"|modelcontextprotocol',
1857
+ },
1858
+ },
1859
+ { content: { include: ['package.json'], pattern: '@modelcontextprotocol' } },
1860
+ ],
1861
+ },
1862
+ }
1863
+ - { fact: 'ai:rag', category: ai, title: 'RAG pipeline', implies: ['db:vector'] }
1864
+ - {
1865
+ fact: 'ai:rag',
1866
+ category: ai,
1867
+ title: 'RAG pipeline',
1868
+ match:
1869
+ {
1870
+ content: { include: ['**/*.{ts,js,py}'], pattern: '(embedding|retriev|rerank|chunking)' },
1871
+ },
1872
+ }
1873
+ - {
1874
+ fact: 'ai:evals',
1875
+ category: ai,
1876
+ title: LLM evals,
1877
+ match:
1878
+ {
1879
+ any_of:
1880
+ [
1881
+ { dir_exists: ['evals', 'eval'] },
1882
+ { any_file: ['**/*.eval.{ts,js,py}', '**/evals/**'] },
1883
+ ],
1884
+ },
1885
+ }
1886
+
1887
+ # ------------------------------------------------------------------- docs
1888
+ - {
1889
+ fact: 'doc:readme',
1890
+ category: doc,
1891
+ title: README,
1892
+ match: { any_file: ['README.md', 'readme.md', 'README.rst', 'README.txt', 'README'] },
1893
+ }
1894
+ - {
1895
+ fact: 'doc:license',
1896
+ category: doc,
1897
+ title: LICENSE,
1898
+ match: { any_file: ['LICENSE*', 'COPYING*'] },
1899
+ }
1900
+ - {
1901
+ fact: 'doc:contributing',
1902
+ category: doc,
1903
+ title: CONTRIBUTING,
1904
+ match: { any_file: ['CONTRIBUTING*'] },
1905
+ }
1906
+ - {
1907
+ fact: 'doc:code-of-conduct',
1908
+ category: doc,
1909
+ title: 'Code of conduct',
1910
+ match: { any_file: ['CODE_OF_CONDUCT*'] },
1911
+ }
1912
+ - {
1913
+ fact: 'doc:security-policy',
1914
+ category: doc,
1915
+ title: SECURITY.md,
1916
+ match: { any_file: ['SECURITY.md', '.github/SECURITY.md', 'docs/SECURITY.md'] },
1917
+ }
1918
+ - {
1919
+ fact: 'doc:changelog',
1920
+ category: doc,
1921
+ title: CHANGELOG,
1922
+ match: { any_file: ['CHANGELOG*', 'CHANGES*', 'HISTORY.md'] },
1923
+ }
1924
+ - {
1925
+ fact: 'doc:architecture',
1926
+ category: doc,
1927
+ title: Architecture docs,
1928
+ match:
1929
+ {
1930
+ any_of:
1931
+ [
1932
+ { dir_exists: ['docs/adrs', 'docs/adr', 'adr', 'docs/architecture'] },
1933
+ {
1934
+ any_file:
1935
+ [
1936
+ 'docs/ARCHITECTURE.md',
1937
+ 'ARCHITECTURE.md',
1938
+ 'docs/**/adr-*.md',
1939
+ 'docs/**/*.adr.md',
1940
+ ],
1941
+ },
1942
+ ],
1943
+ },
1944
+ }
1945
+ - {
1946
+ fact: 'doc:adr',
1947
+ category: doc,
1948
+ title: ADRs,
1949
+ match:
1950
+ {
1951
+ any_of:
1952
+ [
1953
+ { dir_exists: ['docs/adrs', 'docs/adr', 'adr', 'docs/decisions'] },
1954
+ { any_file: ['**/adr-*.md', '**/ADR-*.md'] },
1955
+ ],
1956
+ },
1957
+ }
1958
+ - {
1959
+ fact: 'doc:api-reference',
1960
+ category: doc,
1961
+ title: API reference,
1962
+ match:
1963
+ {
1964
+ any_of:
1965
+ [
1966
+ { any_file: ['**/openapi.yaml', '**/openapi.json', '**/swagger.*', '**/*.graphql'] },
1967
+ { dir_exists: ['docs/api'] },
1968
+ ],
1969
+ },
1970
+ }
1971
+ - {
1972
+ fact: 'doc:runbook',
1973
+ category: doc,
1974
+ title: Runbook,
1975
+ match:
1976
+ {
1977
+ any_of:
1978
+ [
1979
+ { any_file: ['**/RUNBOOK*', '**/runbook*'] },
1980
+ { dir_exists: ['docs/runbooks', 'runbooks'] },
1981
+ ],
1982
+ },
1983
+ }
1984
+ - {
1985
+ fact: 'doc:onboarding',
1986
+ category: doc,
1987
+ title: Onboarding guide,
1988
+ match:
1989
+ {
1990
+ any_of:
1991
+ [
1992
+ { any_file: ['**/ONBOARDING*', '**/onboarding*', 'docs/GETTING_STARTED*'] },
1993
+ { dir_exists: ['docs/onboarding'] },
1994
+ ],
1995
+ },
1996
+ }
1997
+ - {
1998
+ fact: 'doc:env-example',
1999
+ category: doc,
2000
+ title: '.env.example',
2001
+ match: { any_file: ['.env.example', '.env.sample', '.env.template', 'env.example'] },
2002
+ }
2003
+ - {
2004
+ fact: 'doc:gitignore',
2005
+ category: doc,
2006
+ title: '.gitignore',
2007
+ match: { file_exists: ['.gitignore'] },
2008
+ }
2009
+ - {
2010
+ fact: 'doc:codeowners',
2011
+ category: doc,
2012
+ title: CODEOWNERS,
2013
+ match: { any_file: ['.github/CODEOWNERS', 'CODEOWNERS', 'docs/CODEOWNERS'] },
2014
+ }
2015
+ - {
2016
+ fact: 'doc:pr-template',
2017
+ category: doc,
2018
+ title: PR template,
2019
+ match: { any_file: ['.github/PULL_REQUEST_TEMPLATE*', 'docs/PULL_REQUEST_TEMPLATE*'] },
2020
+ }
2021
+ - {
2022
+ fact: 'doc:issue-templates',
2023
+ category: doc,
2024
+ title: Issue templates,
2025
+ match: { dir_exists: ['.github/ISSUE_TEMPLATE'] },
2026
+ }
2027
+ - {
2028
+ fact: 'doc:agents',
2029
+ category: doc,
2030
+ title: 'Agent instruction file',
2031
+ match:
2032
+ {
2033
+ any_file:
2034
+ [
2035
+ 'AGENTS.md',
2036
+ 'CLAUDE.md',
2037
+ '.cursorrules',
2038
+ '.github/copilot-instructions.md',
2039
+ 'codex.md',
2040
+ ],
2041
+ },
2042
+ }
2043
+
2044
+ # --------------------------------------------------------------- practices
2045
+ - {
2046
+ fact: 'practice:conventional-commits',
2047
+ category: practice,
2048
+ title: Conventional commits,
2049
+ match:
2050
+ {
2051
+ any_of:
2052
+ [
2053
+ {
2054
+ content:
2055
+ {
2056
+ include:
2057
+ ['CONTRIBUTING.md', 'docs/**/*.md', 'commitlint.config.*', '.versionrc*'],
2058
+ pattern: 'conventional[- ]commits?',
2059
+ },
2060
+ },
2061
+ { any_file: ['commitlint.config.*', '.commitlintrc*'] },
2062
+ ],
2063
+ },
2064
+ }
2065
+ - {
2066
+ fact: 'practice:codeowners',
2067
+ category: practice,
2068
+ title: Code owners,
2069
+ implies: ['doc:codeowners'],
2070
+ }
2071
+ - {
2072
+ fact: 'practice:signed-commits',
2073
+ category: practice,
2074
+ title: Signed commits/attestations,
2075
+ match:
2076
+ {
2077
+ content:
2078
+ {
2079
+ include: ['.github/workflows/*.yml'],
2080
+ pattern: '(actions/attest|sigstore|gpg-sign|slsa)',
2081
+ },
2082
+ },
2083
+ }
2084
+ - {
2085
+ fact: 'practice:branch-protection-doc',
2086
+ category: practice,
2087
+ title: Branch protection documented,
2088
+ match:
2089
+ {
2090
+ any_of:
2091
+ [
2092
+ { any_file: ['.github/branch-protection*', 'docs/BRANCHING*'] },
2093
+ { content: { include: ['**/*.md'], pattern: 'branch protection' } },
2094
+ ],
2095
+ },
2096
+ }
2097
+ - {
2098
+ fact: 'practice:pinned-versions',
2099
+ category: practice,
2100
+ title: Pinned dependency versions,
2101
+ match:
2102
+ {
2103
+ content:
2104
+ {
2105
+ include: ['package.json', 'requirements*.txt'],
2106
+ pattern: '"[^"]*":\\s*"\\d+\\.\\d+\\.\\d+"|^[a-z0-9-]+==\\d',
2107
+ },
2108
+ },
2109
+ }
2110
+ - {
2111
+ fact: 'practice:i18n',
2112
+ category: practice,
2113
+ title: i18n,
2114
+ match:
2115
+ {
2116
+ any_of:
2117
+ [
2118
+ { dir_exists: ['i18n', 'locales', 'translations', 'src/i18n'] },
2119
+ {
2120
+ content:
2121
+ {
2122
+ include: ['package.json'],
2123
+ pattern: '(i18next|react-intl|vue-i18n|lingui|next-intl)',
2124
+ },
2125
+ },
2126
+ ],
2127
+ },
2128
+ }
2129
+ - {
2130
+ fact: 'practice:feature-flags',
2131
+ category: practice,
2132
+ title: Feature flags,
2133
+ match:
2134
+ {
2135
+ content:
2136
+ {
2137
+ include: ['**/*.{ts,js,py,go,rs}'],
2138
+ pattern: '(launchdarkly|unleash|flagsmith|growthbook|posthog.*flag|isFeatureEnabled|featureFlag)',
2139
+ },
2140
+ },
2141
+ }
2142
+ - {
2143
+ fact: 'practice:queue',
2144
+ category: practice,
2145
+ title: Background jobs/queues,
2146
+ match:
2147
+ {
2148
+ content:
2149
+ {
2150
+ include: ['package.json', 'requirements*.txt', 'go.mod', '**/*.{ts,js,py,go}'],
2151
+ pattern: '(bullmq|bull|amqplib|kafkajs|celery|rq|sqs|nats|temporal)',
2152
+ },
2153
+ },
2154
+ }
2155
+ - { fact: 'practice:cache', category: practice, title: Caching layer, implies: ['db:redis'] }
2156
+ - {
2157
+ fact: 'practice:monorepo-tool-turborepo',
2158
+ category: practice,
2159
+ title: Turborepo,
2160
+ match: { file_exists: ['turbo.json'] },
2161
+ }
2162
+ - {
2163
+ fact: 'practice:monorepo-tool-nx',
2164
+ category: practice,
2165
+ title: Nx,
2166
+ match: { file_exists: ['nx.json'] },
2167
+ }
2168
+
2169
+ # ---------------------------------------------------------------- security
2170
+ - {
2171
+ fact: 'has:crypto',
2172
+ category: security,
2173
+ title: Cryptography usage,
2174
+ match:
2175
+ {
2176
+ content:
2177
+ {
2178
+ include: ['**/*.{ts,js,py,go,rs,java}'],
2179
+ pattern: '(bcrypt|argon2|scrypt|pbkdf2|crypto\\.createCipher|createHash|jose|nacl)',
2180
+ },
2181
+ },
2182
+ }
2183
+ - {
2184
+ fact: 'has:file-upload',
2185
+ category: security,
2186
+ title: File uploads,
2187
+ match:
2188
+ {
2189
+ content:
2190
+ {
2191
+ include: ['**/*.{ts,js,py,go,rs}'],
2192
+ pattern: '(multer|formidable|busboy|UploadedFile|multipart)',
2193
+ },
2194
+ },
2195
+ }
2196
+ - {
2197
+ fact: 'has:payments',
2198
+ category: security,
2199
+ title: Payments,
2200
+ match:
2201
+ {
2202
+ content:
2203
+ {
2204
+ include: ['package.json', 'requirements*.txt', '**/*.{ts,js,py}'],
2205
+ pattern: '(stripe|braintree|paypal|razorpay|adyen|@stripe/)',
2206
+ },
2207
+ },
2208
+ }
2209
+ - {
2210
+ fact: 'has:pii',
2211
+ category: security,
2212
+ title: PII handling,
2213
+ match:
2214
+ {
2215
+ content:
2216
+ {
2217
+ include: ['**/*.{ts,js,py,sql,prisma}'],
2218
+ pattern: '(\\bssn\\b|social[_-]?security|date[_-]?of[_-]?birth|passport[_-]?(no|number)|credit[_-]?card|card[_-]?number|phone[_-]?number|email[_-]?address|home[_-]?address|ip[_-]?address)',
2219
+ },
2220
+ },
2221
+ }
2222
+ - {
2223
+ fact: 'has:csp',
2224
+ category: security,
2225
+ title: CSP / security headers,
2226
+ match:
2227
+ {
2228
+ content:
2229
+ {
2230
+ include: ['**/*.{ts,js,json,conf,nginx,yaml,yml}'],
2231
+ pattern: '(Content-Security-Policy|helmet|Strict-Transport-Security)',
2232
+ },
2233
+ },
2234
+ }
2235
+ - {
2236
+ fact: 'has:rate-limit',
2237
+ category: security,
2238
+ title: Rate limiting,
2239
+ match:
2240
+ {
2241
+ content:
2242
+ {
2243
+ include: ['package.json', '**/*.{ts,js,py,go}'],
2244
+ pattern: '(express-rate-limit|rate-limit|ratelimit|tower.*limit|slowapi)',
2245
+ },
2246
+ },
2247
+ }
2248
+
2249
+ # ------------------------------------------------------------------ git
2250
+ - {
2251
+ fact: 'git:initialized',
2252
+ category: git,
2253
+ title: Git repository,
2254
+ match: { dir_exists: ['.git'] },
2255
+ }