@nerviq/cli 1.0.0 → 1.2.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 (48) hide show
  1. package/bin/cli.js +170 -73
  2. package/package.json +3 -5
  3. package/src/activity.js +20 -0
  4. package/src/aider/domain-packs.js +27 -2
  5. package/src/aider/mcp-packs.js +231 -0
  6. package/src/aider/techniques.js +3210 -1397
  7. package/src/audit.js +290 -9
  8. package/src/catalog.js +18 -2
  9. package/src/codex/domain-packs.js +23 -1
  10. package/src/codex/mcp-packs.js +254 -0
  11. package/src/codex/techniques.js +4738 -3257
  12. package/src/copilot/domain-packs.js +23 -1
  13. package/src/copilot/mcp-packs.js +254 -0
  14. package/src/copilot/techniques.js +3433 -1936
  15. package/src/cursor/domain-packs.js +23 -1
  16. package/src/cursor/mcp-packs.js +257 -0
  17. package/src/cursor/techniques.js +3697 -1869
  18. package/src/deprecation.js +98 -0
  19. package/src/domain-pack-expansion.js +571 -0
  20. package/src/domain-packs.js +25 -2
  21. package/src/formatters/otel.js +151 -0
  22. package/src/gemini/domain-packs.js +23 -1
  23. package/src/gemini/mcp-packs.js +257 -0
  24. package/src/gemini/techniques.js +3734 -2238
  25. package/src/integrations.js +194 -0
  26. package/src/mcp-packs.js +233 -0
  27. package/src/opencode/domain-packs.js +23 -1
  28. package/src/opencode/mcp-packs.js +231 -0
  29. package/src/opencode/techniques.js +3500 -1687
  30. package/src/org.js +68 -0
  31. package/src/source-urls.js +410 -260
  32. package/src/stack-checks.js +565 -0
  33. package/src/supplemental-checks.js +767 -0
  34. package/src/techniques.js +2929 -1449
  35. package/src/telemetry.js +160 -0
  36. package/src/windsurf/domain-packs.js +23 -1
  37. package/src/windsurf/mcp-packs.js +257 -0
  38. package/src/windsurf/techniques.js +3647 -1834
  39. package/src/workspace.js +233 -0
  40. package/CHANGELOG.md +0 -198
  41. package/content/case-study-template.md +0 -91
  42. package/content/claims-governance.md +0 -37
  43. package/content/claude-code/audit-repo/SKILL.md +0 -20
  44. package/content/claude-native-integration.md +0 -60
  45. package/content/devto-article.json +0 -9
  46. package/content/launch-posts.md +0 -226
  47. package/content/pilot-rollout-kit.md +0 -30
  48. package/content/release-checklist.md +0 -31
@@ -0,0 +1,767 @@
1
+ const path = require('path');
2
+
3
+ function normalizeText(value) {
4
+ return String(value || '');
5
+ }
6
+
7
+ function getPackageJson(ctx) {
8
+ return (typeof ctx.jsonFile === 'function' ? ctx.jsonFile('package.json') : null) || {};
9
+ }
10
+
11
+ function getDependencies(ctx) {
12
+ const pkg = getPackageJson(ctx);
13
+ return Object.assign(
14
+ {},
15
+ pkg.dependencies || {},
16
+ pkg.devDependencies || {},
17
+ pkg.peerDependencies || {},
18
+ pkg.optionalDependencies || {}
19
+ );
20
+ }
21
+
22
+ function getScripts(ctx) {
23
+ return getPackageJson(ctx).scripts || {};
24
+ }
25
+
26
+ function hasFile(ctx, filePath) {
27
+ return Boolean(typeof ctx.fileContent === 'function' && ctx.fileContent(filePath));
28
+ }
29
+
30
+ function hasDir(ctx, dirPath) {
31
+ return Boolean(typeof ctx.hasDir === 'function' && ctx.hasDir(dirPath));
32
+ }
33
+
34
+ function dirFiles(ctx, dirPath) {
35
+ return typeof ctx.dirFiles === 'function' ? ctx.dirFiles(dirPath) : [];
36
+ }
37
+
38
+ function workflowPaths(ctx, explicitPaths) {
39
+ if (Array.isArray(explicitPaths) && explicitPaths.length > 0) {
40
+ return explicitPaths;
41
+ }
42
+
43
+ if (typeof ctx.workflowFiles === 'function') {
44
+ return ctx.workflowFiles() || [];
45
+ }
46
+
47
+ return dirFiles(ctx, '.github/workflows')
48
+ .filter((file) => /\.ya?ml$/i.test(file))
49
+ .map((file) => path.join('.github', 'workflows', file).replace(/\\/g, '/'));
50
+ }
51
+
52
+ function workflowText(ctx, explicitPaths) {
53
+ return workflowPaths(ctx, explicitPaths)
54
+ .map((file) => (typeof ctx.fileContent === 'function' ? ctx.fileContent(file) : null) || '')
55
+ .join('\n');
56
+ }
57
+
58
+ function fileListText(ctx) {
59
+ return Array.isArray(ctx.files) ? ctx.files.join('\n') : '';
60
+ }
61
+
62
+ function combinedText(parts) {
63
+ return parts.filter(Boolean).join('\n');
64
+ }
65
+
66
+ function hasAnyDependency(ctx, patterns) {
67
+ const deps = getDependencies(ctx);
68
+ const names = Object.keys(deps);
69
+ return patterns.some((pattern) => names.some((name) => pattern.test(name)));
70
+ }
71
+
72
+ function scriptMatches(ctx, patterns) {
73
+ const scripts = getScripts(ctx);
74
+ return Object.entries(scripts).some(([name, command]) =>
75
+ patterns.some((pattern) => pattern.test(`${name} ${command}`))
76
+ );
77
+ }
78
+
79
+ function fileMatches(ctx, patterns) {
80
+ const files = fileListText(ctx);
81
+ return patterns.some((pattern) => pattern.test(files));
82
+ }
83
+
84
+ function docMatches(text, patterns) {
85
+ return patterns.some((pattern) => pattern.test(text));
86
+ }
87
+
88
+ function configMatches(ctx, patterns) {
89
+ return patterns.some((pattern) => pattern.test(getConfigBundle(ctx)));
90
+ }
91
+
92
+ function hasManifest(ctx) {
93
+ return Boolean(
94
+ hasFile(ctx, 'package.json') ||
95
+ hasFile(ctx, 'pyproject.toml') ||
96
+ hasFile(ctx, 'requirements.txt') ||
97
+ hasFile(ctx, 'go.mod') ||
98
+ hasFile(ctx, 'Cargo.toml') ||
99
+ hasFile(ctx, 'Gemfile') ||
100
+ hasFile(ctx, 'composer.json')
101
+ );
102
+ }
103
+
104
+ function getConfigBundle(ctx) {
105
+ return combinedText([
106
+ typeof ctx.fileContent === 'function' ? ctx.fileContent('.claude/settings.json') : null,
107
+ typeof ctx.fileContent === 'function' ? ctx.fileContent('.codex/config.toml') : null,
108
+ typeof ctx.fileContent === 'function' ? ctx.fileContent('.gemini/settings.json') : null,
109
+ typeof ctx.fileContent === 'function' ? ctx.fileContent('.vscode/settings.json') : null,
110
+ typeof ctx.fileContent === 'function' ? ctx.fileContent('.vscode/mcp.json') : null,
111
+ typeof ctx.fileContent === 'function' ? ctx.fileContent('copilot-setup-steps.yml') : null,
112
+ ]);
113
+ }
114
+
115
+ function getProjectSurface(ctx, platformConfig) {
116
+ const docs = normalizeText(platformConfig.docs ? platformConfig.docs(ctx) : '');
117
+ const workflows = workflowText(ctx, platformConfig.workflowPaths ? platformConfig.workflowPaths(ctx) : null);
118
+ const config = getConfigBundle(ctx);
119
+ const files = fileListText(ctx);
120
+
121
+ return {
122
+ docs,
123
+ workflows,
124
+ config,
125
+ files,
126
+ dependencies: getDependencies(ctx),
127
+ scripts: getScripts(ctx),
128
+ manifest: hasManifest(ctx),
129
+ project: combinedText([docs, workflows, config, files]),
130
+ };
131
+ }
132
+
133
+ function hasRelevantProject(surface) {
134
+ return surface.manifest || Boolean(surface.docs) || Boolean(surface.workflows);
135
+ }
136
+
137
+ function hasApiSurface(surface) {
138
+ return /api|endpoint|rest|graphql|openapi|swagger|express|fastify|koa|hono|nestjs|router|route/i.test(surface.project);
139
+ }
140
+
141
+ function hasDatabaseSurface(surface) {
142
+ return /database|db|postgres|mysql|sqlite|mongo|redis|prisma|typeorm|drizzle|sequelize|alembic|migration/i.test(surface.project);
143
+ }
144
+
145
+ function hasAuthSurface(surface) {
146
+ return /auth|jwt|token|session|oauth|sso|oidc|saml|rbac|permission/i.test(surface.project);
147
+ }
148
+
149
+ function hasMonitoringSurface(surface) {
150
+ return /logging|logger|sentry|bugsnag|rollbar|otel|opentelemetry|prometheus|health|alert|metric|apm/i.test(surface.project);
151
+ }
152
+
153
+ function exactPinnedDependencies(ctx) {
154
+ const deps = getDependencies(ctx);
155
+ const versions = Object.values(deps);
156
+ if (versions.length === 0) return null;
157
+ return versions.every((value) => typeof value === 'string' && !/^[~^><*]/.test(value));
158
+ }
159
+
160
+ function dependencyLockfilePresent(ctx) {
161
+ return [
162
+ 'package-lock.json',
163
+ 'yarn.lock',
164
+ 'pnpm-lock.yaml',
165
+ 'bun.lockb',
166
+ 'Cargo.lock',
167
+ 'Gemfile.lock',
168
+ 'composer.lock',
169
+ 'poetry.lock',
170
+ ].some((file) => hasFile(ctx, file));
171
+ }
172
+
173
+ const CHECK_DEFS = [
174
+ {
175
+ key: 'testingStrategyFrameworkDetected',
176
+ suffix: '01',
177
+ name: 'Testing strategy: test framework detected',
178
+ category: 'testing-strategy',
179
+ impact: 'high',
180
+ fix: 'Document and install a primary test framework (for example Jest, Vitest, Playwright, pytest, or equivalent) so the repo has an explicit testing baseline.',
181
+ check: (ctx, surface) => hasRelevantProject(surface)
182
+ ? hasAnyDependency(ctx, [/\bjest\b/i, /\bvitest\b/i, /\bmocha\b/i, /\bava\b/i, /\bpytest\b/i, /\bplaywright\b/i, /\bcypress\b/i, /\brspec\b/i]) || scriptMatches(ctx, [/\btest\b/i])
183
+ : null,
184
+ },
185
+ {
186
+ key: 'testingStrategyCoverageConfigExists',
187
+ suffix: '02',
188
+ name: 'Testing strategy: coverage configuration exists',
189
+ category: 'testing-strategy',
190
+ impact: 'medium',
191
+ fix: 'Add a coverage configuration or script (`coverage`, `nyc`, `c8`, `coverageThreshold`, or equivalent) so test depth is measurable.',
192
+ check: (ctx, surface) => hasRelevantProject(surface)
193
+ ? scriptMatches(ctx, [/\bcoverage\b/i]) ||
194
+ hasAnyDependency(ctx, [/\bnyc\b/i, /\bc8\b/i]) ||
195
+ hasFile(ctx, 'jest.config.js') ||
196
+ hasFile(ctx, 'vitest.config.ts') ||
197
+ /coverageThreshold|collectCoverage|coverage/i.test(JSON.stringify(getPackageJson(ctx)))
198
+ : null,
199
+ },
200
+ {
201
+ key: 'testingStrategyE2ESetupPresent',
202
+ suffix: '03',
203
+ name: 'Testing strategy: E2E setup present',
204
+ category: 'testing-strategy',
205
+ impact: 'medium',
206
+ fix: 'Add an E2E harness such as Playwright or Cypress, or document why the repo intentionally relies on another end-to-end strategy.',
207
+ check: (ctx, surface) => hasRelevantProject(surface)
208
+ ? hasAnyDependency(ctx, [/\bplaywright\b/i, /\bcypress\b/i]) ||
209
+ hasDir(ctx, 'e2e') ||
210
+ hasDir(ctx, 'tests/e2e') ||
211
+ fileMatches(ctx, [/playwright\.config|cypress\.config|cypress\/|e2e\//i])
212
+ : null,
213
+ },
214
+ {
215
+ key: 'testingStrategySnapshotTestsMentioned',
216
+ suffix: '04',
217
+ name: 'Testing strategy: snapshot tests mentioned',
218
+ category: 'testing-strategy',
219
+ impact: 'low',
220
+ fix: 'Mention snapshot testing expectations or store snapshots in a conventional location so UI and serializer regressions are reviewable.',
221
+ check: (ctx, surface) => hasRelevantProject(surface)
222
+ ? docMatches(surface.project, [/\bsnapshot\b/i, /__snapshots__/i]) || hasDir(ctx, '__snapshots__')
223
+ : null,
224
+ },
225
+ {
226
+ key: 'testingStrategyTestCommandDocumented',
227
+ suffix: '05',
228
+ name: 'Testing strategy: test command documented',
229
+ category: 'testing-strategy',
230
+ impact: 'high',
231
+ fix: 'Document the canonical test command in repo instructions so contributors and agents can verify changes the same way.',
232
+ check: (ctx, surface) => hasRelevantProject(surface)
233
+ ? /\bnpm test\b|\bpnpm test\b|\byarn test\b|\bpytest\b|\bgo test\b|\bcargo test\b/i.test(surface.docs) || Boolean(getScripts(ctx).test)
234
+ : null,
235
+ },
236
+ {
237
+ key: 'testingStrategyCiRunsTests',
238
+ suffix: '06',
239
+ name: 'Testing strategy: CI runs tests',
240
+ category: 'testing-strategy',
241
+ impact: 'high',
242
+ fix: 'Make CI run the project test command so regressions are caught automatically rather than only in local sessions.',
243
+ check: (_ctx, surface) => surface.workflows
244
+ ? /\b(test|pytest|vitest|jest|go test|cargo test|playwright test|cypress run)\b/i.test(surface.workflows)
245
+ : null,
246
+ },
247
+ {
248
+ key: 'codeQualityLinterConfigured',
249
+ suffix: '07',
250
+ name: 'Code quality: linter configured',
251
+ category: 'code-quality',
252
+ impact: 'high',
253
+ fix: 'Configure a linter such as ESLint, Ruff, Flake8, or equivalent so code quality rules are enforceable and repeatable.',
254
+ check: (ctx, surface) => hasRelevantProject(surface)
255
+ ? hasAnyDependency(ctx, [/\beslint\b/i, /\bruff\b/i, /\bflake8\b/i, /\bpylint\b/i, /\bgolangci-lint\b/i]) ||
256
+ scriptMatches(ctx, [/\blint\b/i]) ||
257
+ fileMatches(ctx, [/eslint|\.ruff|flake8|pylintrc|golangci/i])
258
+ : null,
259
+ },
260
+ {
261
+ key: 'codeQualityFormatterConfigured',
262
+ suffix: '08',
263
+ name: 'Code quality: formatter configured',
264
+ category: 'code-quality',
265
+ impact: 'medium',
266
+ fix: 'Configure a formatter such as Prettier, Black, Ruff format, rustfmt, or equivalent so style drift does not become manual toil.',
267
+ check: (ctx, surface) => hasRelevantProject(surface)
268
+ ? hasAnyDependency(ctx, [/\bprettier\b/i, /\bblack\b/i, /\bruff\b/i]) ||
269
+ scriptMatches(ctx, [/\bformat\b/i]) ||
270
+ fileMatches(ctx, [/prettier|\.prettierrc|pyproject\.toml|rustfmt/i])
271
+ : null,
272
+ },
273
+ {
274
+ key: 'codeQualityDeadCodeDetection',
275
+ suffix: '09',
276
+ name: 'Code quality: dead code detection',
277
+ category: 'code-quality',
278
+ impact: 'medium',
279
+ fix: 'Add a dead-code scan (`knip`, `ts-prune`, `depcheck`, `vulture`, or equivalent) or document how the repo handles unused code removal.',
280
+ check: (ctx, surface) => hasRelevantProject(surface)
281
+ ? hasAnyDependency(ctx, [/\bknip\b/i, /\bts-prune\b/i, /\bdepcheck\b/i, /\bvulture\b/i]) ||
282
+ scriptMatches(ctx, [/\bknip\b/i, /\bts-prune\b/i, /\bdepcheck\b/i, /\bvulture\b/i]) ||
283
+ docMatches(surface.docs, [/\bdead code\b/i, /\bunused code\b/i])
284
+ : null,
285
+ },
286
+ {
287
+ key: 'codeQualityComplexityAwareness',
288
+ suffix: '10',
289
+ name: 'Code quality: complexity awareness',
290
+ category: 'code-quality',
291
+ impact: 'medium',
292
+ fix: 'Document or configure complexity guardrails (for example cyclomatic complexity or small-function guidance) so growth pressure stays visible.',
293
+ check: (ctx, surface) => hasRelevantProject(surface)
294
+ ? docMatches(surface.docs, [/\bcomplexity\b/i, /\bcyclomatic\b/i, /\bsmall functions\b/i, /\bkeep functions\b/i]) ||
295
+ configMatches(ctx, [/\bcomplexity\b/i, /\bmax-len\b/i, /\bmax-depth\b/i])
296
+ : null,
297
+ },
298
+ {
299
+ key: 'codeQualityConsistentNamingDocumented',
300
+ suffix: '11',
301
+ name: 'Code quality: consistent naming documented',
302
+ category: 'code-quality',
303
+ impact: 'medium',
304
+ fix: 'Document naming conventions such as camelCase, PascalCase, kebab-case, or snake_case so generated and hand-written code stay aligned.',
305
+ check: (_ctx, surface) => surface.docs
306
+ ? docMatches(surface.docs, [/\bcamelCase\b/, /\bPascalCase\b/, /\bkebab-case\b/, /\bsnake_case\b/, /\bnaming convention\b/i])
307
+ : null,
308
+ },
309
+ {
310
+ key: 'codeQualityCodeReviewProcessMentioned',
311
+ suffix: '12',
312
+ name: 'Code quality: code review process mentioned',
313
+ category: 'code-quality',
314
+ impact: 'medium',
315
+ fix: 'Mention the code review process in repo guidance or templates so contributors know how changes are checked before merge.',
316
+ check: (ctx, surface) => hasRelevantProject(surface)
317
+ ? docMatches(surface.project, [/\bcode review\b/i, /\bpull request\b/i, /\breviewer\b/i, /\bapproval\b/i]) ||
318
+ hasFile(ctx, '.github/pull_request_template.md')
319
+ : null,
320
+ },
321
+ {
322
+ key: 'apiDesignEndpointDocumentation',
323
+ suffix: '13',
324
+ name: 'API design: endpoint documentation present',
325
+ category: 'api-design',
326
+ impact: 'medium',
327
+ fix: 'Document the main API endpoints or ship an OpenAPI/Swagger description so integrations stay reviewable.',
328
+ check: (ctx, surface) => hasApiSurface(surface)
329
+ ? docMatches(surface.project, [/\bendpoint\b/i, /\bopenapi\b/i, /\bswagger\b/i, /\bgraphql\b/i, /\broute\b/i]) ||
330
+ hasFile(ctx, 'openapi.yaml') ||
331
+ hasFile(ctx, 'openapi.json') ||
332
+ hasFile(ctx, 'swagger.json')
333
+ : null,
334
+ },
335
+ {
336
+ key: 'apiDesignVersioningMentioned',
337
+ suffix: '14',
338
+ name: 'API design: versioning mentioned',
339
+ category: 'api-design',
340
+ impact: 'medium',
341
+ fix: 'Document the API versioning strategy (`v1`, header-based, or explicit stability policy) so breaking changes are easier to govern.',
342
+ check: (_ctx, surface) => hasApiSurface(surface)
343
+ ? docMatches(surface.project, [/\bapi version\b/i, /\/v[0-9]+\b/i, /\bversioning\b/i])
344
+ : null,
345
+ },
346
+ {
347
+ key: 'apiDesignErrorHandlingPatterns',
348
+ suffix: '15',
349
+ name: 'API design: error handling patterns defined',
350
+ category: 'api-design',
351
+ impact: 'high',
352
+ fix: 'Document the API error handling shape (for example problem+json, error envelopes, or standard status mapping) so clients get predictable failures.',
353
+ check: (_ctx, surface) => hasApiSurface(surface)
354
+ ? docMatches(surface.project, [/\berror handling\b/i, /\bproblem\+json\b/i, /\berror envelope\b/i, /\bstatus code\b/i])
355
+ : null,
356
+ },
357
+ {
358
+ key: 'apiDesignRateLimitingAwareness',
359
+ suffix: '16',
360
+ name: 'API design: rate limiting awareness',
361
+ category: 'api-design',
362
+ impact: 'medium',
363
+ fix: 'Mention rate limiting or throttling expectations so public and internal API surfaces have clear abuse boundaries.',
364
+ check: (ctx, surface) => hasApiSurface(surface)
365
+ ? docMatches(surface.project, [/\brate limit\b/i, /\bthrottl/i]) ||
366
+ hasAnyDependency(ctx, [/\bexpress-rate-limit\b/i, /\bbottleneck\b/i, /\bslowapi\b/i])
367
+ : null,
368
+ },
369
+ {
370
+ key: 'apiDesignRequestValidation',
371
+ suffix: '17',
372
+ name: 'API design: request validation present',
373
+ category: 'api-design',
374
+ impact: 'high',
375
+ fix: 'Use and document request validation (Zod, Joi, class-validator, Pydantic, or equivalent) so invalid inputs fail early and consistently.',
376
+ check: (ctx, surface) => hasApiSurface(surface)
377
+ ? docMatches(surface.project, [/\brequest validation\b/i, /\binput validation\b/i, /\bzod\b/i, /\bjoi\b/i, /\bpydantic\b/i]) ||
378
+ hasAnyDependency(ctx, [/\bzod\b/i, /\bjoi\b/i, /\bclass-validator\b/i, /\bpydantic\b/i])
379
+ : null,
380
+ },
381
+ {
382
+ key: 'apiDesignResponseFormatConsistent',
383
+ suffix: '18',
384
+ name: 'API design: response format consistency described',
385
+ category: 'api-design',
386
+ impact: 'medium',
387
+ fix: 'Describe the standard API response shape or schema conventions so consumers know what to expect from every endpoint.',
388
+ check: (_ctx, surface) => hasApiSurface(surface)
389
+ ? docMatches(surface.project, [/\bresponse format\b/i, /\bresponse schema\b/i, /\bjson envelope\b/i, /\bconsistent response\b/i])
390
+ : null,
391
+ },
392
+ {
393
+ key: 'databaseMigrationStrategyDocumented',
394
+ suffix: '19',
395
+ name: 'Database: migration strategy documented',
396
+ category: 'database',
397
+ impact: 'high',
398
+ fix: 'Document the migration workflow (`prisma migrate`, `alembic`, SQL migrations, or equivalent) so schema changes are repeatable and reviewable.',
399
+ check: (ctx, surface) => hasDatabaseSurface(surface)
400
+ ? docMatches(surface.project, [/\bmigration\b/i, /\bprisma migrate\b/i, /\balembic\b/i, /\bdbmate\b/i]) ||
401
+ hasDir(ctx, 'prisma/migrations') ||
402
+ hasDir(ctx, 'migrations')
403
+ : null,
404
+ },
405
+ {
406
+ key: 'databaseQueryOptimizationMentioned',
407
+ suffix: '20',
408
+ name: 'Database: query optimization mentioned',
409
+ category: 'database',
410
+ impact: 'medium',
411
+ fix: 'Mention query optimization concerns such as indexes, N+1 prevention, pagination, or query plans so performance work has a shared baseline.',
412
+ check: (_ctx, surface) => hasDatabaseSurface(surface)
413
+ ? docMatches(surface.project, [/\bquery optimization\b/i, /\bindex(es)?\b/i, /\bn\+1\b/i, /\bpagination\b/i, /\bquery plan\b/i])
414
+ : null,
415
+ },
416
+ {
417
+ key: 'databaseConnectionPooling',
418
+ suffix: '21',
419
+ name: 'Database: connection pooling addressed',
420
+ category: 'database',
421
+ impact: 'medium',
422
+ fix: 'Document or configure connection pooling so the database layer does not rely on unbounded per-request connections.',
423
+ check: (ctx, surface) => hasDatabaseSurface(surface)
424
+ ? docMatches(surface.project, [/\bconnection pool\b/i, /\bpooling\b/i]) ||
425
+ hasAnyDependency(ctx, [/\bpg\b/i, /\bmysql2\b/i, /\bprisma\b/i])
426
+ : null,
427
+ },
428
+ {
429
+ key: 'databaseBackupStrategy',
430
+ suffix: '22',
431
+ name: 'Database: backup strategy referenced',
432
+ category: 'database',
433
+ impact: 'high',
434
+ fix: 'Reference backup and restore expectations so data durability is not left as tribal knowledge.',
435
+ check: (_ctx, surface) => hasDatabaseSurface(surface)
436
+ ? docMatches(surface.project, [/\bbackup\b/i, /\brestore\b/i, /\bpoint-in-time\b/i, /\bsnapshot\b/i])
437
+ : null,
438
+ },
439
+ {
440
+ key: 'databaseSchemaDocumentation',
441
+ suffix: '23',
442
+ name: 'Database: schema documentation present',
443
+ category: 'database',
444
+ impact: 'medium',
445
+ fix: 'Include schema documentation or schema files so contributors can understand the data model without guessing.',
446
+ check: (ctx, surface) => hasDatabaseSurface(surface)
447
+ ? docMatches(surface.project, [/\bschema\b/i, /\berd\b/i, /\bdbml\b/i]) ||
448
+ hasFile(ctx, 'schema.prisma') ||
449
+ hasFile(ctx, 'dbml')
450
+ : null,
451
+ },
452
+ {
453
+ key: 'databaseSeedDataMentioned',
454
+ suffix: '24',
455
+ name: 'Database: seed data mentioned',
456
+ category: 'database',
457
+ impact: 'low',
458
+ fix: 'Document seed data or bootstrap fixtures so contributors can stand up realistic local environments quickly.',
459
+ check: (ctx, surface) => hasDatabaseSurface(surface)
460
+ ? docMatches(surface.project, [/\bseed data\b/i, /\bseeding\b/i, /\bbootstrap data\b/i]) ||
461
+ scriptMatches(ctx, [/\bseed\b/i])
462
+ : null,
463
+ },
464
+ {
465
+ key: 'authenticationAuthFlowDocumented',
466
+ suffix: '25',
467
+ name: 'Authentication: auth flow documented',
468
+ category: 'authentication',
469
+ impact: 'high',
470
+ fix: 'Document the authentication flow so login, signup, and trust boundaries are clear for contributors and agents.',
471
+ check: (_ctx, surface) => hasAuthSurface(surface)
472
+ ? docMatches(surface.project, [/\bauth flow\b/i, /\blogin\b/i, /\bsign[- ]?in\b/i, /\bsign[- ]?up\b/i])
473
+ : null,
474
+ },
475
+ {
476
+ key: 'authenticationTokenHandlingGuidance',
477
+ suffix: '26',
478
+ name: 'Authentication: token handling guidance',
479
+ category: 'authentication',
480
+ impact: 'high',
481
+ fix: 'Describe how tokens are stored, refreshed, and protected so secrets do not leak into client storage or logs.',
482
+ check: (_ctx, surface) => hasAuthSurface(surface)
483
+ ? docMatches(surface.project, [/\btoken\b/i, /\brefresh token\b/i, /\bhttpOnly\b/i, /\bbearer\b/i])
484
+ : null,
485
+ },
486
+ {
487
+ key: 'authenticationSessionManagement',
488
+ suffix: '27',
489
+ name: 'Authentication: session management described',
490
+ category: 'authentication',
491
+ impact: 'medium',
492
+ fix: 'Document session lifetime, revocation, and invalidation rules so auth behavior is predictable under change.',
493
+ check: (_ctx, surface) => hasAuthSurface(surface)
494
+ ? docMatches(surface.project, [/\bsession\b/i, /\bcookie\b/i, /\bexpiration\b/i, /\brevocation\b/i])
495
+ : null,
496
+ },
497
+ {
498
+ key: 'authenticationRbacPermissionsReferenced',
499
+ suffix: '28',
500
+ name: 'Authentication: RBAC or permissions referenced',
501
+ category: 'authentication',
502
+ impact: 'high',
503
+ fix: 'Reference roles and permission boundaries so the repo has a shared model for authorization checks.',
504
+ check: (_ctx, surface) => hasAuthSurface(surface)
505
+ ? docMatches(surface.project, [/\brbac\b/i, /\brole\b/i, /\bpermission\b/i, /\bauthorization\b/i])
506
+ : null,
507
+ },
508
+ {
509
+ key: 'authenticationOauthSsoMentioned',
510
+ suffix: '29',
511
+ name: 'Authentication: OAuth or SSO mentioned',
512
+ category: 'authentication',
513
+ impact: 'medium',
514
+ fix: 'Document OAuth, OIDC, SSO, or SAML expectations if the project uses delegated authentication or enterprise identity.',
515
+ check: (_ctx, surface) => hasAuthSurface(surface)
516
+ ? docMatches(surface.project, [/\boauth\b/i, /\boidc\b/i, /\bsso\b/i, /\bsaml\b/i])
517
+ : null,
518
+ },
519
+ {
520
+ key: 'authenticationCredentialRotation',
521
+ suffix: '30',
522
+ name: 'Authentication: credential rotation mentioned',
523
+ category: 'authentication',
524
+ impact: 'medium',
525
+ fix: 'Mention credential rotation or secret rollover procedures so auth incidents do not require ad-hoc recovery.',
526
+ check: (_ctx, surface) => hasAuthSurface(surface)
527
+ ? docMatches(surface.project, [/\brotate\b/i, /\brotation\b/i, /\bsecret rollover\b/i, /\bkey rollover\b/i])
528
+ : null,
529
+ },
530
+ {
531
+ key: 'monitoringLoggingConfigured',
532
+ suffix: '31',
533
+ name: 'Monitoring: logging configured',
534
+ category: 'monitoring',
535
+ impact: 'medium',
536
+ fix: 'Configure structured logging or document the logging approach so production diagnostics do not depend on ad-hoc console output.',
537
+ check: (ctx, surface) => hasRelevantProject(surface)
538
+ ? hasAnyDependency(ctx, [/\bpino\b/i, /\bwinston\b/i, /\bstructured-log\b/i, /\bstructlog\b/i, /\bloguru\b/i]) ||
539
+ docMatches(surface.project, [/\blogging\b/i, /\bstructured logs?\b/i, /\blogger\b/i])
540
+ : null,
541
+ },
542
+ {
543
+ key: 'monitoringErrorTrackingSetup',
544
+ suffix: '32',
545
+ name: 'Monitoring: error tracking setup',
546
+ category: 'monitoring',
547
+ impact: 'medium',
548
+ fix: 'Wire error tracking such as Sentry, Bugsnag, or Rollbar, or document the equivalent crash-reporting path.',
549
+ check: (ctx, surface) => hasRelevantProject(surface)
550
+ ? hasAnyDependency(ctx, [/\bsentry\b/i, /\bbugsnag\b/i, /\brollbar\b/i]) ||
551
+ docMatches(surface.project, [/\bsentry\b/i, /\bbugsnag\b/i, /\brollbar\b/i, /\berror tracking\b/i])
552
+ : null,
553
+ },
554
+ {
555
+ key: 'monitoringApmMetricsMentioned',
556
+ suffix: '33',
557
+ name: 'Monitoring: APM or metrics mentioned',
558
+ category: 'monitoring',
559
+ impact: 'medium',
560
+ fix: 'Mention metrics, tracing, or APM so performance and reliability signals are available before incidents escalate.',
561
+ check: (ctx, surface) => hasRelevantProject(surface)
562
+ ? hasAnyDependency(ctx, [/\bprom-client\b/i, /\bopentelemetry\b/i, /\bdatadog\b/i, /\bnewrelic\b/i]) ||
563
+ docMatches(surface.project, [/\bmetrics\b/i, /\bprometheus\b/i, /\bapm\b/i, /\bopentelemetry\b/i, /\btrace\b/i])
564
+ : null,
565
+ },
566
+ {
567
+ key: 'monitoringHealthCheckEndpoint',
568
+ suffix: '34',
569
+ name: 'Monitoring: health check endpoint referenced',
570
+ category: 'monitoring',
571
+ impact: 'medium',
572
+ fix: 'Document or implement a health check endpoint (`/health`, `/ready`, `/live`) so uptime checks have a stable target.',
573
+ check: (_ctx, surface) => hasRelevantProject(surface)
574
+ ? docMatches(surface.project, [/\bhealth check\b/i, /\bhealthz\b/i, /\bliveness\b/i, /\breadiness\b/i, /\/health\b/i])
575
+ : null,
576
+ },
577
+ {
578
+ key: 'monitoringAlertingReferenced',
579
+ suffix: '35',
580
+ name: 'Monitoring: alerting referenced',
581
+ category: 'monitoring',
582
+ impact: 'medium',
583
+ fix: 'Reference alerting or on-call expectations so incidents have an explicit escalation path.',
584
+ check: (_ctx, surface) => hasRelevantProject(surface)
585
+ ? docMatches(surface.project, [/\balerting\b/i, /\bon-call\b/i, /\bpagerduty\b/i, /\balertmanager\b/i, /\bincident\b/i])
586
+ : null,
587
+ },
588
+ {
589
+ key: 'monitoringLogRotationMentioned',
590
+ suffix: '36',
591
+ name: 'Monitoring: log rotation mentioned',
592
+ category: 'monitoring',
593
+ impact: 'low',
594
+ fix: 'Mention log retention or rotation so long-running services do not silently fill disks with unbounded logs.',
595
+ check: (_ctx, surface) => hasRelevantProject(surface)
596
+ ? docMatches(surface.project, [/\blog rotation\b/i, /\blogrotate\b/i, /\bretention\b/i, /\bmax size\b/i])
597
+ : null,
598
+ },
599
+ {
600
+ key: 'dependencyManagementLockfilePresent',
601
+ suffix: '37',
602
+ name: 'Dependency management: lockfile present',
603
+ category: 'dependency-management',
604
+ impact: 'high',
605
+ fix: 'Commit a lockfile so installs stay reproducible across contributors, CI, and agent-driven edits.',
606
+ check: (ctx, surface) => hasRelevantProject(surface) ? dependencyLockfilePresent(ctx) : null,
607
+ },
608
+ {
609
+ key: 'dependencyManagementOutdatedDepsAwareness',
610
+ suffix: '38',
611
+ name: 'Dependency management: outdated dependency awareness',
612
+ category: 'dependency-management',
613
+ impact: 'medium',
614
+ fix: 'Document or script dependency update checks (`npm outdated`, Renovate, Dependabot, or equivalent) so stale packages are noticed before they become risk.',
615
+ check: (ctx, surface) => hasRelevantProject(surface)
616
+ ? scriptMatches(ctx, [/\boutdated\b/i, /\bdepcheck\b/i]) ||
617
+ docMatches(surface.project, [/\bdependabot\b/i, /\brenovate\b/i, /\boutdated\b/i, /\bdependency update\b/i]) ||
618
+ hasFile(ctx, '.github/dependabot.yml') ||
619
+ hasFile(ctx, 'renovate.json')
620
+ : null,
621
+ },
622
+ {
623
+ key: 'dependencyManagementLicenseCompliance',
624
+ suffix: '39',
625
+ name: 'Dependency management: license compliance referenced',
626
+ category: 'dependency-management',
627
+ impact: 'medium',
628
+ fix: 'Reference license compliance or OSS review so dependency adoption has an explicit governance path.',
629
+ check: (ctx, surface) => hasRelevantProject(surface)
630
+ ? hasFile(ctx, 'LICENSE') || hasFile(ctx, 'LICENSE.md') || docMatches(surface.project, [/\blicense\b/i, /\bcompliance\b/i, /\boss review\b/i])
631
+ : null,
632
+ },
633
+ {
634
+ key: 'dependencyManagementNpmAuditConfigured',
635
+ suffix: '40',
636
+ name: 'Dependency management: audit command configured',
637
+ category: 'dependency-management',
638
+ impact: 'medium',
639
+ fix: 'Run `npm audit`, `pnpm audit`, `yarn audit`, `pip-audit`, or an equivalent scanner in scripts or CI so supply-chain issues are visible.',
640
+ check: (ctx, surface) => hasRelevantProject(surface)
641
+ ? scriptMatches(ctx, [/\baudit\b/i]) ||
642
+ /\baudit\b/i.test(surface.workflows) ||
643
+ docMatches(surface.project, [/\bnpm audit\b/i, /\bpnpm audit\b/i, /\byarn audit\b/i, /\bpip-audit\b/i, /\bsnyk\b/i])
644
+ : null,
645
+ },
646
+ {
647
+ key: 'dependencyManagementPinnedVersions',
648
+ suffix: '41',
649
+ name: 'Dependency management: versions pinned deliberately',
650
+ category: 'dependency-management',
651
+ impact: 'medium',
652
+ fix: 'Prefer exact version pins or document why floating ranges are acceptable so dependency drift stays intentional.',
653
+ check: (ctx, surface) => hasRelevantProject(surface) ? exactPinnedDependencies(ctx) : null,
654
+ },
655
+ {
656
+ key: 'dependencyManagementAutoUpdatePolicy',
657
+ suffix: '42',
658
+ name: 'Dependency management: auto-update policy present',
659
+ category: 'dependency-management',
660
+ impact: 'low',
661
+ fix: 'Add Dependabot, Renovate, or a documented update policy so dependency maintenance does not depend on memory alone.',
662
+ check: (ctx, surface) => hasRelevantProject(surface)
663
+ ? hasFile(ctx, '.github/dependabot.yml') ||
664
+ hasFile(ctx, 'renovate.json') ||
665
+ hasFile(ctx, '.github/renovate.json') ||
666
+ docMatches(surface.project, [/\bdependabot\b/i, /\brenovate\b/i, /\bupdate policy\b/i, /\bdependency policy\b/i])
667
+ : null,
668
+ },
669
+ {
670
+ key: 'costOptimizationTokenUsageAwareness',
671
+ suffix: '43',
672
+ name: 'Cost optimization: token usage awareness',
673
+ category: 'cost-optimization',
674
+ impact: 'medium',
675
+ fix: 'Mention token or context-window costs so AI-heavy workflows do not scale blindly.',
676
+ check: (_ctx, surface) => surface.docs
677
+ ? docMatches(surface.docs, [/\btoken\b/i, /\bcontext window\b/i, /\bcost\b/i, /\bprompt budget\b/i])
678
+ : null,
679
+ },
680
+ {
681
+ key: 'costOptimizationModelSelectionGuidance',
682
+ suffix: '44',
683
+ name: 'Cost optimization: model selection guidance',
684
+ category: 'cost-optimization',
685
+ impact: 'medium',
686
+ fix: 'Document when to use smaller, faster, or cheaper models so the repo does not default every task to the most expensive option.',
687
+ check: (_ctx, surface) => surface.docs
688
+ ? docMatches(surface.docs, [/\bmodel\b/i, /\bmini\b/i, /\bflash\b/i, /\bcheap(er)?\b/i, /\bfast(er)?\b/i])
689
+ : null,
690
+ },
691
+ {
692
+ key: 'costOptimizationCachingGuidance',
693
+ suffix: '45',
694
+ name: 'Cost optimization: caching to reduce API calls mentioned',
695
+ category: 'cost-optimization',
696
+ impact: 'medium',
697
+ fix: 'Mention caching, memoization, or response reuse so repeated requests do not waste tokens or API budget.',
698
+ check: (_ctx, surface) => surface.docs
699
+ ? docMatches(surface.docs, [/\bcach(e|ing)\b/i, /\bmemoiz/i, /\breuse\b/i])
700
+ : null,
701
+ },
702
+ {
703
+ key: 'costOptimizationBatchProcessing',
704
+ suffix: '46',
705
+ name: 'Cost optimization: batch processing mentioned',
706
+ category: 'cost-optimization',
707
+ impact: 'low',
708
+ fix: 'Mention batching or bulk operations where appropriate so repetitive calls can be collapsed into fewer requests.',
709
+ check: (_ctx, surface) => surface.docs
710
+ ? docMatches(surface.docs, [/\bbatch\b/i, /\bbulk\b/i, /\bqueue\b/i, /\bcoalesce\b/i])
711
+ : null,
712
+ },
713
+ {
714
+ key: 'costOptimizationBudgetGuardrails',
715
+ suffix: '47',
716
+ name: 'Cost optimization: budget guardrails mentioned',
717
+ category: 'cost-optimization',
718
+ impact: 'low',
719
+ fix: 'Document spend or usage guardrails so automation has a visible budget boundary.',
720
+ check: (_ctx, surface) => surface.docs
721
+ ? docMatches(surface.docs, [/\bbudget\b/i, /\bquota\b/i, /\bcap\b/i, /\bcost limit\b/i])
722
+ : null,
723
+ },
724
+ {
725
+ key: 'costOptimizationContextPruning',
726
+ suffix: '48',
727
+ name: 'Cost optimization: context pruning guidance',
728
+ category: 'cost-optimization',
729
+ impact: 'low',
730
+ fix: 'Mention context pruning, chunking, or summarization so long prompts do not burn unnecessary tokens.',
731
+ check: (_ctx, surface) => surface.docs
732
+ ? docMatches(surface.docs, [/\bprun(e|ing)\b/i, /\btruncate\b/i, /\bchunk(ing)?\b/i, /\bsummariz/i])
733
+ : null,
734
+ },
735
+ ];
736
+
737
+ function buildSupplementalChecks(options) {
738
+ const {
739
+ idPrefix,
740
+ urlMap,
741
+ docs,
742
+ workflowPaths,
743
+ } = options;
744
+
745
+ const checks = {};
746
+
747
+ for (const def of CHECK_DEFS) {
748
+ checks[def.key] = {
749
+ id: `${idPrefix}${def.suffix}`,
750
+ name: def.name,
751
+ check: (ctx) => def.check(ctx, getProjectSurface(ctx, { docs, workflowPaths })),
752
+ impact: def.impact,
753
+ rating: 3,
754
+ category: def.category,
755
+ fix: def.fix,
756
+ sourceUrl: urlMap[def.category],
757
+ confidence: 0.7,
758
+ template: null,
759
+ };
760
+ }
761
+
762
+ return checks;
763
+ }
764
+
765
+ module.exports = {
766
+ buildSupplementalChecks,
767
+ };