code-auditor-mcp 3.9.6 → 3.9.11
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +220 -0
- package/README.md +18 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +4 -1
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.d.ts +8 -0
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +32 -10
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +23 -20
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +1 -0
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +19 -5
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +44 -2
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js +153 -8
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
- package/dist/analyzers/universal/schema/config.d.ts +8 -0
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/config.js +9 -0
- package/dist/analyzers/universal/schema/config.js.map +1 -1
- package/dist/analyzers/universal/schema/types.d.ts +5 -0
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -1
- package/dist/auditRouter.d.ts.map +1 -1
- package/dist/auditRouter.js +6 -1
- package/dist/auditRouter.js.map +1 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +62 -8
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.js +157 -13
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +29 -7
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/defaults.d.ts +2 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +15 -6
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +22 -1
- package/dist/config/effectiveConfig.d.ts.map +1 -1
- package/dist/config/effectiveConfig.js +69 -2
- package/dist/config/effectiveConfig.js.map +1 -1
- package/dist/config/lintConfigReader.d.ts +56 -0
- package/dist/config/lintConfigReader.d.ts.map +1 -0
- package/dist/config/lintConfigReader.js +263 -0
- package/dist/config/lintConfigReader.js.map +1 -0
- package/dist/constants.d.ts +9 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -6
- package/dist/constants.js.map +1 -1
- package/dist/dataPaths.d.ts +8 -0
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +21 -0
- package/dist/dataPaths.js.map +1 -1
- package/dist/dismissals.d.ts +59 -0
- package/dist/dismissals.d.ts.map +1 -0
- package/dist/dismissals.js +128 -0
- package/dist/dismissals.js.map +1 -0
- package/dist/enforcement/gate.d.ts.map +1 -1
- package/dist/enforcement/gate.js +5 -0
- package/dist/enforcement/gate.js.map +1 -1
- package/dist/hooks/codex.d.ts.map +1 -1
- package/dist/hooks/codex.js +18 -6
- package/dist/hooks/codex.js.map +1 -1
- package/dist/hooks/core.d.ts +12 -0
- package/dist/hooks/core.d.ts.map +1 -1
- package/dist/hooks/core.js +12 -3
- package/dist/hooks/core.js.map +1 -1
- package/dist/hooks/cursor.d.ts.map +1 -1
- package/dist/hooks/cursor.js +17 -10
- package/dist/hooks/cursor.js.map +1 -1
- package/dist/installConfig.d.ts +40 -0
- package/dist/installConfig.d.ts.map +1 -0
- package/dist/installConfig.js +84 -0
- package/dist/installConfig.js.map +1 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +2 -15
- package/dist/installer.js.map +1 -1
- package/dist/languages/go/analyzer +0 -0
- package/dist/languages/go/analyzer-src/solid.go +14 -6
- package/dist/mcp.js +54 -0
- package/dist/mcp.js.map +1 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +12 -1
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +87 -7
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/reporting/csvReportGenerator.d.ts.map +1 -1
- package/dist/reporting/csvReportGenerator.js +1 -0
- package/dist/reporting/csvReportGenerator.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.d.ts.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +5 -0
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/reporting/jsonReportGenerator.d.ts.map +1 -1
- package/dist/reporting/jsonReportGenerator.js +6 -0
- package/dist/reporting/jsonReportGenerator.js.map +1 -1
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +3 -0
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/services/auditJobService.d.ts.map +1 -1
- package/dist/services/auditJobService.js +4 -7
- package/dist/services/auditJobService.js.map +1 -1
- package/dist/structuralSignature.d.ts +41 -0
- package/dist/structuralSignature.d.ts.map +1 -0
- package/dist/structuralSignature.js +93 -0
- package/dist/structuralSignature.js.map +1 -0
- package/dist/telemetry.d.ts +80 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +148 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +72 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.generated.d.ts +2 -0
- package/dist/version.generated.d.ts.map +1 -0
- package/dist/version.generated.js +7 -0
- package/dist/version.generated.js.map +1 -0
- package/package.json +3 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/scripts/hook-common.sh +37 -9
- package/plugin/skills/code-auditor/SKILL.md +24 -2
- package/server.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -8,11 +8,10 @@ import { Command } from 'commander';
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { createAuditRunner } from './auditRunner.js';
|
|
10
10
|
import { runAuditDispatch } from './auditRouter.js';
|
|
11
|
-
import { readFileSync } from 'fs';
|
|
12
11
|
import { promises as fs } from 'fs';
|
|
13
12
|
import { createInterface } from 'readline';
|
|
14
|
-
import { fileURLToPath } from 'url';
|
|
15
13
|
import { dirname, isAbsolute, join, relative, resolve } from 'path';
|
|
14
|
+
import { PACKAGE_VERSION } from './constants.js';
|
|
16
15
|
import inquirer from 'inquirer';
|
|
17
16
|
import { CodeMapGenerator } from './services/CodeMapGenerator.js';
|
|
18
17
|
import { initParsers } from './languages/index.js';
|
|
@@ -20,16 +19,16 @@ import { queryParser } from './search/QueryParser.js';
|
|
|
20
19
|
import { CodeIndexDB } from './codeIndexDB.js';
|
|
21
20
|
import { getFilesProcessed, getFactsConsumed, isVisitorStatus, isReducerStatus } from './pipeline.js';
|
|
22
21
|
import { createBaselineFromFindings, saveBaseline, loadBaseline, diffBaselines } from './baseline.js';
|
|
22
|
+
import { fingerprint, buildFingerprintInput } from './fingerprint.js';
|
|
23
|
+
import { buildDismissalEntry, upsertDismissal } from './dismissals.js';
|
|
24
|
+
import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, formatTelemetryPreview, sendTelemetry, languageHint } from './telemetry.js';
|
|
25
|
+
import { getInstallId } from './installConfig.js';
|
|
23
26
|
import { ALL_ANALYZERS } from './analyzers/ruleRegistry.js';
|
|
24
27
|
import { computeGatingDecision } from './enforcement/gate.js';
|
|
25
28
|
import { BLOCKING_SEVERITIES } from './types.js';
|
|
26
29
|
import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
|
|
27
30
|
import { runNextFile } from './nextFileIncremental.js';
|
|
28
31
|
import { describeSqliteBackend } from './sqlite/driver.js';
|
|
29
|
-
// Get package.json for version info
|
|
30
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
31
|
-
const __dirname = dirname(__filename);
|
|
32
|
-
const packageJson = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
33
32
|
const program = new Command();
|
|
34
33
|
// Spec 44 R4 — file accounting summary line + `--explain-skipped` breakdown.
|
|
35
34
|
// The full per-reason file lists live in the JSON report (`metadata.fileAccounting`);
|
|
@@ -82,7 +81,7 @@ function printFileAccounting(result, explainSkipped) {
|
|
|
82
81
|
program
|
|
83
82
|
.name('code-auditor')
|
|
84
83
|
.description('TypeScript/JavaScript code quality auditor with AI tool integration')
|
|
85
|
-
.version(`${
|
|
84
|
+
.version(`${PACKAGE_VERSION} (sqlite: ${describeSqliteBackend()})`);
|
|
86
85
|
// Legacy audit command (default behavior)
|
|
87
86
|
program
|
|
88
87
|
.command('audit', { isDefault: true })
|
|
@@ -139,6 +138,10 @@ program
|
|
|
139
138
|
});
|
|
140
139
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
141
140
|
const baseline = result.metadata?.baseline;
|
|
141
|
+
// Spec 57 — dismissed count is reported alongside the total, never
|
|
142
|
+
// subtracted from it ("43 findings, 3 dismissed").
|
|
143
|
+
const dismissedCount = result.summary.dismissed ?? 0;
|
|
144
|
+
const dismissedSuffix = dismissedCount > 0 ? `, ${dismissedCount} dismissed` : '';
|
|
142
145
|
// ── Coverage panel leads the report (Spec 47 R2) ─────────────
|
|
143
146
|
// A diagnostic report opens with what was measured before it lists any
|
|
144
147
|
// readings, so a zero-reading report can't be mistaken for a clean tree.
|
|
@@ -153,6 +156,28 @@ program
|
|
|
153
156
|
`${covUnassessed} unassessed · ${covNotApplicable} not-applicable · ` +
|
|
154
157
|
`${covCannotFire} cannot-fire`));
|
|
155
158
|
}
|
|
159
|
+
// ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
|
|
160
|
+
// Coverage diagnostics are the analyzer saying "my visibility ends here",
|
|
161
|
+
// not "the code is wrong". They lead with counts, then per-occurrence
|
|
162
|
+
// file:line — visible and counted, never blocking (they never reach the
|
|
163
|
+
// gate). Rendered alongside the coverage panel that leads the report.
|
|
164
|
+
const coverageDiagnostics = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import');
|
|
165
|
+
if (coverageDiagnostics.length > 0) {
|
|
166
|
+
const byKind = {};
|
|
167
|
+
for (const d of coverageDiagnostics)
|
|
168
|
+
byKind[d.kind] = (byKind[d.kind] ?? 0) + 1;
|
|
169
|
+
const totals = Object.entries(byKind)
|
|
170
|
+
.map(([kind, n]) => `${n} ${kind}`)
|
|
171
|
+
.join(' · ');
|
|
172
|
+
console.log(chalk.gray(`── Coverage gaps ── ${totals}`));
|
|
173
|
+
for (const d of coverageDiagnostics.slice(0, 20)) {
|
|
174
|
+
const loc = d.file ? `${d.file}${typeof d.line === 'number' ? `:${d.line}` : ''}` : '(unknown)';
|
|
175
|
+
console.log(chalk.gray(` ${loc} [${d.kind}] — ${d.message}`));
|
|
176
|
+
}
|
|
177
|
+
if (coverageDiagnostics.length > 20) {
|
|
178
|
+
console.log(chalk.gray(` … and ${coverageDiagnostics.length - 20} more`));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
156
181
|
// ── Delta output (Spec 18 R2) ─────────────────────────────────
|
|
157
182
|
if (baseline && !options.full) {
|
|
158
183
|
const newViolations = violations.filter((v) => v.new === true);
|
|
@@ -215,7 +240,7 @@ program
|
|
|
215
240
|
}
|
|
216
241
|
else if (!baseline) {
|
|
217
242
|
// No baseline: current behavior + hint
|
|
218
|
-
console.log(`\nFound ${result.summary.totalViolations}
|
|
243
|
+
console.log(`\nFound ${result.summary.totalViolations} findings${dismissedSuffix}`);
|
|
219
244
|
console.log(`Critical: ${result.summary.criticalIssues}`);
|
|
220
245
|
console.log(`Severe: ${result.summary.severe}`);
|
|
221
246
|
console.log(`High: ${result.summary.high}`);
|
|
@@ -224,7 +249,7 @@ program
|
|
|
224
249
|
}
|
|
225
250
|
else {
|
|
226
251
|
// --full with baseline: full itemized inventory (current behavior)
|
|
227
|
-
console.log(`\nFound ${result.summary.totalViolations}
|
|
252
|
+
console.log(`\nFound ${result.summary.totalViolations} findings${dismissedSuffix}`);
|
|
228
253
|
console.log(`Critical: ${result.summary.criticalIssues}`);
|
|
229
254
|
console.log(`Severe: ${result.summary.severe}`);
|
|
230
255
|
console.log(`High: ${result.summary.high}`);
|
|
@@ -381,6 +406,9 @@ program
|
|
|
381
406
|
const severityOrder = ['critical', 'severe', 'high'];
|
|
382
407
|
const failIndex = severityOrder.indexOf(failOnSeverity);
|
|
383
408
|
const hasAtOrAbove = evaluableViolations.some((v) => {
|
|
409
|
+
// Spec 57 — a dismissed finding never blocks the gate.
|
|
410
|
+
if (v.dismissed)
|
|
411
|
+
return false;
|
|
384
412
|
const vIndex = severityOrder.indexOf(v.severity);
|
|
385
413
|
return vIndex >= 0 && vIndex <= failIndex;
|
|
386
414
|
});
|
|
@@ -466,6 +494,10 @@ program
|
|
|
466
494
|
}
|
|
467
495
|
// Collect all violations
|
|
468
496
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
497
|
+
// Spec 57 — dismissed count is reported alongside the total, never
|
|
498
|
+
// subtracted from it. The hook surface (`changed`) is agent-facing, so it
|
|
499
|
+
// must show the same "N findings, M dismissed" figure as the full report.
|
|
500
|
+
const dismissedCount = result.summary.dismissed ?? 0;
|
|
469
501
|
// Blocking gate decision (Spec 45 R1/R4, Spec 54 R3), computed once so the
|
|
470
502
|
// agent-facing before/after count (Spec 45 A2) and the exit code agree on
|
|
471
503
|
// the same number. Every registered rule participates (R1); enforcement is
|
|
@@ -481,7 +513,7 @@ program
|
|
|
481
513
|
}
|
|
482
514
|
else if (options.json) {
|
|
483
515
|
const projectDir = resolve(options.path || process.cwd());
|
|
484
|
-
const
|
|
516
|
+
const jsonViolations = violations.map((v) => {
|
|
485
517
|
// Compute relative path if file resolves inside the project
|
|
486
518
|
let filePath = v.file || '';
|
|
487
519
|
if (filePath.startsWith('/') || filePath.startsWith('\\\\')) {
|
|
@@ -506,9 +538,23 @@ program
|
|
|
506
538
|
enclosingSymbol: v.symbol || v.enclosingFunction || '',
|
|
507
539
|
suggestion: v.suggestion || '',
|
|
508
540
|
details: v.details || '',
|
|
509
|
-
...(v.new !== undefined && { new: v.new })
|
|
541
|
+
...(v.new !== undefined && { new: v.new }),
|
|
542
|
+
...(v.dismissed !== undefined && { dismissed: v.dismissed }),
|
|
543
|
+
fingerprint: fingerprint(buildFingerprintInput(v))
|
|
510
544
|
};
|
|
511
545
|
});
|
|
546
|
+
// Coverage diagnostics ride alongside violations so an agent sees "the
|
|
547
|
+
// analyzer couldn't resolve this" without a metadata flag — and without
|
|
548
|
+
// it ever gating (diagnostics never reach computeGatingDecision).
|
|
549
|
+
const jsonDiagnostics = (result.metadata?.diagnostics ?? []).map((d) => ({
|
|
550
|
+
analyzer: d.analyzerName ?? d.analyzer ?? '',
|
|
551
|
+
kind: d.kind,
|
|
552
|
+
message: d.message,
|
|
553
|
+
...(d.file ? { file: d.file } : {}),
|
|
554
|
+
...(typeof d.line === 'number' ? { line: d.line } : {}),
|
|
555
|
+
...(d.details ? { details: d.details } : {}),
|
|
556
|
+
}));
|
|
557
|
+
const jsonOutput = { violations: jsonViolations, diagnostics: jsonDiagnostics };
|
|
512
558
|
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
|
|
513
559
|
}
|
|
514
560
|
else if (!options.quiet || violations.length > 0) {
|
|
@@ -522,6 +568,9 @@ program
|
|
|
522
568
|
console.log('');
|
|
523
569
|
printCountSummary(violations);
|
|
524
570
|
console.log(chalk.bold(`gate before/after: ${violations.length} → ${blocking.length} blocking`));
|
|
571
|
+
if (dismissedCount > 0) {
|
|
572
|
+
console.log(chalk.dim(` ${dismissedCount} dismissed — still counted above, excluded from the gate`));
|
|
573
|
+
}
|
|
525
574
|
console.log(chalk.gray('── Violations ────────────────────────────────────'));
|
|
526
575
|
for (const v of violations) {
|
|
527
576
|
const icon = v.severity === 'critical' ? '🔴' :
|
|
@@ -529,7 +578,10 @@ program
|
|
|
529
578
|
const statusTag = v.new === false
|
|
530
579
|
? chalk.dim(' [known — still open]')
|
|
531
580
|
: '';
|
|
532
|
-
|
|
581
|
+
const dismissedTag = v.dismissed
|
|
582
|
+
? chalk.dim(' [dismissed]')
|
|
583
|
+
: '';
|
|
584
|
+
console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}${statusTag}${dismissedTag}`);
|
|
533
585
|
}
|
|
534
586
|
}
|
|
535
587
|
else {
|
|
@@ -911,7 +963,7 @@ program
|
|
|
911
963
|
}
|
|
912
964
|
// Create new baseline
|
|
913
965
|
const newBaseline = createBaselineFromFindings(allViolations, {
|
|
914
|
-
toolVersion:
|
|
966
|
+
toolVersion: PACKAGE_VERSION,
|
|
915
967
|
totalFindings: allViolations.filter((v) => v.analyzer !== 'invariants').length,
|
|
916
968
|
analyzerCounts,
|
|
917
969
|
corpusStats,
|
|
@@ -947,6 +999,90 @@ program
|
|
|
947
999
|
process.exit(1);
|
|
948
1000
|
}
|
|
949
1001
|
});
|
|
1002
|
+
// Dismiss command (Spec 57)
|
|
1003
|
+
program
|
|
1004
|
+
.command('dismiss <fingerprint>')
|
|
1005
|
+
.description('Dismiss one finding by fingerprint — a written reason is required')
|
|
1006
|
+
.requiredOption('--reason <reason>', 'Why this finding is dismissed (required — a dismissal without a reason is a config error)')
|
|
1007
|
+
.option('-p, --path <path>', 'Project path', process.cwd())
|
|
1008
|
+
.option('--telemetry-endpoint <url>', 'Feedback service endpoint override (defaults to the opt-in endpoint recorded by the telemetry tool)')
|
|
1009
|
+
.option('--json', 'Output as JSON')
|
|
1010
|
+
.action(async (fingerprintArg, options) => {
|
|
1011
|
+
try {
|
|
1012
|
+
const reason = (options.reason ?? '').trim();
|
|
1013
|
+
if (!reason) {
|
|
1014
|
+
console.error(chalk.red('Error: --reason is required and must be non-empty. A dismissal without a reason is a config error, not a suppression.'));
|
|
1015
|
+
process.exit(1);
|
|
1016
|
+
}
|
|
1017
|
+
const projectRoot = resolve(options.path || process.cwd());
|
|
1018
|
+
await initParsers();
|
|
1019
|
+
// Re-audit so the fingerprint resolves against the live finding. The
|
|
1020
|
+
// fingerprint is a content hash of rule+file+symbol, so it is stable
|
|
1021
|
+
// across runs on the same machine (same tuple the baseline uses).
|
|
1022
|
+
const runner = createAuditRunner({ projectRoot });
|
|
1023
|
+
const result = await runner.run();
|
|
1024
|
+
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
1025
|
+
const match = violations.find((v) => fingerprint(buildFingerprintInput(v)) === fingerprintArg);
|
|
1026
|
+
if (!match) {
|
|
1027
|
+
console.error(chalk.red(`Error: no finding matches fingerprint "${fingerprintArg}".`));
|
|
1028
|
+
console.error(chalk.gray(`The fingerprint is a content hash of rule+file+symbol. Get the exact value from `));
|
|
1029
|
+
console.error(chalk.gray(`the JSON report: run ${chalk.cyan('code-audit audit --format json')} (or ${chalk.cyan('code-audit changed --json')}) `));
|
|
1030
|
+
console.error(chalk.gray(`and copy the "fingerprint" field of the finding you want to dismiss.`));
|
|
1031
|
+
process.exit(1);
|
|
1032
|
+
}
|
|
1033
|
+
const entry = buildDismissalEntry(match, reason, PACKAGE_VERSION);
|
|
1034
|
+
const dismissals = upsertDismissal(projectRoot, entry);
|
|
1035
|
+
// Spec 57 — opt-in telemetry. Nothing is sent unless the user opted in via
|
|
1036
|
+
// the `telemetry` MCP tool (default OFF) AND an endpoint is configured. The
|
|
1037
|
+
// payload is printed for review before sending; any send failure is silent
|
|
1038
|
+
// and never affects the dismissal or the exit code.
|
|
1039
|
+
const telemetry = resolveTelemetryConfig({ endpoint: options.telemetryEndpoint });
|
|
1040
|
+
if (telemetry.enabled && telemetry.endpoint) {
|
|
1041
|
+
const signature = signatureForFinding(match.file, match.line ?? match.start?.line ?? 1, match.column ?? match.start?.column ?? 1) ?? 'unknown';
|
|
1042
|
+
const payload = buildTelemetryPayload({
|
|
1043
|
+
install_id: getInstallId(),
|
|
1044
|
+
toolVersion: PACKAGE_VERSION,
|
|
1045
|
+
rule: entry.rule,
|
|
1046
|
+
level: match.severity,
|
|
1047
|
+
reason: entry.reason,
|
|
1048
|
+
signature,
|
|
1049
|
+
lang: languageHint(match.file),
|
|
1050
|
+
});
|
|
1051
|
+
process.stderr.write(formatTelemetryPreview(payload));
|
|
1052
|
+
const sendResult = await sendTelemetry(payload, telemetry.endpoint);
|
|
1053
|
+
if (options.json) {
|
|
1054
|
+
// Surface the send outcome in machine output without ever failing on it.
|
|
1055
|
+
process.stderr.write(` telemetry: ${sendResult.sent ? 'sent' : 'not sent'}${sendResult.error ? ` (${sendResult.error})` : ''}\n`);
|
|
1056
|
+
}
|
|
1057
|
+
else if (!sendResult.sent) {
|
|
1058
|
+
console.log(chalk.gray(` Telemetry not sent${sendResult.error ? ` (${sendResult.error})` : ''} — offline is never an error.`));
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
console.log(chalk.gray(' Telemetry sent.'));
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
if (options.json) {
|
|
1065
|
+
process.stdout.write(JSON.stringify({
|
|
1066
|
+
success: true,
|
|
1067
|
+
fingerprint: entry.fingerprint,
|
|
1068
|
+
rule: entry.rule,
|
|
1069
|
+
file: entry.file,
|
|
1070
|
+
symbol: entry.symbol,
|
|
1071
|
+
reason: entry.reason,
|
|
1072
|
+
totalDismissed: dismissals.entries.length,
|
|
1073
|
+
}, null, 2) + '\n');
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
console.log(chalk.green(`\n✓ Dismissed ${chalk.bold(entry.rule)} @ ${entry.symbol || entry.file} — "${entry.reason}"`));
|
|
1077
|
+
console.log(chalk.gray(` ${dismissals.entries.length} dismissal(s) recorded in .codeauditor.dismissals.json.`));
|
|
1078
|
+
console.log(chalk.gray(` The finding is still counted in every report, but excluded from the gate.`));
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
catch (error) {
|
|
1082
|
+
console.error(chalk.red('Error:'), error);
|
|
1083
|
+
process.exit(1);
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
950
1086
|
// Generate configuration command
|
|
951
1087
|
program
|
|
952
1088
|
.command('generate-config')
|
|
@@ -2903,6 +3039,7 @@ program
|
|
|
2903
3039
|
const { getDefaultConfig, DEFAULT_CODE_INDEX_CONFIG } = await import('./config/defaults.js');
|
|
2904
3040
|
const { computeEffectiveConfig, computeTopLevelConfig, } = await import('./config/effectiveConfig.js');
|
|
2905
3041
|
const { getPreset, PRESET_IDS } = await import('./presets/presets.js');
|
|
3042
|
+
const { readProjectLintThresholds, thresholdsToAnalyzerConfig } = await import('./config/lintConfigReader.js');
|
|
2906
3043
|
const projectRoot = pathModule.resolve(options.path);
|
|
2907
3044
|
const filePath = pathModule.resolve(file);
|
|
2908
3045
|
const configPath = pathModule.join(projectRoot, '.codeauditor.json');
|
|
@@ -2937,6 +3074,10 @@ program
|
|
|
2937
3074
|
}
|
|
2938
3075
|
}
|
|
2939
3076
|
const presets = presetIds.map((id) => getPreset(id));
|
|
3077
|
+
// Spec 50 R2 — read the project's own lint config so size thresholds sourced
|
|
3078
|
+
// from ESLint are named `project-lint-config` rather than `default`.
|
|
3079
|
+
const lintResult = await readProjectLintThresholds(projectRoot);
|
|
3080
|
+
const lintConfig = lintResult ? thresholdsToAnalyzerConfig(lintResult.thresholds) : {};
|
|
2940
3081
|
const effective = computeEffectiveConfig({
|
|
2941
3082
|
filePath,
|
|
2942
3083
|
projectRoot,
|
|
@@ -2944,6 +3085,7 @@ program
|
|
|
2944
3085
|
pathProfiles,
|
|
2945
3086
|
enabledAnalyzers,
|
|
2946
3087
|
presets,
|
|
3088
|
+
lintConfig,
|
|
2947
3089
|
});
|
|
2948
3090
|
const topLevel = computeTopLevelConfig(pristine, config);
|
|
2949
3091
|
if (options.json) {
|
|
@@ -2952,6 +3094,7 @@ program
|
|
|
2952
3094
|
projectRoot,
|
|
2953
3095
|
configPath,
|
|
2954
3096
|
presets: presetIds,
|
|
3097
|
+
lintConfigPath: lintResult?.configPath ?? null,
|
|
2955
3098
|
matchedProfiles: effective.matchedProfiles,
|
|
2956
3099
|
excludeFromGate: effective.excludeFromGate ?? null,
|
|
2957
3100
|
topLevel,
|
|
@@ -2964,6 +3107,7 @@ program
|
|
|
2964
3107
|
console.log(`${chalk.bold('file:')} ${effective.relativePath}`);
|
|
2965
3108
|
console.log(`${chalk.bold('project root:')} ${projectRoot}`);
|
|
2966
3109
|
console.log(`${chalk.bold('presets:')} ${presetIds.length > 0 ? presetIds.join(', ') : chalk.gray('(none)')}`);
|
|
3110
|
+
console.log(`${chalk.bold('lint config:')} ${lintResult?.configPath ? chalk.cyan(lintResult.configKind) + ' ' + chalk.dim(lintResult.configPath) : chalk.gray('(none — defaults)')}`);
|
|
2967
3111
|
console.log(`${chalk.bold('profiles:')} ${effective.matchedProfiles.length > 0 ? effective.matchedProfiles.join(', ') : chalk.gray('(none)')}`);
|
|
2968
3112
|
if (effective.excludeFromGate) {
|
|
2969
3113
|
console.log(`${chalk.bold('excludeFromGate:')} ${chalk.yellow('true')}`);
|