@nerviq/cli 1.20.1 → 1.21.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 (181) hide show
  1. package/LICENSE +23 -23
  2. package/README.md +2 -2
  3. package/package.json +1 -1
  4. package/src/activity.js +1039 -1039
  5. package/src/adoption-advisor.js +299 -299
  6. package/src/aider/config-parser.js +166 -166
  7. package/src/aider/context.js +4 -1
  8. package/src/aider/deep-review.js +316 -316
  9. package/src/aider/domain-packs.js +303 -303
  10. package/src/aider/freshness.js +93 -93
  11. package/src/aider/governance.js +253 -253
  12. package/src/aider/interactive.js +334 -334
  13. package/src/aider/mcp-packs.js +329 -329
  14. package/src/aider/patch.js +214 -214
  15. package/src/aider/plans.js +186 -186
  16. package/src/aider/premium.js +360 -360
  17. package/src/aider/setup.js +404 -404
  18. package/src/aider/techniques.js +312 -67
  19. package/src/analyze.js +951 -951
  20. package/src/anti-patterns.js +485 -485
  21. package/src/audit/instruction-files.js +180 -180
  22. package/src/audit/recommendations.js +577 -577
  23. package/src/auto-suggest.js +154 -154
  24. package/src/badge.js +13 -13
  25. package/src/behavioral-drift.js +801 -801
  26. package/src/benchmark.js +67 -67
  27. package/src/catalog.js +103 -103
  28. package/src/certification.js +128 -128
  29. package/src/codex/config-parser.js +183 -183
  30. package/src/codex/context.js +223 -223
  31. package/src/codex/deep-review.js +493 -493
  32. package/src/codex/domain-packs.js +394 -394
  33. package/src/codex/freshness.js +84 -84
  34. package/src/codex/governance.js +192 -192
  35. package/src/codex/interactive.js +618 -618
  36. package/src/codex/mcp-packs.js +914 -914
  37. package/src/codex/patch.js +209 -209
  38. package/src/codex/plans.js +251 -251
  39. package/src/codex/premium.js +614 -614
  40. package/src/codex/setup.js +591 -591
  41. package/src/continuous-ops.js +681 -681
  42. package/src/copilot/activity.js +309 -309
  43. package/src/copilot/deep-review.js +346 -346
  44. package/src/copilot/domain-packs.js +372 -372
  45. package/src/copilot/freshness.js +57 -57
  46. package/src/copilot/governance.js +222 -222
  47. package/src/copilot/interactive.js +406 -406
  48. package/src/copilot/mcp-packs.js +826 -826
  49. package/src/copilot/plans.js +253 -253
  50. package/src/copilot/premium.js +451 -451
  51. package/src/copilot/setup.js +488 -488
  52. package/src/cost-tracking.js +61 -61
  53. package/src/cursor/activity.js +301 -301
  54. package/src/cursor/config-parser.js +265 -265
  55. package/src/cursor/context.js +256 -256
  56. package/src/cursor/deep-review.js +334 -334
  57. package/src/cursor/domain-packs.js +368 -368
  58. package/src/cursor/freshness.js +65 -65
  59. package/src/cursor/governance.js +229 -229
  60. package/src/cursor/interactive.js +391 -391
  61. package/src/cursor/mcp-packs.js +828 -828
  62. package/src/cursor/plans.js +254 -254
  63. package/src/cursor/premium.js +469 -469
  64. package/src/cursor/setup.js +488 -488
  65. package/src/dashboard.js +493 -493
  66. package/src/deep-review.js +428 -428
  67. package/src/deprecation.js +98 -98
  68. package/src/diff-only.js +280 -280
  69. package/src/doctor.js +119 -119
  70. package/src/domain-pack-expansion.js +1033 -1033
  71. package/src/domain-packs.js +387 -387
  72. package/src/feedback.js +178 -178
  73. package/src/fix-engine.js +783 -783
  74. package/src/fix-prompts.js +122 -122
  75. package/src/formatters/sarif.js +115 -115
  76. package/src/freshness.js +74 -74
  77. package/src/gemini/config-parser.js +275 -275
  78. package/src/gemini/deep-review.js +559 -559
  79. package/src/gemini/domain-packs.js +393 -393
  80. package/src/gemini/freshness.js +66 -66
  81. package/src/gemini/governance.js +201 -201
  82. package/src/gemini/interactive.js +860 -860
  83. package/src/gemini/mcp-packs.js +915 -915
  84. package/src/gemini/plans.js +269 -269
  85. package/src/gemini/premium.js +760 -760
  86. package/src/gemini/setup.js +692 -692
  87. package/src/governance.js +72 -72
  88. package/src/harmony/add.js +68 -68
  89. package/src/harmony/advisor.js +333 -333
  90. package/src/harmony/canon.js +565 -565
  91. package/src/harmony/cli.js +591 -591
  92. package/src/harmony/drift.js +401 -401
  93. package/src/harmony/governance.js +313 -313
  94. package/src/harmony/memory.js +239 -239
  95. package/src/harmony/sync.js +475 -475
  96. package/src/harmony/watch.js +370 -370
  97. package/src/hook-validation.js +342 -342
  98. package/src/index.js +271 -271
  99. package/src/init.js +184 -184
  100. package/src/instruction-surfaces.js +185 -185
  101. package/src/integrations.js +144 -144
  102. package/src/interactive.js +118 -118
  103. package/src/locales/en.json +1 -1
  104. package/src/locales/es.json +1 -1
  105. package/src/mcp-packs.js +830 -830
  106. package/src/mcp-server.js +726 -726
  107. package/src/mcp-validation.js +337 -337
  108. package/src/nerviq-sync.json +7 -7
  109. package/src/opencode/config-parser.js +109 -109
  110. package/src/opencode/context.js +247 -247
  111. package/src/opencode/deep-review.js +313 -313
  112. package/src/opencode/domain-packs.js +262 -262
  113. package/src/opencode/freshness.js +66 -66
  114. package/src/opencode/governance.js +159 -159
  115. package/src/opencode/interactive.js +392 -392
  116. package/src/opencode/mcp-packs.js +705 -705
  117. package/src/opencode/patch.js +184 -184
  118. package/src/opencode/plans.js +231 -231
  119. package/src/opencode/premium.js +413 -413
  120. package/src/opencode/setup.js +449 -449
  121. package/src/opencode/techniques.js +27 -27
  122. package/src/operating-profile.js +574 -574
  123. package/src/org.js +152 -152
  124. package/src/permission-rules.js +218 -218
  125. package/src/plans.js +839 -839
  126. package/src/platform-change-manifest.js +86 -86
  127. package/src/plugins.js +110 -110
  128. package/src/policy-layers.js +210 -210
  129. package/src/profiles.js +124 -124
  130. package/src/prompt-injection.js +74 -74
  131. package/src/public-api.js +173 -173
  132. package/src/recommendation-rules.js +84 -84
  133. package/src/repo-archetype.js +386 -386
  134. package/src/secret-patterns.js +39 -39
  135. package/src/server.js +527 -527
  136. package/src/setup/analysis.js +607 -607
  137. package/src/setup/runtime.js +172 -172
  138. package/src/setup.js +677 -677
  139. package/src/shared/capabilities.js +194 -194
  140. package/src/source-urls.js +132 -132
  141. package/src/stack-checks.js +565 -565
  142. package/src/supplemental-checks.js +13 -13
  143. package/src/synergy/adaptive.js +261 -261
  144. package/src/synergy/compensation.js +137 -137
  145. package/src/synergy/evidence.js +193 -193
  146. package/src/synergy/learning.js +199 -199
  147. package/src/synergy/patterns.js +227 -227
  148. package/src/synergy/ranking.js +83 -83
  149. package/src/synergy/report.js +165 -165
  150. package/src/synergy/routing.js +146 -146
  151. package/src/techniques/api.js +407 -407
  152. package/src/techniques/automation.js +316 -316
  153. package/src/techniques/compliance.js +257 -257
  154. package/src/techniques/hygiene.js +294 -294
  155. package/src/techniques/instructions.js +243 -243
  156. package/src/techniques/observability.js +226 -226
  157. package/src/techniques/optimization.js +142 -142
  158. package/src/techniques/quality.js +318 -318
  159. package/src/techniques/security.js +237 -237
  160. package/src/techniques/shared.js +443 -443
  161. package/src/techniques/stacks.js +2294 -2294
  162. package/src/techniques/tools.js +106 -106
  163. package/src/techniques/workflow.js +413 -413
  164. package/src/techniques.js +81 -81
  165. package/src/terminology.js +73 -73
  166. package/src/token-estimate.js +35 -35
  167. package/src/usage-patterns.js +99 -99
  168. package/src/verification-metadata.js +145 -145
  169. package/src/watch.js +247 -247
  170. package/src/windsurf/activity.js +302 -302
  171. package/src/windsurf/config-parser.js +267 -267
  172. package/src/windsurf/deep-review.js +337 -337
  173. package/src/windsurf/domain-packs.js +370 -370
  174. package/src/windsurf/freshness.js +36 -36
  175. package/src/windsurf/governance.js +231 -231
  176. package/src/windsurf/interactive.js +388 -388
  177. package/src/windsurf/mcp-packs.js +792 -792
  178. package/src/windsurf/plans.js +247 -247
  179. package/src/windsurf/premium.js +468 -468
  180. package/src/windsurf/setup.js +471 -471
  181. package/src/workspace.js +375 -375
@@ -91,6 +91,98 @@ function findFillerLine(content) {
91
91
  return firstLineMatching(content, (line) => FILLER_PATTERNS.some((pattern) => pattern.test(line)));
92
92
  }
93
93
 
94
+ // PP-04: Helpers for N/A gating and broader instruction surfaces ---------------
95
+
96
+ function hasAiderConfig(ctx) {
97
+ // .yml is canonical, .yaml is also accepted by Aider itself
98
+ return Boolean(
99
+ (ctx.fileContent && (ctx.fileContent('.aider.conf.yml') || ctx.fileContent('.aider.conf.yaml')))
100
+ );
101
+ }
102
+
103
+ function hasAiderModelSettings(ctx) {
104
+ return Boolean(
105
+ ctx.fileContent && (ctx.fileContent('.aider.model.settings.yml') || ctx.fileContent('.aider.model.settings.yaml'))
106
+ );
107
+ }
108
+
109
+ function hasAiderignore(ctx) {
110
+ return Boolean(ctx.fileContent && ctx.fileContent('.aiderignore'));
111
+ }
112
+
113
+ function readmeContent(ctx) {
114
+ return (
115
+ (ctx.fileContent && (ctx.fileContent('README.md') || ctx.fileContent('readme.md') || ctx.fileContent('README.rst'))) || ''
116
+ );
117
+ }
118
+
119
+ function contributingContent(ctx) {
120
+ return (
121
+ (ctx.fileContent && (ctx.fileContent('CONTRIBUTING.md') || ctx.fileContent('.github/CONTRIBUTING.md'))) || ''
122
+ );
123
+ }
124
+
125
+ function aidermdContent(ctx) {
126
+ return (
127
+ (ctx.fileContent && (ctx.fileContent('AIDER.md') || ctx.fileContent('AGENTS.md') || ctx.fileContent('CLAUDE.md') || ctx.fileContent('.claude/CLAUDE.md'))) || ''
128
+ );
129
+ }
130
+
131
+ // PP-04: Effective Aider docs surface — Aider has no auto-discovered instructions
132
+ // surface like CLAUDE.md, but real Aider users document Aider workflow across
133
+ // README, CONTRIBUTING, CONVENTIONS, AGENTS.md, CLAUDE.md, .ai/instructions.md.
134
+ function docsBundle(ctx) {
135
+ const parts = [];
136
+ parts.push(readmeContent(ctx));
137
+ parts.push(contributingContent(ctx));
138
+ parts.push(conventionContent(ctx));
139
+ parts.push(aidermdContent(ctx));
140
+ if (ctx.fileContent) {
141
+ parts.push(ctx.fileContent('.ai/instructions.md') || '');
142
+ parts.push(ctx.fileContent('docs/AIDER.md') || '');
143
+ parts.push(ctx.fileContent('ARCHITECTURE.md') || '');
144
+ }
145
+ return parts.filter(Boolean).join('\n\n');
146
+ }
147
+
148
+ function hasAnyAiderSurface(ctx) {
149
+ if (hasAiderConfig(ctx)) return true;
150
+ if (hasAiderModelSettings(ctx)) return true;
151
+ if (hasAiderignore(ctx)) return true;
152
+ if (conventionFiles(ctx).length > 0) return true;
153
+ // README/CONTRIBUTING/AGENTS/CLAUDE explicitly mentioning aider counts
154
+ const docs = `${readmeContent(ctx)}\n${contributingContent(ctx)}\n${aidermdContent(ctx)}`;
155
+ return /\baider\b/i.test(docs);
156
+ }
157
+
158
+ function isPythonProject(ctx) {
159
+ if (!ctx.fileContent) return false;
160
+ return Boolean(
161
+ ctx.fileContent('requirements.txt') ||
162
+ ctx.fileContent('Pipfile') ||
163
+ ctx.fileContent('pyproject.toml') ||
164
+ ctx.fileContent('setup.py') ||
165
+ ctx.fileContent('setup.cfg')
166
+ );
167
+ }
168
+
169
+ function hasArchitectMode(ctx) {
170
+ const config = configContent(ctx);
171
+ if (!config) return false;
172
+ return /\barchitect\s*:\s*true\b/i.test(config) || /\barchitect-mode\s*:\s*true\b/i.test(config);
173
+ }
174
+
175
+ function hasEnvExample(ctx) {
176
+ if (!ctx.fileContent) return false;
177
+ return Boolean(
178
+ ctx.fileContent('.env.example') ||
179
+ ctx.fileContent('.env.sample') ||
180
+ ctx.fileContent('.env.template') ||
181
+ ctx.fileContent('.env.dist') ||
182
+ ctx.fileContent('env.example')
183
+ );
184
+ }
185
+
94
186
  function repoLooksRegulated(ctx) {
95
187
  const filenames = ctx.files.join('\n');
96
188
  const packageJson = ctx.fileContent('package.json') || '';
@@ -112,7 +204,14 @@ const AIDER_TECHNIQUES = {
112
204
  aiderConfYmlExists: {
113
205
  id: 'AD-A01',
114
206
  name: '.aider.conf.yml config file exists',
115
- check: (ctx) => Boolean(ctx.fileContent('.aider.conf.yml')),
207
+ // PP-04: Both .yml and .yaml are accepted by Aider. The config file is
208
+ // recommended but optional — many real Aider repos drive Aider entirely
209
+ // via CONVENTIONS.md + CLI flags. N/A when no Aider surface at all so
210
+ // arbitrary repos do not surface this as a top finding.
211
+ check: (ctx) => {
212
+ if (!hasAnyAiderSurface(ctx)) return null;
213
+ return hasAiderConfig(ctx);
214
+ },
116
215
  impact: 'critical',
117
216
  rating: 5,
118
217
  category: 'config',
@@ -177,10 +276,14 @@ const AIDER_TECHNIQUES = {
177
276
  aiderMapTokensConfigured: {
178
277
  id: 'AD-A05',
179
278
  name: 'Map tokens setting is configured',
279
+ // PP-04: Repo-map sizing is opt-in tuning — the default works for most
280
+ // repos. N/A when not explicitly set; only fail if explicitly set to a
281
+ // non-numeric or out-of-range value (caught by other checks).
180
282
  check: (ctx) => {
181
283
  const config = configContent(ctx);
182
284
  if (!config) return null;
183
- return /\bmap-tokens\s*:/i.test(config);
285
+ if (/\bmap-tokens\s*:/i.test(config)) return true;
286
+ return null;
184
287
  },
185
288
  impact: 'medium',
186
289
  rating: 3,
@@ -228,10 +331,14 @@ const AIDER_TECHNIQUES = {
228
331
  aiderEditFormatConfigured: {
229
332
  id: 'AD-A08',
230
333
  name: 'Edit format explicitly set',
334
+ // PP-04: Aider auto-selects edit-format per model; explicit override is
335
+ // only needed for unusual setups. Pass when set, N/A when relying on the
336
+ // sensible default.
231
337
  check: (ctx) => {
232
338
  const config = configContent(ctx);
233
339
  if (!config) return null;
234
- return /\bedit-format\s*:/i.test(config);
340
+ if (/\bedit-format\s*:/i.test(config)) return true;
341
+ return null;
235
342
  },
236
343
  impact: 'medium',
237
344
  rating: 3,
@@ -350,10 +457,14 @@ const AIDER_TECHNIQUES = {
350
457
  aiderCommitPrefixConfigured: {
351
458
  id: 'AD-B07',
352
459
  name: 'Commit prefix set for AI-authored commits',
460
+ // PP-04: Optional traceability nicety. Pass when set, N/A when not —
461
+ // attribute-author/committer (AD-B06) covers the same intent at higher
462
+ // confidence.
353
463
  check: (ctx) => {
354
464
  const config = configContent(ctx);
355
465
  if (!config) return null;
356
- return /\baider-commit-prefix\s*:/i.test(config) || /\bcommit-prefix\s*:/i.test(config);
466
+ if (/\baider-commit-prefix\s*:/i.test(config) || /\bcommit-prefix\s*:/i.test(config)) return true;
467
+ return null;
357
468
  },
358
469
  impact: 'low',
359
470
  rating: 2,
@@ -368,9 +479,14 @@ const AIDER_TECHNIQUES = {
368
479
  id: 'AD-B08',
369
480
  name: '/undo command awareness documented',
370
481
  check: (ctx) => {
371
- const conventions = conventionContent(ctx);
482
+ // PP-04: Awareness check. N/A unless the repo has an .aider.conf.yml —
483
+ // /undo is an Aider-specific concept and only meaningful for users who
484
+ // have actually configured Aider.
485
+ if (!hasAiderConfig(ctx)) return null;
486
+ const docs = docsBundle(ctx);
372
487
  const config = configContent(ctx);
373
- return /\bundo\b/i.test(conventions) || /\bundo\b/i.test(config);
488
+ if (!docs && !config) return null;
489
+ return /\bundo\b/i.test(docs) || /\bundo\b/i.test(config);
374
490
  },
375
491
  impact: 'low',
376
492
  rating: 2,
@@ -389,6 +505,9 @@ const AIDER_TECHNIQUES = {
389
505
  id: 'AD-C01',
390
506
  name: 'Editor model explicitly configured',
391
507
  check: (ctx) => {
508
+ // PP-04: editor-model is an architect-mode optimisation. N/A when not opted in.
509
+ if (!hasAiderConfig(ctx)) return null;
510
+ if (!hasArchitectMode(ctx)) return null;
392
511
  const roles = modelRoles(ctx);
393
512
  return roles.editor !== null;
394
513
  },
@@ -405,6 +524,8 @@ const AIDER_TECHNIQUES = {
405
524
  id: 'AD-C02',
406
525
  name: 'Weak model configured for commit messages',
407
526
  check: (ctx) => {
527
+ // PP-04: weak-model is a cost optimisation. N/A when no .aider.conf.yml.
528
+ if (!hasAiderConfig(ctx)) return null;
408
529
  const roles = modelRoles(ctx);
409
530
  return roles.weak !== null;
410
531
  },
@@ -420,10 +541,13 @@ const AIDER_TECHNIQUES = {
420
541
  aiderArchitectModeAvailable: {
421
542
  id: 'AD-C03',
422
543
  name: 'Architect mode configured (2-model workflow)',
544
+ // PP-04: Architect mode is opt-in (~1.73x cost). Pass when on, N/A when
545
+ // not set — most teams correctly stick with the cheaper standard mode.
423
546
  check: (ctx) => {
424
547
  const config = configContent(ctx);
425
548
  if (!config) return null;
426
- return /\barchitect\s*:\s*true\b/i.test(config);
549
+ if (/\barchitect\s*:\s*true\b/i.test(config)) return true;
550
+ return null;
427
551
  },
428
552
  impact: 'high',
429
553
  rating: 4,
@@ -437,7 +561,12 @@ const AIDER_TECHNIQUES = {
437
561
  aiderModelSettingsFileExists: {
438
562
  id: 'AD-C04',
439
563
  name: '.aider.model.settings.yml exists for model customization',
440
- check: (ctx) => Boolean(ctx.fileContent('.aider.model.settings.yml')),
564
+ // PP-04: model settings file is opt-in advanced customization. N/A when no
565
+ // .aider.conf.yml — there's no signal the team is using Aider intentionally.
566
+ check: (ctx) => {
567
+ if (!hasAiderConfig(ctx)) return null;
568
+ return hasAiderModelSettings(ctx);
569
+ },
441
570
  impact: 'medium',
442
571
  rating: 3,
443
572
  category: 'model-config',
@@ -468,10 +597,13 @@ const AIDER_TECHNIQUES = {
468
597
  aiderCachePromptsEnabled: {
469
598
  id: 'AD-C06',
470
599
  name: 'Prompt caching enabled for cost savings',
600
+ // PP-04: Cost optimisation. Pass when explicitly on, N/A when not — only
601
+ // some providers/models support prompt caching, so absence is not a defect.
471
602
  check: (ctx) => {
472
603
  const config = configContent(ctx);
473
604
  if (!config) return null;
474
- return /\bcache-prompts\s*:\s*true\b/i.test(config);
605
+ if (/\bcache-prompts\s*:\s*true\b/i.test(config)) return true;
606
+ return null;
475
607
  },
476
608
  impact: 'medium',
477
609
  rating: 3,
@@ -526,7 +658,16 @@ const AIDER_TECHNIQUES = {
526
658
  aiderConventionFileExists: {
527
659
  id: 'AD-D01',
528
660
  name: 'Convention file exists for Aider context',
529
- check: (ctx) => conventionFiles(ctx).length > 0,
661
+ check: (ctx) => {
662
+ // PP-04: AGENTS.md / CLAUDE.md / .ai/instructions.md / AIDER.md count as
663
+ // effective convention surfaces in real Aider repos — Aider users
664
+ // routinely use these files as their context bundle even though Aider
665
+ // itself does not auto-discover them. N/A when no Aider surface at all.
666
+ if (!hasAnyAiderSurface(ctx)) return null;
667
+ if (conventionFiles(ctx).length > 0) return true;
668
+ if (ctx.fileContent && (ctx.fileContent('AGENTS.md') || ctx.fileContent('CLAUDE.md') || ctx.fileContent('.claude/CLAUDE.md') || ctx.fileContent('AIDER.md') || ctx.fileContent('.ai/instructions.md'))) return true;
669
+ return false;
670
+ },
530
671
  impact: 'high',
531
672
  rating: 4,
532
673
  category: 'conventions',
@@ -556,11 +697,15 @@ const AIDER_TECHNIQUES = {
556
697
  aiderConventionHasArchitecture: {
557
698
  id: 'AD-D03',
558
699
  name: 'Convention file includes architecture/structure section',
700
+ // PP-04: Architecture content commonly lives in ARCHITECTURE.md, README,
701
+ // AGENTS.md, or CLAUDE.md — not just CONVENTIONS.md. Widen source.
559
702
  check: (ctx) => {
560
- const content = conventionContent(ctx);
703
+ if (!hasAnyAiderSurface(ctx)) return null;
704
+ const content = docsBundle(ctx);
561
705
  if (!content) return null;
562
- return /##\s+(?:Architecture|Structure|Project Map|Directory)/i.test(content) ||
563
- /```mermaid/i.test(content);
706
+ return /##\s+(?:Architecture|Structure|Project Map|Project Snapshot|Project Layout|Directory|Layout|Modules|Module Tiers|Components|Stack|Tech Stack|Overview|Tour)/i.test(content) ||
707
+ /```mermaid/i.test(content) ||
708
+ /\bproject\s+(?:layout|structure|snapshot)\b/i.test(content);
564
709
  },
565
710
  impact: 'high',
566
711
  rating: 4,
@@ -574,10 +719,13 @@ const AIDER_TECHNIQUES = {
574
719
  aiderConventionHasVerification: {
575
720
  id: 'AD-D04',
576
721
  name: 'Convention file includes verification commands',
722
+ // PP-04: Test/lint commands frequently live in README, CONTRIBUTING.md,
723
+ // AGENTS.md, or CLAUDE.md, not just CONVENTIONS.md. Widen source.
577
724
  check: (ctx) => {
578
- const content = conventionContent(ctx);
725
+ if (!hasAnyAiderSurface(ctx)) return null;
726
+ const content = docsBundle(ctx);
579
727
  if (!content) return null;
580
- return /\bnpm test\b|\bpnpm test\b|\byarn test\b|\bpytest\b|\bgo test\b|\bcargo test\b|\bmake test\b/i.test(content);
728
+ return /\bnpm (?:run )?test\b|\bpnpm test\b|\byarn test\b|\bpytest\b|\bgo test\b|\bcargo test\b|\bmake test\b|\bmvn test\b|\bgradle (?:test|check)\b|\brake test\b|\bdotnet test\b|\bswift test\b|\btox\b/i.test(content);
581
729
  },
582
730
  impact: 'high',
583
731
  rating: 4,
@@ -666,7 +814,14 @@ const AIDER_TECHNIQUES = {
666
814
  aiderAiderignoreExists: {
667
815
  id: 'AD-E03',
668
816
  name: '.aiderignore file exists for file filtering',
669
- check: (ctx) => Boolean(ctx.fileContent('.aiderignore')),
817
+ // PP-04: .aiderignore is a fully optional advanced filter. N/A unless the
818
+ // repo has an .aider.conf.yml (the strongest "we use Aider intentionally"
819
+ // signal). Most real Aider repos rely on .gitignore + repo-map and do not
820
+ // ship an .aiderignore.
821
+ check: (ctx) => {
822
+ if (!hasAiderConfig(ctx)) return null;
823
+ return hasAiderignore(ctx);
824
+ },
670
825
  impact: 'medium',
671
826
  rating: 3,
672
827
  category: 'architecture',
@@ -700,10 +855,13 @@ const AIDER_TECHNIQUES = {
700
855
  aiderEnvInGitignore: {
701
856
  id: 'AD-F01',
702
857
  name: '.env file excluded from git',
858
+ // PP-04: Only meaningful when the team uses Aider (or any tool that loads
859
+ // .env). N/A when no Aider surface and no .env in the repo at all.
703
860
  check: (ctx) => {
704
861
  const gi = gitignoreContent(ctx);
862
+ if (!hasAnyAiderSurface(ctx) && !(ctx.fileContent && ctx.fileContent('.env'))) return null;
705
863
  if (!gi) return false;
706
- return /^\.env$/m.test(gi) || /^\.env\b/m.test(gi);
864
+ return /^\.env$/m.test(gi) || /^\.env\b/m.test(gi) || /^\*\.env$/m.test(gi);
707
865
  },
708
866
  impact: 'critical',
709
867
  rating: 5,
@@ -868,11 +1026,25 @@ const AIDER_TECHNIQUES = {
868
1026
  aiderGitHooksForPreCommit: {
869
1027
  id: 'AD-G04',
870
1028
  name: 'Git pre-commit hooks or CI gates for quality',
871
- check: (ctx) => {
872
- // Check for pre-commit config or husky
873
- return Boolean(ctx.fileContent('.pre-commit-config.yaml')) ||
1029
+ // PP-04: pre-commit hooks are opt-in. N/A when no Aider surface — and accept
1030
+ // CI-as-quality-gate (a workflow that runs lint/test on PR) as a valid
1031
+ // alternative to local pre-commit hooks.
1032
+ check: (ctx) => {
1033
+ if (!hasAnyAiderSurface(ctx)) return null;
1034
+ if (
1035
+ Boolean(ctx.fileContent('.pre-commit-config.yaml')) ||
1036
+ Boolean(ctx.fileContent('.pre-commit-config.yml')) ||
874
1037
  Boolean(ctx.fileContent('.husky/pre-commit')) ||
875
- Boolean(ctx.fileContent('.lefthook.yml'));
1038
+ Boolean(ctx.fileContent('.lefthook.yml')) ||
1039
+ Boolean(ctx.fileContent('lefthook.yml'))
1040
+ ) return true;
1041
+ // Accept CI-side quality gate as equivalent
1042
+ const workflows = ctx.workflowFiles ? ctx.workflowFiles() : [];
1043
+ for (const wf of workflows) {
1044
+ const content = ctx.fileContent(wf) || '';
1045
+ if (/\b(lint|test|check|format)\b/i.test(content) && /\bpull_request\b|\bon:\s*\[/i.test(content)) return true;
1046
+ }
1047
+ return false;
876
1048
  },
877
1049
  impact: 'high',
878
1050
  rating: 4,
@@ -939,10 +1111,14 @@ const AIDER_TECHNIQUES = {
939
1111
  aiderConventionHasCodingStandards: {
940
1112
  id: 'AD-H01',
941
1113
  name: 'Convention file has coding standards section',
1114
+ // PP-04: Widen source to docsBundle (AGENTS.md / CLAUDE.md / CONTRIBUTING
1115
+ // commonly host the coding-standards section).
942
1116
  check: (ctx) => {
943
- const content = conventionContent(ctx);
1117
+ if (!hasAnyAiderSurface(ctx)) return null;
1118
+ const content = docsBundle(ctx);
944
1119
  if (!content) return null;
945
- return /##\s+(?:Coding|Style|Standards|Formatting|Conventions)/i.test(content);
1120
+ return /##\s+(?:Coding|Style|Standards|Formatting|Conventions|Guidelines|Code\s+Style|Hard constraints|Constraints|Platform conventions|Rules|Quick commands)/i.test(content) ||
1121
+ /\b(?:swift|rust|python|java|kotlin|go|typescript)\s+(?:and|\&)?\s*(?:platform\s+)?conventions?\b/i.test(content);
946
1122
  },
947
1123
  impact: 'high',
948
1124
  rating: 4,
@@ -957,9 +1133,10 @@ const AIDER_TECHNIQUES = {
957
1133
  id: 'AD-H02',
958
1134
  name: 'Convention file covers error handling',
959
1135
  check: (ctx) => {
960
- const content = conventionContent(ctx);
1136
+ if (!hasAnyAiderSurface(ctx)) return null;
1137
+ const content = docsBundle(ctx);
961
1138
  if (!content) return null;
962
- return /\berror\s+handling\b|\bexception\b|\btry[- ]catch\b|\bResult\s*<\b/i.test(content);
1139
+ return /\berror\s+handling\b|\bexception\b|\btry[- ]catch\b|\bResult\s*<\b|\bpanic\b|\b\?\?\s+/i.test(content);
963
1140
  },
964
1141
  impact: 'medium',
965
1142
  rating: 3,
@@ -974,9 +1151,11 @@ const AIDER_TECHNIQUES = {
974
1151
  id: 'AD-H03',
975
1152
  name: 'Convention file covers testing guidelines',
976
1153
  check: (ctx) => {
977
- const content = conventionContent(ctx);
1154
+ if (!hasAnyAiderSurface(ctx)) return null;
1155
+ const content = docsBundle(ctx);
978
1156
  if (!content) return null;
979
- return /##\s+(?:Test|Testing)/i.test(content) || /\bunit test\b|\bintegration test\b|\btest coverage\b/i.test(content);
1157
+ return /##\s+(?:Test|Testing|Tests)/i.test(content) ||
1158
+ /\bunit test\b|\bintegration test\b|\btest coverage\b|\bend[- ]to[- ]end\b/i.test(content);
980
1159
  },
981
1160
  impact: 'high',
982
1161
  rating: 4,
@@ -1047,7 +1226,16 @@ const AIDER_TECHNIQUES = {
1047
1226
  aiderEnvFileExists: {
1048
1227
  id: 'AD-M01',
1049
1228
  name: '.env file exists with API configuration',
1050
- check: (ctx) => Boolean(ctx.fileContent('.env')),
1229
+ // PP-04: .env is conventionally gitignored — its absence in a public repo
1230
+ // is the secure default, not a finding. Accept .env.example/.sample/.template
1231
+ // as valid evidence the team documents their env-var contract. N/A unless
1232
+ // there is an actual .aider.conf.yml — without one we have no signal the
1233
+ // team is on Aider rather than just mentioning it in docs.
1234
+ check: (ctx) => {
1235
+ if (!hasAiderConfig(ctx)) return null;
1236
+ if (ctx.fileContent && ctx.fileContent('.env')) return true;
1237
+ return hasEnvExample(ctx);
1238
+ },
1051
1239
  impact: 'high',
1052
1240
  rating: 4,
1053
1241
  category: 'advanced-config',
@@ -1060,10 +1248,20 @@ const AIDER_TECHNIQUES = {
1060
1248
  aiderEnvHasApiKey: {
1061
1249
  id: 'AD-M02',
1062
1250
  name: '.env contains at least one API key',
1251
+ // PP-04: Already correctly N/A when no .env (committed). Also accept
1252
+ // .env.example/sample with placeholder keys as evidence the env contract
1253
+ // is documented (real .env is gitignored and won't be in the audit tree).
1063
1254
  check: (ctx) => {
1064
1255
  const env = envContent(ctx);
1065
- if (!env) return null;
1066
- return /\b(?:OPENAI_API_KEY|ANTHROPIC_API_KEY|OPENROUTER_API_KEY|DEEPSEEK_API_KEY)\s*=/i.test(env);
1256
+ if (env) {
1257
+ return /\b(?:OPENAI_API_KEY|ANTHROPIC_API_KEY|OPENROUTER_API_KEY|DEEPSEEK_API_KEY|GEMINI_API_KEY|GROQ_API_KEY)\s*=/i.test(env);
1258
+ }
1259
+ // Fall back to an example file if no committed .env
1260
+ const example = (ctx.fileContent && (
1261
+ ctx.fileContent('.env.example') || ctx.fileContent('.env.sample') || ctx.fileContent('.env.template')
1262
+ )) || '';
1263
+ if (!example) return null;
1264
+ return /\b(?:OPENAI_API_KEY|ANTHROPIC_API_KEY|OPENROUTER_API_KEY|DEEPSEEK_API_KEY|GEMINI_API_KEY|GROQ_API_KEY)\s*=/i.test(example);
1067
1265
  },
1068
1266
  impact: 'high',
1069
1267
  rating: 4,
@@ -1134,9 +1332,13 @@ const AIDER_TECHNIQUES = {
1134
1332
  aiderBrowserModeForDocs: {
1135
1333
  id: 'AD-N02',
1136
1334
  name: 'Browser integration known (/web command)',
1335
+ // PP-04: low-impact awareness. N/A unless an .aider.conf.yml exists —
1336
+ // /web is an Aider-specific in-chat command, not a generic concern.
1137
1337
  check: (ctx) => {
1138
- const content = conventionContent(ctx);
1139
- return /\b\/web\b|\bbrowser\b/i.test(content);
1338
+ if (!hasAiderConfig(ctx)) return null;
1339
+ const docs = docsBundle(ctx);
1340
+ if (!docs) return null;
1341
+ return /\b\/web\b|\bbrowser\s+(?:mode|docs)\b|\bbrowser\b.*\b\/web\b/i.test(docs);
1140
1342
  },
1141
1343
  impact: 'low',
1142
1344
  rating: 2,
@@ -1150,11 +1352,13 @@ const AIDER_TECHNIQUES = {
1150
1352
  aiderInChatCommandsDocumented: {
1151
1353
  id: 'AD-N03',
1152
1354
  name: 'Key in-chat commands documented in conventions',
1355
+ // PP-04: Aider-specific in-chat commands. N/A unless an .aider.conf.yml
1356
+ // exists. Widen content source to docsBundle.
1153
1357
  check: (ctx) => {
1154
- const content = conventionContent(ctx);
1358
+ if (!hasAiderConfig(ctx)) return null;
1359
+ const content = docsBundle(ctx);
1155
1360
  if (!content) return null;
1156
- // Check for documentation of key commands
1157
- const commands = ['/add', '/drop', '/run', '/test', '/undo'];
1361
+ const commands = ['/add', '/drop', '/run', '/test', '/undo', '/web', '/ask', '/code'];
1158
1362
  const found = commands.filter(cmd => content.includes(cmd));
1159
1363
  return found.length >= 2;
1160
1364
  },
@@ -1170,10 +1374,14 @@ const AIDER_TECHNIQUES = {
1170
1374
  aiderVoiceModeAware: {
1171
1375
  id: 'AD-N04',
1172
1376
  name: 'Voice mode configuration known',
1377
+ // PP-04: Voice coding is a niche developer preference, not a project
1378
+ // requirement. Pass when documented, N/A otherwise (no team should be
1379
+ // penalised for not using voice coding).
1173
1380
  check: (ctx) => {
1174
1381
  const config = configContent(ctx);
1175
1382
  if (!config) return null;
1176
- return /\bvoice-language\s*:/i.test(config) || /\bvoice\b/i.test(conventionContent(ctx));
1383
+ if (/\bvoice-language\s*:/i.test(config) || /\bvoice\b/i.test(docsBundle(ctx))) return true;
1384
+ return null;
1177
1385
  },
1178
1386
  impact: 'low',
1179
1387
  rating: 2,
@@ -1187,11 +1395,13 @@ const AIDER_TECHNIQUES = {
1187
1395
  aiderPlaywrightUrlScraping: {
1188
1396
  id: 'AD-N05',
1189
1397
  name: 'Playwright URL auto-scraping side effect is expected',
1398
+ // PP-04: Niche awareness advisory. N/A unless an .aider.conf.yml exists.
1190
1399
  check: (ctx) => {
1191
- const conventions = conventionContent(ctx);
1400
+ if (!hasAiderConfig(ctx)) return null;
1401
+ const docs = docsBundle(ctx);
1192
1402
  const config = configContent(ctx);
1193
- // Check if team is aware of the Playwright auto-scraping behavior
1194
- return /playwright|url.*scrap|scrape.*url|auto.*fetch|web.*fetch/i.test(conventions) ||
1403
+ if (!docs && !config) return null;
1404
+ return /playwright|url.*scrap|scrape.*url|auto.*fetch|web.*fetch/i.test(docs) ||
1195
1405
  /playwright|url.*scrap/i.test(config);
1196
1406
  },
1197
1407
  impact: 'medium',
@@ -1210,10 +1420,15 @@ const AIDER_TECHNIQUES = {
1210
1420
  aiderEditorIntegrationDocumented: {
1211
1421
  id: 'AD-O01',
1212
1422
  name: 'Editor integration documented (VS Code, NeoVim, etc.)',
1423
+ // PP-04: Editor integration is a developer-local concern, not a project
1424
+ // requirement — it tells team members which editor plugins exist for
1425
+ // Aider, but absence is not a real defect. N/A unless the repo has a
1426
+ // .aider.conf.yml AND the docs already discuss tooling/setup.
1213
1427
  check: (ctx) => {
1214
- const content = conventionContent(ctx);
1428
+ if (!hasAiderConfig(ctx)) return null;
1429
+ const content = docsBundle(ctx);
1215
1430
  if (!content) return null;
1216
- return /\bvs\s*code\b|\bneovim\b|\bvim\b|\beditor\b/i.test(content);
1431
+ return /\bvs\s*code\b|\bneovim\b|\bvim\b|\bemacs\b|\bjetbrains\b|\bintellij\b|\bsublime\b|\beditor[- ]integration\b/i.test(content);
1217
1432
  },
1218
1433
  impact: 'low',
1219
1434
  rating: 2,
@@ -1227,10 +1442,14 @@ const AIDER_TECHNIQUES = {
1227
1442
  aiderWatchModeKnown: {
1228
1443
  id: 'AD-O02',
1229
1444
  name: 'Watch mode (--watch-files) documented or configured',
1445
+ // PP-04: Niche feature awareness. N/A when no Aider config — without
1446
+ // .aider.conf.yml there's no place watch-files would meaningfully live.
1230
1447
  check: (ctx) => {
1448
+ if (!hasAiderConfig(ctx)) return null;
1231
1449
  const config = configContent(ctx);
1232
- if (!config) return null;
1233
- return /\bwatch-files\s*:/i.test(config) || /\bwatch\b/i.test(conventionContent(ctx));
1450
+ if (/\bwatch-files\s*:/i.test(config)) return true;
1451
+ const docs = docsBundle(ctx);
1452
+ return /\bwatch[- ]files\b|\b--watch\b/i.test(docs);
1234
1453
  },
1235
1454
  impact: 'medium',
1236
1455
  rating: 3,
@@ -1244,11 +1463,11 @@ const AIDER_TECHNIQUES = {
1244
1463
  aiderDarkModeConfigured: {
1245
1464
  id: 'AD-O03',
1246
1465
  name: 'Theme/dark mode configured for terminal',
1247
- check: (ctx) => {
1248
- const config = configContent(ctx);
1249
- if (!config) return null;
1250
- return /\bdark-mode\s*:/i.test(config) || /\blight-mode\s*:/i.test(config);
1251
- },
1466
+ // PP-04: Cosmetic preference; not meaningful as a project-level requirement.
1467
+ // Downgrade to N/A across the board (kept as a check so the catalog still
1468
+ // surfaces it, but it should not be a "fail" advisory on real repos —
1469
+ // theme is a developer-local preference, not a project artifact).
1470
+ check: () => null,
1252
1471
  impact: 'low',
1253
1472
  rating: 1,
1254
1473
  category: 'editor-integration',
@@ -1282,11 +1501,18 @@ const AIDER_TECHNIQUES = {
1282
1501
  aiderVersionPinned: {
1283
1502
  id: 'AD-P01',
1284
1503
  name: 'Aider version pinned in requirements or package manager',
1504
+ // PP-04: Aider is a Python package — pinning is only meaningful for Python
1505
+ // projects. Non-Python repos use Aider via a separate venv, not via their
1506
+ // own dependency manifest. N/A when the project isn't Python.
1285
1507
  check: (ctx) => {
1508
+ if (!isPythonProject(ctx)) return null;
1509
+ if (!hasAnyAiderSurface(ctx)) return null;
1286
1510
  const req = ctx.fileContent('requirements.txt') || '';
1287
1511
  const pipfile = ctx.fileContent('Pipfile') || '';
1288
1512
  const pyproject = ctx.fileContent('pyproject.toml') || '';
1289
- return /\baider-chat\b/i.test(req) || /\baider-chat\b/i.test(pipfile) || /\baider-chat\b/i.test(pyproject);
1513
+ const setupPy = ctx.fileContent('setup.py') || '';
1514
+ return /\baider-chat\b/i.test(req) || /\baider-chat\b/i.test(pipfile) ||
1515
+ /\baider-chat\b/i.test(pyproject) || /\baider-chat\b/i.test(setupPy);
1290
1516
  },
1291
1517
  impact: 'medium',
1292
1518
  rating: 3,
@@ -1300,11 +1526,16 @@ const AIDER_TECHNIQUES = {
1300
1526
  aiderAllConfigSurfacesPresent: {
1301
1527
  id: 'AD-P02',
1302
1528
  name: 'All essential Aider config surfaces present',
1529
+ // PP-04: .env is gitignored by convention; accept .env.example/sample as
1530
+ // evidence the env contract is documented. N/A unless an .aider.conf.yml
1531
+ // exists — otherwise this fails on every repo that mentions Aider in docs
1532
+ // but doesn't ship the full config triple.
1303
1533
  check: (ctx) => {
1304
- const hasConf = Boolean(ctx.fileContent('.aider.conf.yml'));
1305
- const hasEnv = Boolean(ctx.fileContent('.env'));
1306
- const hasGitignore = Boolean(ctx.fileContent('.gitignore'));
1307
- return hasConf && hasEnv && hasGitignore;
1534
+ if (!hasAiderConfig(ctx)) return null;
1535
+ const envFile = ctx.fileContent && ctx.fileContent('.env');
1536
+ const hasEnvSurface = Boolean(envFile) || hasEnvExample(ctx);
1537
+ const hasGitignore = Boolean(ctx.fileContent && ctx.fileContent('.gitignore'));
1538
+ return hasEnvSurface && hasGitignore;
1308
1539
  },
1309
1540
  impact: 'high',
1310
1541
  rating: 4,
@@ -1318,10 +1549,13 @@ const AIDER_TECHNIQUES = {
1318
1549
  aiderDocumentedWorkflow: {
1319
1550
  id: 'AD-P03',
1320
1551
  name: 'Aider workflow documented in README or conventions',
1552
+ // PP-04: Widen to the full docsBundle (README/CONTRIBUTING/CONVENTIONS/
1553
+ // AGENTS/CLAUDE/.ai/instructions). N/A when no Aider surface at all.
1321
1554
  check: (ctx) => {
1322
- const readme = ctx.fileContent('README.md') || '';
1323
- const content = conventionContent(ctx);
1324
- return /\baider\b/i.test(readme) || /\bworkflow\b/i.test(content);
1555
+ if (!hasAnyAiderSurface(ctx)) return null;
1556
+ const docs = docsBundle(ctx);
1557
+ if (!docs) return null;
1558
+ return /\baider\b/i.test(docs) || /\baider[- ]chat\b/i.test(docs);
1325
1559
  },
1326
1560
  impact: 'medium',
1327
1561
  rating: 3,
@@ -1336,16 +1570,16 @@ const AIDER_TECHNIQUES = {
1336
1570
  id: 'AD-P04',
1337
1571
  name: 'No conflicting platform configs (CLAUDE.md, AGENTS.md) without awareness',
1338
1572
  check: (ctx) => {
1339
- const hasAider = Boolean(ctx.fileContent('.aider.conf.yml'));
1573
+ // PP-04: Multi-platform is the norm in 2026, not a defect. Widen the
1574
+ // awareness source to the full docsBundle (CLAUDE.md often mentions
1575
+ // Aider, AGENTS.md often mentions Claude, README often does both).
1576
+ const hasAider = hasAiderConfig(ctx);
1340
1577
  const hasClaude = Boolean(ctx.fileContent('CLAUDE.md')) || Boolean(ctx.fileContent('.claude/CLAUDE.md'));
1341
1578
  const hasCodex = Boolean(ctx.fileContent('AGENTS.md'));
1342
1579
  if (!hasAider) return null;
1343
- // Multi-platform is fine just check conventions mention it
1344
- if (hasClaude || hasCodex) {
1345
- const content = conventionContent(ctx);
1346
- return /\bmulti[- ]?platform\b|\bclaude\b|\bcodex\b/i.test(content);
1347
- }
1348
- return true;
1580
+ if (!hasClaude && !hasCodex) return true;
1581
+ const content = docsBundle(ctx);
1582
+ return /\bmulti[- ]?platform\b|\bclaude\b|\bcodex\b|\bagents?\.md\b|\baider\b/i.test(content);
1349
1583
  },
1350
1584
  impact: 'medium',
1351
1585
  rating: 3,
@@ -1359,12 +1593,16 @@ const AIDER_TECHNIQUES = {
1359
1593
  aiderModelCostAwareness: {
1360
1594
  id: 'AD-P05',
1361
1595
  name: 'Model cost awareness configured (cache-prompts or explicit model selection)',
1596
+ // PP-04: Cost optimisation. Pass when any cost-aware knob is set; N/A
1597
+ // when none are — the default is sensible for most teams.
1362
1598
  check: (ctx) => {
1363
1599
  const config = configContent(ctx);
1364
1600
  if (!config) return null;
1365
- return /\bcache-prompts\s*:\s*true\b/i.test(config) ||
1601
+ if (/\bcache-prompts\s*:\s*true\b/i.test(config) ||
1366
1602
  /\bweak-model\s*:/i.test(config) ||
1367
- /\beditor-model\s*:/i.test(config);
1603
+ /\beditor-model\s*:/i.test(config) ||
1604
+ /\bmodel\s*:/i.test(config)) return true;
1605
+ return null;
1368
1606
  },
1369
1607
  impact: 'medium',
1370
1608
  rating: 3,
@@ -1378,10 +1616,17 @@ const AIDER_TECHNIQUES = {
1378
1616
  aiderGitBranchStrategy: {
1379
1617
  id: 'AD-P06',
1380
1618
  name: 'Git branch strategy for Aider work',
1619
+ // PP-04: Branch strategy lives in CONTRIBUTING / README more often than
1620
+ // CONVENTIONS. Widen source. N/A unless any Aider surface exists — we
1621
+ // don't expect arbitrary repos to document Aider-specific branching.
1381
1622
  check: (ctx) => {
1382
- const content = conventionContent(ctx);
1623
+ if (!hasAnyAiderSurface(ctx)) return null;
1624
+ const content = docsBundle(ctx);
1383
1625
  if (!content) return null;
1384
- return /\bbranch\b/i.test(content) && /\baider\b/i.test(content);
1626
+ // Either explicit aider+branch combo, or a documented branching workflow
1627
+ // (feature-branch / git-flow / trunk-based) is enough.
1628
+ if (/\baider\b/i.test(content) && /\bbranch\b/i.test(content)) return true;
1629
+ return /\bfeature[- ]branch\b|\bgit[- ]flow\b|\btrunk[- ]based\b|\bpull request\b.*\bbranch\b/i.test(content);
1385
1630
  },
1386
1631
  impact: 'medium',
1387
1632
  rating: 3,