code-auditor-mcp 5.2.0 → 5.3.3

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 (136) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +142 -0
  3. package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
  4. package/dist/analyzers/reactAnalyzer.js +35 -9
  5. package/dist/analyzers/reactAnalyzer.js.map +1 -1
  6. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +0 -2
  7. package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
  8. package/dist/analyzers/universal/UniversalDRYAnalyzer.js +4 -5
  9. package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
  10. package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
  11. package/dist/analyzers/universal/schema/codeAnalysis.js +51 -3
  12. package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
  13. package/dist/auditRunner.d.ts +9 -1
  14. package/dist/auditRunner.d.ts.map +1 -1
  15. package/dist/auditRunner.js +65 -7
  16. package/dist/auditRunner.js.map +1 -1
  17. package/dist/cacheGc.d.ts +88 -0
  18. package/dist/cacheGc.d.ts.map +1 -0
  19. package/dist/cacheGc.js +196 -0
  20. package/dist/cacheGc.js.map +1 -0
  21. package/dist/cli.js +65 -21
  22. package/dist/cli.js.map +1 -1
  23. package/dist/config/defaults.d.ts +60 -25
  24. package/dist/config/defaults.d.ts.map +1 -1
  25. package/dist/config/defaults.js +69 -58
  26. package/dist/config/defaults.js.map +1 -1
  27. package/dist/config/effectiveConfig.d.ts +1 -1
  28. package/dist/config/effectiveConfig.d.ts.map +1 -1
  29. package/dist/config/effectiveConfig.js +4 -6
  30. package/dist/config/effectiveConfig.js.map +1 -1
  31. package/dist/config/pathProfiles.d.ts +4 -3
  32. package/dist/config/pathProfiles.d.ts.map +1 -1
  33. package/dist/config/pathProfiles.js +3 -2
  34. package/dist/config/pathProfiles.js.map +1 -1
  35. package/dist/constants.d.ts +1 -1
  36. package/dist/dataPaths.d.ts +17 -0
  37. package/dist/dataPaths.d.ts.map +1 -1
  38. package/dist/dataPaths.js +20 -2
  39. package/dist/dataPaths.js.map +1 -1
  40. package/dist/installer.d.ts.map +1 -1
  41. package/dist/installer.js +2 -1
  42. package/dist/installer.js.map +1 -1
  43. package/dist/languages/adapterBridge.d.ts +11 -0
  44. package/dist/languages/adapterBridge.d.ts.map +1 -1
  45. package/dist/languages/adapterBridge.js +11 -0
  46. package/dist/languages/adapterBridge.js.map +1 -1
  47. package/dist/languages/types.d.ts +8 -0
  48. package/dist/languages/types.d.ts.map +1 -1
  49. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +13 -0
  50. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
  51. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +117 -15
  52. package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
  53. package/dist/ledger.d.ts +7 -0
  54. package/dist/ledger.d.ts.map +1 -1
  55. package/dist/ledger.js +7 -0
  56. package/dist/ledger.js.map +1 -1
  57. package/dist/mcpDiagnostics.d.ts +2 -0
  58. package/dist/mcpDiagnostics.d.ts.map +1 -1
  59. package/dist/mcpDiagnostics.js +2 -0
  60. package/dist/mcpDiagnostics.js.map +1 -1
  61. package/dist/phase/candidates.d.ts +53 -0
  62. package/dist/phase/candidates.d.ts.map +1 -0
  63. package/dist/phase/candidates.js +67 -0
  64. package/dist/phase/candidates.js.map +1 -0
  65. package/dist/phase/imports.d.ts.map +1 -1
  66. package/dist/phase/imports.js +1 -0
  67. package/dist/phase/imports.js.map +1 -1
  68. package/dist/phase/oracles.d.ts +28 -0
  69. package/dist/phase/oracles.d.ts.map +1 -1
  70. package/dist/phase/oracles.js +28 -0
  71. package/dist/phase/oracles.js.map +1 -1
  72. package/dist/phase/phaseModel.d.ts +11 -1
  73. package/dist/phase/phaseModel.d.ts.map +1 -1
  74. package/dist/phase/phaseModel.js +16 -5
  75. package/dist/phase/phaseModel.js.map +1 -1
  76. package/dist/phase/rules/conventions.d.ts.map +1 -1
  77. package/dist/phase/rules/conventions.js +6 -2
  78. package/dist/phase/rules/conventions.js.map +1 -1
  79. package/dist/phase/rules/dataAccess.d.ts.map +1 -1
  80. package/dist/phase/rules/dataAccess.js +13 -0
  81. package/dist/phase/rules/dataAccess.js.map +1 -1
  82. package/dist/phase/rules/documentation.d.ts.map +1 -1
  83. package/dist/phase/rules/documentation.js +11 -1
  84. package/dist/phase/rules/documentation.js.map +1 -1
  85. package/dist/phase/rules/dry.d.ts +16 -7
  86. package/dist/phase/rules/dry.d.ts.map +1 -1
  87. package/dist/phase/rules/dry.js +51 -31
  88. package/dist/phase/rules/dry.js.map +1 -1
  89. package/dist/phase/rules/secrets.d.ts.map +1 -1
  90. package/dist/phase/rules/secrets.js +29 -4
  91. package/dist/phase/rules/secrets.js.map +1 -1
  92. package/dist/phase/rules/styles.d.ts.map +1 -1
  93. package/dist/phase/rules/styles.js +14 -0
  94. package/dist/phase/rules/styles.js.map +1 -1
  95. package/dist/phase/rules/unreferencedModule.d.ts.map +1 -1
  96. package/dist/phase/rules/unreferencedModule.js +16 -5
  97. package/dist/phase/rules/unreferencedModule.js.map +1 -1
  98. package/dist/phase/stringLiterals.d.ts.map +1 -1
  99. package/dist/phase/stringLiterals.js +16 -7
  100. package/dist/phase/stringLiterals.js.map +1 -1
  101. package/dist/phase/types.d.ts +58 -0
  102. package/dist/phase/types.d.ts.map +1 -1
  103. package/dist/pipeline.d.ts +4 -0
  104. package/dist/pipeline.d.ts.map +1 -1
  105. package/dist/pipeline.js +4 -0
  106. package/dist/pipeline.js.map +1 -1
  107. package/dist/services/auditJobService.d.ts +15 -0
  108. package/dist/services/auditJobService.d.ts.map +1 -1
  109. package/dist/services/auditJobService.js +15 -0
  110. package/dist/services/auditJobService.js.map +1 -1
  111. package/dist/services/projectTaskHelpers.d.ts +7 -0
  112. package/dist/services/projectTaskHelpers.d.ts.map +1 -1
  113. package/dist/services/projectTaskHelpers.js +7 -0
  114. package/dist/services/projectTaskHelpers.js.map +1 -1
  115. package/dist/services/whitelistService.d.ts +28 -0
  116. package/dist/services/whitelistService.d.ts.map +1 -1
  117. package/dist/services/whitelistService.js +28 -0
  118. package/dist/services/whitelistService.js.map +1 -1
  119. package/dist/styles/tailwindUtilityExpander.d.ts +4 -0
  120. package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
  121. package/dist/styles/tailwindUtilityExpander.js +4 -0
  122. package/dist/styles/tailwindUtilityExpander.js.map +1 -1
  123. package/dist/types.d.ts +50 -0
  124. package/dist/types.d.ts.map +1 -1
  125. package/dist/types.js.map +1 -1
  126. package/dist/version.generated.d.ts +1 -1
  127. package/dist/version.generated.js +1 -1
  128. package/package.json +4 -2
  129. package/plugin/.claude-plugin/manifest.json +7 -7
  130. package/plugin/.claude-plugin/plugin.json +1 -1
  131. package/plugin/hooks/hooks.json +4 -4
  132. package/plugin/scripts/hook-audit.sh +16 -10
  133. package/plugin/scripts/hook-common.sh +4 -5
  134. package/plugin/scripts/hook-self-audit.sh +12 -6
  135. package/plugin/skills/code-auditor/SKILL.md +1 -1
  136. package/server.json +2 -2
package/dist/cli.js CHANGED
@@ -27,6 +27,8 @@ import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
27
27
  import { runNextFile } from './nextFileIncremental.js';
28
28
  import { describeSqliteBackend } from './sqlite/driver.js';
29
29
  import { isRuleTimingEnabled } from './analyzers/ruleTiming.js';
30
+ import { gateBudgetResolutions } from './config/defaults.js';
31
+ import { buildCandidatesArtifact } from './phase/candidates.js';
30
32
  const program = new Command();
31
33
  // Legacy severity names that were renamed off Spec-54's "advisory" back to "high"
32
34
  // (Spec-54's third tier rendered as "advisory", which read as optional — the very
@@ -181,6 +183,27 @@ function printAuditDiagnostics(result) {
181
183
  console.log(chalk.yellow(` ${file} — ${entry.lines.length} site(s)${loc}: ${shown}${more}`));
182
184
  }
183
185
  }
186
+ // ── Per-rule unproven breakdown (K9) ──────────────────────────
187
+ // The raw unproven-site total answers "which analyzer produced these?"
188
+ // (`schema`); this answers the question the finding actually asks — "which
189
+ // rule may have under-reported?" One unproven site blanks every rule in its
190
+ // `affectedRules` set, so a rule's `count` is the number of sites that could
191
+ // have produced a finding under it, with `causes` naming the emission path
192
+ // that drove each (parse-failure / dialect-undetermined / unresolved-import /
193
+ // unproven-receiver). This is the missing discriminant: it surfaces the 465
194
+ // sites as *per-rule under-report risk*, not a single opaque analyzer total.
195
+ const unprovenByRule = result.summary?.unprovenSitesByRule;
196
+ if (unprovenByRule && Object.keys(unprovenByRule).length > 0) {
197
+ console.log(chalk.yellow(`── Unproven sites by rule ──`));
198
+ const ruleRows = Object.entries(unprovenByRule).sort(([, a], [, b]) => b.count - a.count);
199
+ for (const [rule, entry] of ruleRows) {
200
+ const causes = Object.entries(entry.causes)
201
+ .sort(([, a], [, b]) => b - a)
202
+ .map(([cause, n]) => `${n} ${cause}`)
203
+ .join(' · ');
204
+ console.log(chalk.yellow(` ${rule} — ${entry.count} site(s): ${causes}`));
205
+ }
206
+ }
184
207
  // ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
185
208
  const otherCoverageGaps = allDiagnostics.filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
186
209
  if (otherCoverageGaps.length > 0) {
@@ -237,6 +260,19 @@ function printAuditDiagnostics(result) {
237
260
  console.log(chalk.gray(` … and ${deadClusters.length - 10} more clusters`));
238
261
  }
239
262
  }
263
+ // ── Directive K10 — cache GC ─────────────────────────────────
264
+ // State what the GC collected so a silent delete of operator cache is visible.
265
+ const cacheGc = result.metadata?.cacheGc;
266
+ if (cacheGc && (cacheGc.removedCliVersions.length > 0 || cacheGc.removedProjects.length > 0)) {
267
+ const parts = [];
268
+ if (cacheGc.removedCliVersions.length > 0) {
269
+ parts.push(`${cacheGc.removedCliVersions.length} superseded CLI cop${cacheGc.removedCliVersions.length === 1 ? 'y' : 'ies'}`);
270
+ }
271
+ if (cacheGc.removedProjects.length > 0) {
272
+ parts.push(`${cacheGc.removedProjects.length} abandoned project index${cacheGc.removedProjects.length === 1 ? '' : 'es'}`);
273
+ }
274
+ console.log(chalk.gray(`── Cache GC ── collected ${parts.join(' and ')}`));
275
+ }
240
276
  }
241
277
  /**
242
278
  * Print the findings summary: the delta view when a baseline exists, or the
@@ -552,6 +588,7 @@ program
552
588
  .option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
553
589
  .option('--detach', 'Run the audit in a detached background process and print the job ID')
554
590
  .option('--explain-skipped', 'Print a per-reason breakdown of files dropped from analysis')
591
+ .option('--emit-candidates <path>', 'Write the sampled-audit candidates artifact (rule dispositions + candidate sites) to this file')
555
592
  .action(async (options) => {
556
593
  console.log(chalk.blue('🔍 Code Quality Audit Tool'));
557
594
  console.log(chalk.gray('══════════════════════════════════════════════════'));
@@ -604,16 +641,18 @@ program
604
641
  printFileAccounting(result, !!options.explainSkipped);
605
642
  printUnparsedAndSkipped(result);
606
643
  printPipelineStages(result, options);
607
- // Spec-20 R4: built-in profile visibility — silent behavior changes
608
- // are never acceptable. Notify when scripts-and-tests excludes findings
609
- // from the blocking gate.
610
- const builtinCapped = Object.values(result.analyzerResults)
611
- .reduce((count, ar) => count + ar.violations.filter(v => v.profile === 'scripts-and-tests').length, 0);
612
- if (builtinCapped > 0) {
613
- console.log(chalk.blue(`\nℹ️ ${builtinCapped.toLocaleString()} readings excluded from the blocking gate by built-in profile "scripts-and-tests" (scripts/tests/fixtures).`));
614
- console.log(chalk.gray(` Set ${chalk.cyan('"builtin": false')} in .codeauditor.json to disable.`));
615
- }
616
644
  await writeReport(result, options);
645
+ // Directive H item 2 — emit the sampled-audit candidates artifact when
646
+ // `--emit-candidates` names a destination. The artifact is the audit's
647
+ // only dependency on app/: rule disposition vocabularies (read from the
648
+ // rules) + candidate sites with their source line text. Nothing here
649
+ // reaches a model; it only serializes what the deterministic matchers
650
+ // emitted.
651
+ if (options.emitCandidates) {
652
+ const artifact = await buildCandidatesArtifact(result);
653
+ await fs.writeFile(resolve(options.emitCandidates), JSON.stringify(artifact, null, 2), 'utf8');
654
+ console.log(chalk.gray(`Candidates artifact written to ${options.emitCandidates} (${artifact.rules.reduce((n, r) => n + r.sites.length, 0)} sites across ${artifact.rules.length} rules)`));
655
+ }
617
656
  applyFailOnGates(result, violations, baseline, options, failOnSeverity);
618
657
  }
619
658
  catch (error) {
@@ -724,20 +763,29 @@ program
724
763
  // blocks; there is no configurable gate.
725
764
  const { blocking, resolutionGaps } = computeGatingDecision(violations, BLOCKING_SEVERITIES);
726
765
  await emitChangedOutput(result, violations, blocking, dismissedCount, options);
727
- // Spec 38 R2/R3 — per-rule timing + gate cpu-time, opt-in by env var.
766
+ // Spec 38 R2/R3 — per-rule timing + gate work count, opt-in by env var.
728
767
  // Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
729
- // The gate metric is CPU time (auditCpuMs), not wall clock: the budget is
730
- // about a slow *rule*, and CPU time isolates that from a slow machine.
768
+ // Directive J: the gate budget is now a deterministic-work count
769
+ // (receiverResolutions), not CPU time — the count moves only when the work
770
+ // does, where CPU time jitters with machine load. The wall/CPU times are
771
+ // still printed as a measurement for bench/gate-timing.ts, but they are no
772
+ // longer what the budget asserts on.
731
773
  if (isRuleTimingEnabled()) {
732
774
  const ruleTiming = result.metadata?.ruleTiming;
733
775
  const gateMs = result.metadata?.auditCpuMs;
734
776
  const gateWallMs = result.metadata?.auditDuration;
777
+ const gateWork = result.metadata?.receiverResolutions;
778
+ const budget = gateBudgetResolutions();
735
779
  const lines = [];
736
780
  lines.push('');
737
- // §16 criterion 16 — report both wall-clock (what the hook's user feels)
738
- // and CPU (what the rule actually burned); the budget asserts on CPU.
781
+ // Directive J — the asserted metric: deterministic receiver-resolution
782
+ // count vs the single-source budget (gateBudgetResolutions()). The
783
+ // verifier parses this line, so the CLI is the one source of the budget.
784
+ lines.push(`⏱ gate work: ${gateWork ?? 0} receiver resolutions (budget ${budget})`);
785
+ // Measurement, not assertion — wall-clock (what the hook's user feels)
786
+ // and CPU (what the rule actually burned); consumed by bench/gate-timing.ts.
739
787
  lines.push(`⏱ gate wall-clock: ${(gateWallMs ?? 0).toFixed(1)} ms`);
740
- lines.push(`⏱ gate cpu-time: ${(gateMs ?? 0).toFixed(1)} ms (budget 400 ms)`);
788
+ lines.push(`⏱ gate cpu-time: ${(gateMs ?? 0).toFixed(1)} ms`);
741
789
  lines.push('── per-rule timing (slowest first) ──');
742
790
  if (ruleTiming && ruleTiming.length > 0) {
743
791
  for (const t of ruleTiming) {
@@ -1883,9 +1931,7 @@ configCmd
1883
1931
  else {
1884
1932
  console.log(chalk.bold(`Profiles matching: ${options.file}`));
1885
1933
  for (const name of resolved.matchedProfileNames) {
1886
- const profile = profiles.find(p => p.name === name);
1887
- const builtinTag = profile?.builtin !== false && profiles.some(p => p.name === name) ? '' : '';
1888
- console.log(` ${chalk.cyan(name)}${builtinTag ? chalk.gray(' (built-in)') : ''}`);
1934
+ console.log(` ${chalk.cyan(name)}`);
1889
1935
  }
1890
1936
  if (resolved.excludeFromGate) {
1891
1937
  console.log(chalk.yellow(` excludeFromGate: true`));
@@ -1908,7 +1954,6 @@ configCmd
1908
1954
  name: p.name,
1909
1955
  paths: p.paths,
1910
1956
  overrides: p.overrides,
1911
- builtin: p.builtin !== false,
1912
1957
  })),
1913
1958
  }, null, 2) + '\n');
1914
1959
  }
@@ -1919,8 +1964,7 @@ configCmd
1919
1964
  else {
1920
1965
  console.log(chalk.bold('Active path profiles:'));
1921
1966
  for (const p of profiles) {
1922
- const tag = p.builtin !== false ? chalk.gray(' [built-in]') : '';
1923
- console.log(` ${chalk.cyan(p.name)}${tag}`);
1967
+ console.log(` ${chalk.cyan(p.name)}`);
1924
1968
  console.log(chalk.gray(` paths: ${p.paths.join(', ')}`));
1925
1969
  if (p.overrides && Object.keys(p.overrides).length > 0) {
1926
1970
  for (const [key, value] of Object.entries(p.overrides)) {