code-auditor-mcp 4.0.4 → 4.1.1
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 +132 -0
- package/README.md +5 -5
- package/configs/hhra-compat.json +2 -2
- package/dist/analyzers/applicability.d.ts +25 -2
- package/dist/analyzers/applicability.d.ts.map +1 -1
- package/dist/analyzers/applicability.js +44 -50
- package/dist/analyzers/applicability.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +18 -49
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +22 -240
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +2 -14
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +0 -104
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/orgFilterTiers.d.ts +89 -0
- package/dist/analyzers/orgFilterTiers.d.ts.map +1 -0
- package/dist/analyzers/orgFilterTiers.js +96 -0
- package/dist/analyzers/orgFilterTiers.js.map +1 -0
- package/dist/analyzers/ruleRegistry.d.ts +15 -0
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +139 -185
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +1 -12
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +1 -66
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +39 -0
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +62 -41
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +2 -2
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +2 -2
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts +86 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js +606 -0
- package/dist/analyzers/universal/UniversalSecurityAnalyzer.js.map +1 -0
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +14 -0
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.js +35 -18
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -1
- package/dist/analyzers/universal/schema/migrations.d.ts +14 -1
- package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -1
- package/dist/analyzers/universal/schema/migrations.js +58 -14
- package/dist/analyzers/universal/schema/migrations.js.map +1 -1
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +71 -33
- package/dist/auditRunner.js.map +1 -1
- package/dist/churn/churnExtractor.js +5 -5
- package/dist/churn/churnExtractor.js.map +1 -1
- package/dist/cli.js +75 -26
- package/dist/cli.js.map +1 -1
- package/dist/codeIndexDB.d.ts +7 -9
- package/dist/codeIndexDB.d.ts.map +1 -1
- package/dist/codeIndexDB.js +33 -21
- package/dist/codeIndexDB.js.map +1 -1
- package/dist/config/configGeneratePath.d.ts +19 -0
- package/dist/config/configGeneratePath.d.ts.map +1 -0
- package/dist/config/configGeneratePath.js +29 -0
- package/dist/config/configGeneratePath.js.map +1 -0
- package/dist/config/configLoader.d.ts +36 -4
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +141 -16
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +5 -6
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/lintConfigReader.d.ts +4 -0
- package/dist/config/lintConfigReader.d.ts.map +1 -1
- package/dist/config/lintConfigReader.js +35 -36
- package/dist/config/lintConfigReader.js.map +1 -1
- package/dist/config/staticObjectExtract.d.ts +35 -0
- package/dist/config/staticObjectExtract.d.ts.map +1 -0
- package/dist/config/staticObjectExtract.js +312 -0
- package/dist/config/staticObjectExtract.js.map +1 -0
- package/dist/constants.d.ts +1 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -7
- package/dist/constants.js.map +1 -1
- package/dist/daemon/core.js +1 -1
- package/dist/daemon/core.js.map +1 -1
- package/dist/daemon/main.js +1 -1
- package/dist/daemon/main.js.map +1 -1
- package/dist/daemon/resolve.d.ts +4 -4
- package/dist/daemon/resolve.js +4 -4
- package/dist/dataPaths.d.ts +10 -0
- package/dist/dataPaths.d.ts.map +1 -1
- package/dist/dataPaths.js +23 -2
- package/dist/dataPaths.js.map +1 -1
- package/dist/generators/BaseConfigGenerator.d.ts +0 -4
- package/dist/generators/BaseConfigGenerator.d.ts.map +1 -1
- package/dist/generators/BaseConfigGenerator.js +1 -7
- package/dist/generators/BaseConfigGenerator.js.map +1 -1
- package/dist/installConfig.d.ts +3 -1
- package/dist/installConfig.d.ts.map +1 -1
- package/dist/installConfig.js +15 -2
- package/dist/installConfig.js.map +1 -1
- package/dist/installer.d.ts +10 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +14 -2
- package/dist/installer.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +6 -6
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/RuntimeManager.js +13 -10
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js +7 -2
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/mcp-ui-simple.d.ts +5 -1
- package/dist/mcp-ui-simple.d.ts.map +1 -1
- package/dist/mcp-ui-simple.js +81 -29
- package/dist/mcp-ui-simple.js.map +1 -1
- package/dist/mcp.js +25 -6
- package/dist/mcp.js.map +1 -1
- package/dist/mcpAuditJobs.d.ts.map +1 -1
- package/dist/mcpAuditJobs.js +1 -0
- package/dist/mcpAuditJobs.js.map +1 -1
- package/dist/nextFileIncremental.js +1 -1
- package/dist/nextFileIncremental.js.map +1 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +13 -2
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +16 -1
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +142 -11
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/reporting/htmlReportGenerator.js +2 -2
- package/dist/reporting/htmlReportGenerator.js.map +1 -1
- package/dist/ruleAliases.d.ts.map +1 -1
- package/dist/ruleAliases.js +45 -0
- package/dist/ruleAliases.js.map +1 -1
- package/dist/styles/tailwindConfigLoader.d.ts +4 -0
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
- package/dist/styles/tailwindConfigLoader.js +54 -36
- package/dist/styles/tailwindConfigLoader.js.map +1 -1
- package/dist/types.d.ts +60 -23
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/examples/.auditrc.example.json +2 -2
- package/examples/.auditrc.json +3 -3
- package/examples/nextjs.auditrc.json +1 -1
- package/examples/node-api.auditrc.json +1 -1
- package/examples/react.auditrc.json +2 -2
- package/examples/tolerant-solid.auditrc.json +1 -1
- package/package.json +5 -4
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/scripts/hook-common.sh +27 -27
- package/plugin/skills/code-auditor/SKILL.md +1 -1
- package/server.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -25,7 +25,7 @@ import { resolveTelemetryConfig, signatureForFinding, buildTelemetryPayload, for
|
|
|
25
25
|
import { getInstallId } from './installConfig.js';
|
|
26
26
|
import { ALL_ANALYZERS } from './analyzers/ruleRegistry.js';
|
|
27
27
|
import { computeGatingDecision } from './enforcement/gate.js';
|
|
28
|
-
import { BLOCKING_SEVERITIES } from './types.js';
|
|
28
|
+
import { BLOCKING_SEVERITIES, SEVERITIES } from './types.js';
|
|
29
29
|
import { rankFilesByPriority, orderFindingsWithinFile } from './nextFile.js';
|
|
30
30
|
import { runNextFile } from './nextFileIncremental.js';
|
|
31
31
|
import { describeSqliteBackend } from './sqlite/driver.js';
|
|
@@ -96,6 +96,7 @@ program
|
|
|
96
96
|
.option('-c, --config <config>', 'Configuration name')
|
|
97
97
|
.option('-o, --output <dir>', 'Output directory for reports')
|
|
98
98
|
.option('-f, --format <format>', 'Report format: html, json, csv, or sarif')
|
|
99
|
+
.option('--overwrite', 'Overwrite an existing report file at --output (default: refuse)')
|
|
99
100
|
.option('--fail-on <severity>', 'Exit code 2 when violations at or above this severity exist')
|
|
100
101
|
.option('--full', 'Show full violation inventory (overrides default delta view when baseline exists)')
|
|
101
102
|
.option('--include-baseline', 'Evaluate baseline-known violations in --fail-on checks')
|
|
@@ -118,7 +119,7 @@ program
|
|
|
118
119
|
try {
|
|
119
120
|
await initParsers();
|
|
120
121
|
// Validate --fail-on severity
|
|
121
|
-
const validSeverities =
|
|
122
|
+
const validSeverities = SEVERITIES;
|
|
122
123
|
const failOnSeverity = options.failOn;
|
|
123
124
|
if (failOnSeverity && !validSeverities.includes(failOnSeverity)) {
|
|
124
125
|
const renamed = LEGACY_SEVERITY_NAMES[failOnSeverity];
|
|
@@ -194,12 +195,24 @@ program
|
|
|
194
195
|
console.log(chalk.yellow(` ${name} [${d.kind}] — ${d.message}`));
|
|
195
196
|
}
|
|
196
197
|
}
|
|
198
|
+
// ── Ignored config keys (Spec 61 R1.4) ─────────────────────────
|
|
199
|
+
// Keys dropped by sanitizeProjectFileConfig: undeclared keys (like
|
|
200
|
+
// `scope`, which previously reached execSync via mergeConfig's raw-key
|
|
201
|
+
// iteration) and path keys resolving outside the project root. Surfaced
|
|
202
|
+
// so a silently-ignored config never reads as "loaded fine".
|
|
203
|
+
const ignoredConfigKeys = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'config-key-rejected');
|
|
204
|
+
if (ignoredConfigKeys.length > 0) {
|
|
205
|
+
console.log(chalk.yellow(`── Ignored config keys ── ${ignoredConfigKeys.length}`));
|
|
206
|
+
for (const d of ignoredConfigKeys) {
|
|
207
|
+
console.log(chalk.yellow(` ${d.message}`));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
197
210
|
// ── Coverage gaps (Spec 58 follow-up) ─────────────────────────
|
|
198
211
|
// Coverage diagnostics are the analyzer saying "my visibility ends here",
|
|
199
212
|
// not "the code is wrong". They lead with counts, then per-occurrence
|
|
200
213
|
// file:line — visible and counted, never blocking (they never reach the
|
|
201
214
|
// gate). Rendered alongside the coverage panel that leads the report.
|
|
202
|
-
const coverageDiagnostics = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found');
|
|
215
|
+
const coverageDiagnostics = (result.metadata?.diagnostics ?? []).filter((d) => d.kind === 'unresolved-query' || d.kind === 'unresolved-dynamic-import' || d.kind === 'undefined-class-not-found' || d.kind === 'cannot-fire');
|
|
203
216
|
if (coverageDiagnostics.length > 0) {
|
|
204
217
|
const byKind = {};
|
|
205
218
|
for (const d of coverageDiagnostics)
|
|
@@ -444,12 +457,33 @@ program
|
|
|
444
457
|
? { rootDir, ...readVersionControlProvenance(rootDir) }
|
|
445
458
|
: { rootDir };
|
|
446
459
|
const report = generateReport(result, options.format, config);
|
|
447
|
-
const outputDir = options.output || process.cwd();
|
|
448
460
|
const ext = options.format === 'sarif' ? 'sarif' : options.format;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
461
|
+
if (options.output) {
|
|
462
|
+
// Explicit output path — write a file. Refuse to clobber an existing
|
|
463
|
+
// report unless --overwrite is passed. A report write is destructive;
|
|
464
|
+
// a silent overwrite of `audit-report.json` in a consuming repo is the
|
|
465
|
+
// defect this guard removes.
|
|
466
|
+
const outputDir = resolve(options.output);
|
|
467
|
+
const reportPath = join(outputDir, `audit-report.${ext}`);
|
|
468
|
+
let exists = false;
|
|
469
|
+
try {
|
|
470
|
+
await fs.access(reportPath);
|
|
471
|
+
exists = true;
|
|
472
|
+
}
|
|
473
|
+
catch { /* ok */ }
|
|
474
|
+
if (exists && !options.overwrite) {
|
|
475
|
+
console.error(chalk.red(`Refusing to overwrite existing report ${reportPath} — pass --overwrite to replace it.`));
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
await fs.mkdir(dirname(reportPath), { recursive: true });
|
|
479
|
+
await fs.writeFile(reportPath, report, 'utf-8');
|
|
480
|
+
console.log(chalk.green(`\nReport written to ${reportPath}`));
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
// No output path — default to stdout. Never write a report into the
|
|
484
|
+
// audited project without an explicit --output on the command line.
|
|
485
|
+
process.stdout.write(report + '\n');
|
|
486
|
+
}
|
|
453
487
|
}
|
|
454
488
|
// ── Fail-on logic (Spec 18 R3) ───────────────────────────────
|
|
455
489
|
// --fail-on-regression: compare total high debt to baseline snapshot
|
|
@@ -487,7 +521,7 @@ program
|
|
|
487
521
|
const evaluableViolations = (baseline && !options.includeBaseline)
|
|
488
522
|
? violations.filter((v) => v.new || v.analyzer === 'invariants')
|
|
489
523
|
: violations;
|
|
490
|
-
const severityOrder =
|
|
524
|
+
const severityOrder = SEVERITIES;
|
|
491
525
|
const failIndex = severityOrder.indexOf(failOnSeverity);
|
|
492
526
|
const hasAtOrAbove = evaluableViolations.some((v) => {
|
|
493
527
|
// Spec 57 — a dismissed finding never blocks the gate.
|
|
@@ -821,7 +855,7 @@ program
|
|
|
821
855
|
.action(async (paths, options) => {
|
|
822
856
|
try {
|
|
823
857
|
await initParsers();
|
|
824
|
-
const validSeverities =
|
|
858
|
+
const validSeverities = SEVERITIES;
|
|
825
859
|
const failOnSeverity = options.failOn;
|
|
826
860
|
if (!validSeverities.includes(failOnSeverity)) {
|
|
827
861
|
const renamed = LEGACY_SEVERITY_NAMES[failOnSeverity];
|
|
@@ -859,7 +893,7 @@ program
|
|
|
859
893
|
});
|
|
860
894
|
const result = await runner.run();
|
|
861
895
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
862
|
-
const severityOrder =
|
|
896
|
+
const severityOrder = SEVERITIES;
|
|
863
897
|
const failIndex = severityOrder.indexOf(failOnSeverity);
|
|
864
898
|
const blocking = violations.filter((v) => {
|
|
865
899
|
if (!isSelfAuditInScope(v.file ?? ''))
|
|
@@ -1186,8 +1220,9 @@ program
|
|
|
1186
1220
|
.description('Generate a .codeauditor.json scaffold with invariant rules')
|
|
1187
1221
|
.option('-o, --output <dir>', 'Output directory', '.')
|
|
1188
1222
|
.option('-i, --interactive', 'Interactive rule builder')
|
|
1189
|
-
.option('
|
|
1190
|
-
.option('-
|
|
1223
|
+
.option('--overwrite', 'Overwrite an existing .codeauditor.json without confirmation')
|
|
1224
|
+
.option('-f, --force', 'Alias for --overwrite')
|
|
1225
|
+
.option('-y, --yes', 'Alias for --overwrite')
|
|
1191
1226
|
.action(async (options) => {
|
|
1192
1227
|
console.log(chalk.blue('🛠️ Code Auditor Config Generator'));
|
|
1193
1228
|
console.log(chalk.gray('════════════════════════════════════════════════════'));
|
|
@@ -1492,7 +1527,7 @@ configCmd
|
|
|
1492
1527
|
let profiles = [];
|
|
1493
1528
|
try {
|
|
1494
1529
|
await fs.access(configPath);
|
|
1495
|
-
const config = await loadConfig({ configPath });
|
|
1530
|
+
const { config } = await loadConfig({ configPath, projectRoot });
|
|
1496
1531
|
profiles = config.pathProfiles || [];
|
|
1497
1532
|
}
|
|
1498
1533
|
catch {
|
|
@@ -1600,7 +1635,7 @@ configCmd
|
|
|
1600
1635
|
try {
|
|
1601
1636
|
const configPath = pathModule.join(projectRoot, '.codeauditor.json');
|
|
1602
1637
|
await fsPromises.access(configPath);
|
|
1603
|
-
config = await loadConfig({ configPath });
|
|
1638
|
+
config = (await loadConfig({ configPath, projectRoot })).config;
|
|
1604
1639
|
}
|
|
1605
1640
|
catch {
|
|
1606
1641
|
// No config — use defaults
|
|
@@ -1783,6 +1818,7 @@ program
|
|
|
1783
1818
|
.option('--no-unused-imports', 'Hide unused import warnings')
|
|
1784
1819
|
.option('--min-complexity <threshold>', 'Minimum complexity threshold for warnings', '7')
|
|
1785
1820
|
.option('-o, --output <file>', 'Save output to file instead of displaying')
|
|
1821
|
+
.option('--overwrite', 'Overwrite an existing output file (default: refuse)')
|
|
1786
1822
|
.action(async (options) => {
|
|
1787
1823
|
console.log(chalk.blue('🗺️ Codebase Map Generator'));
|
|
1788
1824
|
console.log(chalk.gray('════════════════════════════════════════════════════'));
|
|
@@ -3143,16 +3179,13 @@ program
|
|
|
3143
3179
|
// normalizes the real config, so path keys compare cleanly instead of
|
|
3144
3180
|
// reading as "user-set" because one side is absolute and the other is not.
|
|
3145
3181
|
const pristine = { ...getDefaultConfig(), codeIndex: { ...DEFAULT_CODE_INDEX_CONFIG } };
|
|
3146
|
-
if (pristine.outputDirectory) {
|
|
3147
|
-
pristine.outputDirectory = pathModule.resolve(projectRoot, pristine.outputDirectory);
|
|
3148
|
-
}
|
|
3149
3182
|
pristine.includePaths = (pristine.includePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
|
|
3150
3183
|
pristine.excludePaths = (pristine.excludePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
|
|
3151
3184
|
// Load the project config (defaults when no config file exists).
|
|
3152
3185
|
let config;
|
|
3153
3186
|
try {
|
|
3154
3187
|
await fs.access(configPath);
|
|
3155
|
-
config = await loadConfig({ configPath });
|
|
3188
|
+
config = (await loadConfig({ configPath, projectRoot })).config;
|
|
3156
3189
|
}
|
|
3157
3190
|
catch {
|
|
3158
3191
|
config = pristine;
|
|
@@ -3522,6 +3555,9 @@ const SCAFFOLD_CONFIG = {
|
|
|
3522
3555
|
async function generateConfigurations(options) {
|
|
3523
3556
|
const outputDir = resolve(options.output || '.');
|
|
3524
3557
|
const outputPath = join(outputDir, '.codeauditor.json');
|
|
3558
|
+
// One consent spelling across the CLI: `--overwrite`. `--force`/`--yes` are
|
|
3559
|
+
// retained as aliases for back-compat.
|
|
3560
|
+
const overwrite = Boolean(options.overwrite || options.force || options.yes);
|
|
3525
3561
|
let config;
|
|
3526
3562
|
if (options.interactive) {
|
|
3527
3563
|
// --- Interactive rule builder ---
|
|
@@ -3541,7 +3577,7 @@ async function generateConfigurations(options) {
|
|
|
3541
3577
|
config = await buildRulesInteractively();
|
|
3542
3578
|
}
|
|
3543
3579
|
// Confirm output directory
|
|
3544
|
-
if (!
|
|
3580
|
+
if (!overwrite) {
|
|
3545
3581
|
const { dir } = await inquirer.prompt({
|
|
3546
3582
|
dir: {
|
|
3547
3583
|
type: 'input',
|
|
@@ -3586,12 +3622,12 @@ async function generateConfigurations(options) {
|
|
|
3586
3622
|
exists = true;
|
|
3587
3623
|
}
|
|
3588
3624
|
catch { /* ok */ }
|
|
3589
|
-
if (exists && !
|
|
3625
|
+
if (exists && !overwrite) {
|
|
3590
3626
|
console.log(chalk.yellow(`.codeauditor.json already exists at ${outputPath}`));
|
|
3591
|
-
console.log(chalk.gray('Use --
|
|
3627
|
+
console.log(chalk.gray('Use --overwrite to replace it, or --interactive to build a custom config.'));
|
|
3592
3628
|
return;
|
|
3593
3629
|
}
|
|
3594
|
-
if (exists &&
|
|
3630
|
+
if (exists && overwrite) {
|
|
3595
3631
|
console.log(chalk.yellow('Overwriting existing .codeauditor.json...'));
|
|
3596
3632
|
}
|
|
3597
3633
|
config = SCAFFOLD_CONFIG;
|
|
@@ -3865,9 +3901,22 @@ async function generateCodeMap(options) {
|
|
|
3865
3901
|
const textOutput = mapGenerator.formatAsText(completeCodeMap, mapOptions);
|
|
3866
3902
|
// Output results
|
|
3867
3903
|
if (options.output) {
|
|
3868
|
-
// Save to file
|
|
3869
|
-
|
|
3870
|
-
|
|
3904
|
+
// Save to file. Refuse to clobber an existing file unless --overwrite is
|
|
3905
|
+
// passed — same guard as the report writer, so `map -o` cannot silently
|
|
3906
|
+
// destroy a file the developer did not consent to replacing.
|
|
3907
|
+
const outPath = resolve(options.output);
|
|
3908
|
+
let exists = false;
|
|
3909
|
+
try {
|
|
3910
|
+
await fs.access(outPath);
|
|
3911
|
+
exists = true;
|
|
3912
|
+
}
|
|
3913
|
+
catch { /* ok */ }
|
|
3914
|
+
if (exists && !options.overwrite) {
|
|
3915
|
+
console.error(chalk.red(`Refusing to overwrite existing file ${outPath} — pass --overwrite to replace it.`));
|
|
3916
|
+
process.exit(1);
|
|
3917
|
+
}
|
|
3918
|
+
await fs.writeFile(outPath, textOutput);
|
|
3919
|
+
console.log(chalk.green(`✓ Code map saved to: ${outPath}`));
|
|
3871
3920
|
// Show summary
|
|
3872
3921
|
console.log('');
|
|
3873
3922
|
console.log(chalk.blue('Summary:'));
|