code-auditor-mcp 3.4.12 ā 3.4.13
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/CHANGELOG.md +46 -1
- package/dist/analyzers/__verify_self_probe.d.ts +2 -0
- package/dist/analyzers/__verify_self_probe.d.ts.map +1 -0
- package/dist/analyzers/__verify_self_probe.js +5 -0
- package/dist/analyzers/__verify_self_probe.js.map +1 -0
- package/dist/analyzers/analyzerUtils.d.ts +34 -1
- package/dist/analyzers/analyzerUtils.d.ts.map +1 -1
- package/dist/analyzers/analyzerUtils.js +61 -22
- package/dist/analyzers/analyzerUtils.js.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts +18 -23
- package/dist/analyzers/cross-language/APIContractAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/cross-language/APIContractAnalyzer.js +198 -186
- package/dist/analyzers/cross-language/APIContractAnalyzer.js.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts +78 -55
- package/dist/analyzers/cross-language/DependencyGraphBuilder.d.ts.map +1 -1
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js +244 -243
- package/dist/analyzers/cross-language/DependencyGraphBuilder.js.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.d.ts +27 -37
- package/dist/analyzers/cross-language/SchemaValidator.d.ts.map +1 -1
- package/dist/analyzers/cross-language/SchemaValidator.js +314 -275
- package/dist/analyzers/cross-language/SchemaValidator.js.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts +17 -46
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js +620 -523
- package/dist/analyzers/crossDomain/CrossDomainAnalyzer.js.map +1 -1
- package/dist/analyzers/documentationAnalyzer.d.ts +7 -2
- package/dist/analyzers/documentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/documentationAnalyzer.js +259 -198
- package/dist/analyzers/documentationAnalyzer.js.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/invariantsAnalyzer.js +57 -50
- package/dist/analyzers/invariantsAnalyzer.js.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.d.ts +10 -0
- package/dist/analyzers/orm/adapterRegistry.d.ts.map +1 -1
- package/dist/analyzers/orm/adapterRegistry.js +10 -0
- package/dist/analyzers/orm/adapterRegistry.js.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.d.ts +22 -0
- package/dist/analyzers/orm/drizzleAdapter.d.ts.map +1 -1
- package/dist/analyzers/orm/drizzleAdapter.js +42 -44
- package/dist/analyzers/orm/drizzleAdapter.js.map +1 -1
- package/dist/analyzers/orm/prismaAdapter.d.ts +30 -0
- package/dist/analyzers/orm/prismaAdapter.d.ts.map +1 -1
- package/dist/analyzers/orm/prismaAdapter.js +30 -0
- package/dist/analyzers/orm/prismaAdapter.js.map +1 -1
- package/dist/analyzers/provenance.d.ts +62 -2
- package/dist/analyzers/provenance.d.ts.map +1 -1
- package/dist/analyzers/provenance.js +443 -349
- package/dist/analyzers/provenance.js.map +1 -1
- package/dist/analyzers/reactAnalyzer.d.ts +11 -0
- package/dist/analyzers/reactAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/reactAnalyzer.js +188 -125
- package/dist/analyzers/reactAnalyzer.js.map +1 -1
- package/dist/analyzers/ruleRegistry.d.ts +110 -1
- package/dist/analyzers/ruleRegistry.d.ts.map +1 -1
- package/dist/analyzers/ruleRegistry.js +1714 -124
- package/dist/analyzers/ruleRegistry.js.map +1 -1
- package/dist/analyzers/ruleTiming.d.ts +54 -0
- package/dist/analyzers/ruleTiming.d.ts.map +1 -0
- package/dist/analyzers/ruleTiming.js +71 -0
- package/dist/analyzers/ruleTiming.js.map +1 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts +13 -0
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js +362 -283
- package/dist/analyzers/universal/UniversalConventionsAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts +26 -68
- package/dist/analyzers/universal/UniversalDRYAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js +430 -391
- package/dist/analyzers/universal/UniversalDRYAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts +17 -202
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js +1073 -1121
- package/dist/analyzers/universal/UniversalDataAccessAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts +3 -49
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js +414 -340
- package/dist/analyzers/universal/UniversalDocumentationAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts +48 -5
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js +262 -134
- package/dist/analyzers/universal/UniversalSOLIDAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts +23 -214
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js +139 -1521
- package/dist/analyzers/universal/UniversalSchemaAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts +78 -79
- package/dist/analyzers/universal/UniversalStylesAnalyzer.d.ts.map +1 -1
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js +613 -480
- package/dist/analyzers/universal/UniversalStylesAnalyzer.js.map +1 -1
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts +307 -0
- package/dist/analyzers/universal/schema/codeAnalysis.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/codeAnalysis.js +965 -0
- package/dist/analyzers/universal/schema/codeAnalysis.js.map +1 -0
- package/dist/analyzers/universal/schema/config.d.ts +31 -0
- package/dist/analyzers/universal/schema/config.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/config.js +57 -0
- package/dist/analyzers/universal/schema/config.js.map +1 -0
- package/dist/analyzers/universal/schema/discovery.d.ts +167 -0
- package/dist/analyzers/universal/schema/discovery.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/discovery.js +616 -0
- package/dist/analyzers/universal/schema/discovery.js.map +1 -0
- package/dist/analyzers/universal/schema/jsonSchema.d.ts +27 -0
- package/dist/analyzers/universal/schema/jsonSchema.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/jsonSchema.js +368 -0
- package/dist/analyzers/universal/schema/jsonSchema.js.map +1 -0
- package/dist/analyzers/universal/schema/migrations.d.ts +90 -0
- package/dist/analyzers/universal/schema/migrations.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/migrations.js +238 -0
- package/dist/analyzers/universal/schema/migrations.js.map +1 -0
- package/dist/analyzers/universal/schema/types.d.ts +131 -0
- package/dist/analyzers/universal/schema/types.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/types.js +10 -0
- package/dist/analyzers/universal/schema/types.js.map +1 -0
- package/dist/analyzers/universal/schema/violations.d.ts +44 -0
- package/dist/analyzers/universal/schema/violations.d.ts.map +1 -0
- package/dist/analyzers/universal/schema/violations.js +54 -0
- package/dist/analyzers/universal/schema/violations.js.map +1 -0
- package/dist/auditRunner.d.ts.map +1 -1
- package/dist/auditRunner.js +89 -13
- package/dist/auditRunner.js.map +1 -1
- package/dist/cli.js +315 -40
- package/dist/cli.js.map +1 -1
- package/dist/config/configLoader.d.ts.map +1 -1
- package/dist/config/configLoader.js +9 -7
- package/dist/config/configLoader.js.map +1 -1
- package/dist/config/defaults.d.ts +12 -3
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +15 -5
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/effectiveConfig.d.ts +75 -0
- package/dist/config/effectiveConfig.d.ts.map +1 -0
- package/dist/config/effectiveConfig.js +213 -0
- package/dist/config/effectiveConfig.js.map +1 -0
- package/dist/config/pathProfiles.d.ts +7 -4
- package/dist/config/pathProfiles.d.ts.map +1 -1
- package/dist/config/pathProfiles.js +8 -10
- package/dist/config/pathProfiles.js.map +1 -1
- package/dist/config/thresholdRationales.d.ts +45 -0
- package/dist/config/thresholdRationales.d.ts.map +1 -0
- package/dist/config/thresholdRationales.js +67 -0
- package/dist/config/thresholdRationales.js.map +1 -0
- package/dist/conventions/conventionMiner.d.ts.map +1 -1
- package/dist/conventions/conventionMiner.js +6 -2
- package/dist/conventions/conventionMiner.js.map +1 -1
- package/dist/enforcement/diffGate.d.ts +55 -0
- package/dist/enforcement/diffGate.d.ts.map +1 -0
- package/dist/enforcement/diffGate.js +136 -0
- package/dist/enforcement/diffGate.js.map +1 -0
- package/dist/enforcement/gate.d.ts +36 -0
- package/dist/enforcement/gate.d.ts.map +1 -0
- package/dist/enforcement/gate.js +72 -0
- package/dist/enforcement/gate.js.map +1 -0
- package/dist/enforcement/suppressions.d.ts +41 -0
- package/dist/enforcement/suppressions.d.ts.map +1 -0
- package/dist/enforcement/suppressions.js +170 -0
- package/dist/enforcement/suppressions.js.map +1 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +4 -1
- package/dist/fingerprint.js.map +1 -1
- package/dist/invariants/ruleEngine.d.ts +3 -4
- package/dist/invariants/ruleEngine.d.ts.map +1 -1
- package/dist/invariants/ruleEngine.js +2 -2
- package/dist/invariants/ruleEngine.js.map +1 -1
- package/dist/languages/LanguageOrchestrator.d.ts +14 -0
- package/dist/languages/LanguageOrchestrator.d.ts.map +1 -1
- package/dist/languages/LanguageOrchestrator.js +93 -55
- package/dist/languages/LanguageOrchestrator.js.map +1 -1
- package/dist/languages/LanguageRegistry.d.ts +8 -0
- package/dist/languages/LanguageRegistry.d.ts.map +1 -1
- package/dist/languages/LanguageRegistry.js +8 -0
- package/dist/languages/LanguageRegistry.js.map +1 -1
- package/dist/languages/RuntimeManager.d.ts +82 -60
- package/dist/languages/RuntimeManager.d.ts.map +1 -1
- package/dist/languages/RuntimeManager.js +404 -349
- package/dist/languages/RuntimeManager.js.map +1 -1
- package/dist/languages/UniversalAnalyzer.d.ts +51 -6
- package/dist/languages/UniversalAnalyzer.d.ts.map +1 -1
- package/dist/languages/UniversalAnalyzer.js +127 -88
- package/dist/languages/UniversalAnalyzer.js.map +1 -1
- package/dist/languages/adapterBridge.d.ts +36 -0
- package/dist/languages/adapterBridge.d.ts.map +1 -1
- package/dist/languages/adapterBridge.js +89 -35
- package/dist/languages/adapterBridge.js.map +1 -1
- package/dist/languages/go/GoAdapter.d.ts +66 -23
- package/dist/languages/go/GoAdapter.d.ts.map +1 -1
- package/dist/languages/go/GoAdapter.js +553 -474
- package/dist/languages/go/GoAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts +33 -4
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.d.ts.map +1 -1
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js +101 -66
- package/dist/languages/tree-sitter/TreeSitterCssAdapter.js.map +1 -1
- package/dist/languages/tree-sitter/converter.d.ts +18 -0
- package/dist/languages/tree-sitter/converter.d.ts.map +1 -1
- package/dist/languages/tree-sitter/converter.js +18 -0
- package/dist/languages/tree-sitter/converter.js.map +1 -1
- package/dist/languages/tree-sitter/parser.d.ts +39 -2
- package/dist/languages/tree-sitter/parser.d.ts.map +1 -1
- package/dist/languages/tree-sitter/parser.js +93 -12
- package/dist/languages/tree-sitter/parser.js.map +1 -1
- package/dist/languages/types.d.ts +67 -2
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts +253 -72
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.d.ts.map +1 -1
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js +1419 -923
- package/dist/languages/typescript/TreeSitterTypeScriptAdapter.js.map +1 -1
- package/dist/mcp-tools-shared.d.ts.map +1 -1
- package/dist/mcp-tools-shared.js.map +1 -1
- package/dist/pipeline.d.ts +13 -5
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +381 -200
- package/dist/pipeline.js.map +1 -1
- package/dist/pipelineAdapters.d.ts +3 -8
- package/dist/pipelineAdapters.d.ts.map +1 -1
- package/dist/pipelineAdapters.js +149 -94
- package/dist/pipelineAdapters.js.map +1 -1
- package/dist/presets/presets.d.ts +54 -0
- package/dist/presets/presets.d.ts.map +1 -0
- package/dist/presets/presets.js +181 -0
- package/dist/presets/presets.js.map +1 -0
- package/dist/reporting/sarifReportGenerator.d.ts.map +1 -1
- package/dist/reporting/sarifReportGenerator.js +0 -2
- package/dist/reporting/sarifReportGenerator.js.map +1 -1
- package/dist/ruleAliases.d.ts +67 -0
- package/dist/ruleAliases.d.ts.map +1 -0
- package/dist/ruleAliases.js +79 -0
- package/dist/ruleAliases.js.map +1 -0
- package/dist/styles/cssAstExtractor.d.ts.map +1 -1
- package/dist/styles/cssAstExtractor.js +78 -18
- package/dist/styles/cssAstExtractor.js.map +1 -1
- package/dist/styles/styleIndexer.js +10 -1
- package/dist/styles/styleIndexer.js.map +1 -1
- package/dist/styles/tailwindConfigLoader.d.ts +8 -6
- package/dist/styles/tailwindConfigLoader.d.ts.map +1 -1
- package/dist/styles/tailwindConfigLoader.js +61 -15
- package/dist/styles/tailwindConfigLoader.js.map +1 -1
- package/dist/styles/tailwindUtilityExpander.d.ts.map +1 -1
- package/dist/styles/tailwindUtilityExpander.js +13 -1
- package/dist/styles/tailwindUtilityExpander.js.map +1 -1
- package/dist/types.d.ts +181 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -1
- package/package.json +5 -3
- package/plugin/hooks/hooks.json +1 -1
- package/plugin/scripts/hook-audit.sh +25 -7
- package/plugin/skills/code-auditor/SKILL.md +5 -5
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,8 @@ import { queryParser } from './search/QueryParser.js';
|
|
|
20
20
|
import { CodeIndexDB } from './codeIndexDB.js';
|
|
21
21
|
import { getFilesProcessed, getFactsConsumed, isVisitorStatus, isReducerStatus } from './pipeline.js';
|
|
22
22
|
import { createBaselineFromFindings, saveBaseline, loadBaseline, diffBaselines } from './baseline.js';
|
|
23
|
+
import { computeDiffGatingDecision } from './enforcement/gate.js';
|
|
24
|
+
import { computeDiffGate } from './enforcement/diffGate.js';
|
|
23
25
|
// Get package.json for version info
|
|
24
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
25
27
|
const __dirname = dirname(__filename);
|
|
@@ -42,6 +44,7 @@ program
|
|
|
42
44
|
.option('--full', 'Show full violation inventory (overrides default delta view when baseline exists)')
|
|
43
45
|
.option('--include-baseline', 'Evaluate baseline-known violations in --fail-on checks')
|
|
44
46
|
.option('--fail-on-regression', 'Exit code 2 when total advisory debt exceeds the baseline snapshot')
|
|
47
|
+
.option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
45
48
|
.action(async (options) => {
|
|
46
49
|
console.log(chalk.blue('š Code Quality Audit Tool'));
|
|
47
50
|
console.log(chalk.gray('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
@@ -54,10 +57,23 @@ program
|
|
|
54
57
|
console.error(chalk.red(`Invalid --fail-on severity: "${failOnSeverity}". Must be one of: ${validSeverities.join(', ')}`));
|
|
55
58
|
process.exit(1);
|
|
56
59
|
}
|
|
60
|
+
// Validate --preset ids up front (Spec 38 R4). Unknown ids are a hard
|
|
61
|
+
// error, not a silent skip ā same contract as print-config.
|
|
62
|
+
const presetIds = Array.isArray(options.preset) ? options.preset : (options.preset ? [options.preset] : []);
|
|
63
|
+
if (presetIds.length > 0) {
|
|
64
|
+
const { getPreset, PRESET_IDS } = await import('./presets/presets.js');
|
|
65
|
+
for (const id of presetIds) {
|
|
66
|
+
if (!getPreset(id)) {
|
|
67
|
+
console.error(chalk.red(`Error: unknown preset "${id}". Available: ${PRESET_IDS.join(', ')}`));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
57
72
|
const runner = createAuditRunner({
|
|
58
73
|
projectRoot: options.path,
|
|
59
74
|
configName: options.config,
|
|
60
|
-
outputDirectory: options.output
|
|
75
|
+
outputDirectory: options.output,
|
|
76
|
+
presets: presetIds
|
|
61
77
|
});
|
|
62
78
|
const result = await runner.run();
|
|
63
79
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
@@ -134,6 +150,38 @@ program
|
|
|
134
150
|
console.log(`Warnings: ${result.summary.warnings}`);
|
|
135
151
|
console.log(`Suggestions: ${result.summary.suggestions}`);
|
|
136
152
|
}
|
|
153
|
+
// Spec 32 ā unparsed files are never silent. A run where any file failed
|
|
154
|
+
// to parse (WASM abort, read error, ā¦) must surface the count + reasons.
|
|
155
|
+
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
156
|
+
if (unparsedFiles.length > 0) {
|
|
157
|
+
console.error(chalk.red(`\nā ļø ${unparsedFiles.length} file${unparsedFiles.length !== 1 ? 's' : ''} failed to parse:`));
|
|
158
|
+
for (const u of unparsedFiles.slice(0, 20)) {
|
|
159
|
+
console.error(` ${u.filePath} ā ${u.reason}`);
|
|
160
|
+
}
|
|
161
|
+
if (unparsedFiles.length > 20) {
|
|
162
|
+
console.error(` ⦠and ${unparsedFiles.length - 20} more`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Spec 36 R7 ā suppressions are reported: how many exist, where, and how
|
|
166
|
+
// many are unnecessary (or reasonless). Both are errors, surfaced here.
|
|
167
|
+
const suppressions = result.metadata?.suppressions;
|
|
168
|
+
if (suppressions) {
|
|
169
|
+
console.log(chalk.gray(`\nāā Suppressions āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`));
|
|
170
|
+
console.log(` ${suppressions.total} directive${suppressions.total !== 1 ? 's' : ''}, ` +
|
|
171
|
+
`${suppressions.suppressed} finding${suppressions.suppressed !== 1 ? 's' : ''} suppressed`);
|
|
172
|
+
if (suppressions.unnecessary.length > 0) {
|
|
173
|
+
console.error(chalk.red(` ā ${suppressions.unnecessary.length} unnecessary (finding no longer fires):`));
|
|
174
|
+
for (const d of suppressions.unnecessary) {
|
|
175
|
+
console.error(` ${d.rule} @ ${d.file}:${d.line}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (suppressions.reasonless.length > 0) {
|
|
179
|
+
console.error(chalk.red(` ā ${suppressions.reasonless.length} missing a required reason:`));
|
|
180
|
+
for (const d of suppressions.reasonless) {
|
|
181
|
+
console.error(` ${d.rule} @ ${d.file}:${d.line}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
137
185
|
// Per-analyzer activity (read from result data, not serialized summary)
|
|
138
186
|
// ā surfaces zero-scan failures that would otherwise be invisible.
|
|
139
187
|
if (!options.json) {
|
|
@@ -208,6 +256,7 @@ program
|
|
|
208
256
|
const outputDir = options.output || process.cwd();
|
|
209
257
|
const ext = options.format === 'sarif' ? 'sarif' : options.format;
|
|
210
258
|
const reportPath = join(outputDir, `audit-report.${ext}`);
|
|
259
|
+
await fs.mkdir(dirname(reportPath), { recursive: true });
|
|
211
260
|
await fs.writeFile(reportPath, report, 'utf-8');
|
|
212
261
|
console.log(chalk.green(`\nReport written to ${reportPath}`));
|
|
213
262
|
}
|
|
@@ -232,6 +281,16 @@ program
|
|
|
232
281
|
process.exit(2);
|
|
233
282
|
}
|
|
234
283
|
}
|
|
284
|
+
// Spec 32 ā a run where any file failed to parse is incomplete and must
|
|
285
|
+
// not exit 0. This mirrors the zero-files gate: a silent dark-analyzer
|
|
286
|
+
// failure at file granularity is a hard failure.
|
|
287
|
+
{
|
|
288
|
+
const unparsedFiles = result.metadata?.unparsedFiles ?? [];
|
|
289
|
+
if (unparsedFiles.length > 0) {
|
|
290
|
+
console.error(`Parse-failure: ${unparsedFiles.length} file(s) could not be parsed; analysis is incomplete.`);
|
|
291
|
+
process.exit(2);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
235
294
|
// --fail-on: evaluate new + invariant findings only (unless --include-baseline)
|
|
236
295
|
if (failOnSeverity) {
|
|
237
296
|
const evaluableViolations = (baseline && !options.includeBaseline)
|
|
@@ -260,8 +319,6 @@ program
|
|
|
260
319
|
.option('--json', 'Output violations as machine-readable JSON to stdout')
|
|
261
320
|
.option('-f, --format <format>', 'Report format: json, or sarif (overrides --json)')
|
|
262
321
|
.option('--quiet', 'Suppress output when zero violations')
|
|
263
|
-
.option('--fail-on <severity>', 'Exit code 2 when violations at or above this severity exist', 'critical')
|
|
264
|
-
.option('--include-baseline', 'Evaluate baseline-known violations in --fail-on checks')
|
|
265
322
|
.option('--fail-on-zero-files', 'Exit code 2 when any enabled analyzer matches zero source files', true)
|
|
266
323
|
.option('--stdin', 'Read file paths from stdin (one per line)')
|
|
267
324
|
.option('-p, --path <projectPath>', 'Project root path', process.cwd())
|
|
@@ -293,13 +350,6 @@ program
|
|
|
293
350
|
const resolved = [...fileSet].map((f) => isAbsolute(f) ? f : resolve(process.cwd(), f));
|
|
294
351
|
scope = resolved;
|
|
295
352
|
}
|
|
296
|
-
// Validate --fail-on severity
|
|
297
|
-
const validSeverities = ['critical', 'warning', 'suggestion'];
|
|
298
|
-
const failOnSeverity = options.failOn;
|
|
299
|
-
if (!validSeverities.includes(failOnSeverity)) {
|
|
300
|
-
console.error(chalk.red(`Invalid --fail-on severity: "${failOnSeverity}". Must be one of: ${validSeverities.join(', ')}`));
|
|
301
|
-
process.exit(1);
|
|
302
|
-
}
|
|
303
353
|
// Create runner
|
|
304
354
|
const runner = createAuditRunner({
|
|
305
355
|
projectRoot: options.path,
|
|
@@ -309,11 +359,6 @@ program
|
|
|
309
359
|
const result = await runner.run();
|
|
310
360
|
// Collect all violations
|
|
311
361
|
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
312
|
-
// Baseline classification (Spec 18 R4 ā hook path)
|
|
313
|
-
const baseline = result.metadata?.baseline;
|
|
314
|
-
const knownViolations = violations.filter((v) => v.new === false);
|
|
315
|
-
const newViolations = violations.filter((v) => v.new === true || v.new === undefined);
|
|
316
|
-
const knownCount = knownViolations.length;
|
|
317
362
|
// JSON output
|
|
318
363
|
if (options.format === 'sarif') {
|
|
319
364
|
const { generateSARIFReport } = await import('./reporting/sarifReportGenerator.js');
|
|
@@ -353,18 +398,13 @@ program
|
|
|
353
398
|
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
|
|
354
399
|
}
|
|
355
400
|
else if (!options.quiet || violations.length > 0) {
|
|
356
|
-
// Console output
|
|
401
|
+
// Console output ā Spec 36 R3: agent-facing output emits findings,
|
|
402
|
+
// never an aggregate total. The hook and the `changed` command are the
|
|
403
|
+
// agent's surface; a finding total is exactly the representation the
|
|
404
|
+
// consumer routes around. Findings only, each with file:line.
|
|
357
405
|
console.log(chalk.blue('š Diff-Scoped Code Audit'));
|
|
358
406
|
console.log(chalk.gray('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
359
407
|
console.log(chalk.gray(`Files analyzed: ${result.metadata.filesAnalyzed}`));
|
|
360
|
-
console.log(`\nFound ${violations.length} violations`);
|
|
361
|
-
console.log(`Critical: ${result.summary.criticalIssues}`);
|
|
362
|
-
console.log(`Warnings: ${result.summary.warnings}`);
|
|
363
|
-
console.log(`Suggestions: ${result.summary.suggestions}`);
|
|
364
|
-
// Known-finding informational line (Spec 18 R4)
|
|
365
|
-
if (baseline && knownCount > 0) {
|
|
366
|
-
console.log(chalk.gray(`\nā¹ļø ${knownCount} pre-existing finding${knownCount !== 1 ? 's' : ''} in files you touched (not blocking).`));
|
|
367
|
-
}
|
|
368
408
|
if (violations.length > 0) {
|
|
369
409
|
console.log(chalk.gray('\nāā Violations āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
370
410
|
for (const v of violations) {
|
|
@@ -376,19 +416,80 @@ program
|
|
|
376
416
|
console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.severity}] ${v.message}${statusTag}`);
|
|
377
417
|
}
|
|
378
418
|
}
|
|
419
|
+
else {
|
|
420
|
+
console.log(chalk.green('\nā No findings.'));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Spec 38 R2/R3 ā per-rule timing + gate wall-clock, opt-in by env var.
|
|
424
|
+
// Emitted to stderr so it never corrupts --json stdout. Slowest rule first.
|
|
425
|
+
if (process.env.CODE_AUDIT_RULE_TIMING === '1') {
|
|
426
|
+
const ruleTiming = result.metadata?.ruleTiming;
|
|
427
|
+
const gateMs = result.metadata?.auditDuration;
|
|
428
|
+
const lines = [];
|
|
429
|
+
lines.push('');
|
|
430
|
+
lines.push(`ā± gate wall-clock: ${(gateMs ?? 0).toFixed(1)} ms (budget 300 ms)`);
|
|
431
|
+
lines.push('āā per-rule timing (slowest first) āā');
|
|
432
|
+
if (ruleTiming && ruleTiming.length > 0) {
|
|
433
|
+
for (const t of ruleTiming) {
|
|
434
|
+
lines.push(` ${t.ruleId.padEnd(28)} ${t.totalMs.toFixed(2).padStart(9)} ms Ć${t.calls}`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
lines.push(' (no gating rules recorded)');
|
|
439
|
+
}
|
|
440
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
379
441
|
}
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
442
|
+
// Binary gate (Spec 36 R2/R4/R6): exit 2 when a gating finding is present.
|
|
443
|
+
// The `changed` command is the edit-boundary gate, so it compares against
|
|
444
|
+
// the file's prior state (git HEAD) via the touched-line diff gate (R2),
|
|
445
|
+
// NOT against a stored baseline. Gate-excluded files and non-gating rules
|
|
446
|
+
// never block. Severity does NOT factor in (R4). A gating rule that cannot
|
|
447
|
+
// name a next action for an occurrence emits non-blocking and the gap is
|
|
448
|
+
// recorded (R6), surfaced below to stderr.
|
|
449
|
+
{
|
|
450
|
+
const analyzedFiles = result.metadata?.analyzedFiles;
|
|
451
|
+
// The `changed` command is always scoped, so the runner records the
|
|
452
|
+
// analyzed file list. If it is somehow absent we still derive the gate
|
|
453
|
+
// from the files that actually produced findings rather than falling
|
|
454
|
+
// back to the baseline gate ā a silent soft-fail is the one outcome R1
|
|
455
|
+
// forbids.
|
|
456
|
+
const gateFiles = analyzedFiles && analyzedFiles.length > 0
|
|
457
|
+
? analyzedFiles
|
|
458
|
+
: [...new Set(violations.map((v) => v.file).filter(Boolean))];
|
|
459
|
+
const diffGate = computeDiffGate(options.path, gateFiles);
|
|
460
|
+
const { blocking, resolutionGaps } = computeDiffGatingDecision(violations, diffGate);
|
|
461
|
+
if (resolutionGaps.length > 0) {
|
|
462
|
+
// Spec 36 R6 ā a gating rule that could not name an action is a defect
|
|
463
|
+
// in the rule. Record it loudly so it cannot route around as a count.
|
|
464
|
+
const lines = [
|
|
465
|
+
'ā ļø resolution gap ā gating rule produced no next action for these occurrences (Spec 36 R6):',
|
|
466
|
+
...resolutionGaps.map((g) => ` - ${g.rule} @ ${g.file}${g.line ? `:${g.line}` : ''}`),
|
|
467
|
+
];
|
|
468
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
469
|
+
}
|
|
470
|
+
if (blocking.length > 0) {
|
|
471
|
+
process.exit(2);
|
|
472
|
+
}
|
|
473
|
+
// Spec 36 R7 ā an unnecessary or reasonless suppression is itself an
|
|
474
|
+
// error. It is reported loudly and fails the write, exactly like an
|
|
475
|
+
// unused `@ts-expect-error`. A suppression that outlives its finding is
|
|
476
|
+
// a baseline entry with better branding unless it errors here.
|
|
477
|
+
const suppressions = result.metadata?.suppressions;
|
|
478
|
+
if (suppressions && (suppressions.unnecessary.length > 0 || suppressions.reasonless.length > 0)) {
|
|
479
|
+
const lines = [];
|
|
480
|
+
if (suppressions.reasonless.length > 0) {
|
|
481
|
+
lines.push('ā suppression missing a required reason (Spec 36 R7):');
|
|
482
|
+
for (const d of suppressions.reasonless) {
|
|
483
|
+
lines.push(` - ${d.rule} @ ${d.file}:${d.line}`);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if (suppressions.unnecessary.length > 0) {
|
|
487
|
+
lines.push('ā unnecessary suppression ā the finding no longer fires (Spec 36 R7):');
|
|
488
|
+
for (const d of suppressions.unnecessary) {
|
|
489
|
+
lines.push(` - ${d.rule} @ ${d.file}:${d.line}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
process.stderr.write(lines.join('\n') + '\n');
|
|
392
493
|
process.exit(2);
|
|
393
494
|
}
|
|
394
495
|
}
|
|
@@ -819,7 +920,7 @@ configCmd
|
|
|
819
920
|
file: options.file,
|
|
820
921
|
resolvedFilePath: filePath,
|
|
821
922
|
matchedProfileNames: resolved.matchedProfileNames,
|
|
822
|
-
|
|
923
|
+
excludeFromGate: resolved.excludeFromGate || null,
|
|
823
924
|
overrides: resolved.overrides,
|
|
824
925
|
}, null, 2) + '\n');
|
|
825
926
|
}
|
|
@@ -834,8 +935,8 @@ configCmd
|
|
|
834
935
|
const builtinTag = profile?.builtin !== false && profiles.some(p => p.name === name) ? '' : '';
|
|
835
936
|
console.log(` ${chalk.cyan(name)}${builtinTag ? chalk.gray(' (built-in)') : ''}`);
|
|
836
937
|
}
|
|
837
|
-
if (resolved.
|
|
838
|
-
console.log(chalk.yellow(`
|
|
938
|
+
if (resolved.excludeFromGate) {
|
|
939
|
+
console.log(chalk.yellow(` excludeFromGate: true`));
|
|
839
940
|
}
|
|
840
941
|
if (Object.keys(resolved.overrides).length > 0) {
|
|
841
942
|
console.log(chalk.gray(' merged overrides:'));
|
|
@@ -2117,6 +2218,180 @@ coverageCmd
|
|
|
2117
2218
|
process.exit(1);
|
|
2118
2219
|
}
|
|
2119
2220
|
});
|
|
2221
|
+
// print-config command ā effective config with source attribution (Spec 38 R1)
|
|
2222
|
+
program
|
|
2223
|
+
.command('print-config <file>')
|
|
2224
|
+
.description('Print the effective config for a file, naming the source of every value')
|
|
2225
|
+
.option('-p, --path <path>', 'Project path', process.cwd())
|
|
2226
|
+
.option('--json', 'Output as JSON')
|
|
2227
|
+
.option('--preset <id>', 'Apply a shareable preset (repeatable)', (v, prev) => prev.concat([v]), [])
|
|
2228
|
+
.action(async (file, options) => {
|
|
2229
|
+
try {
|
|
2230
|
+
const pathModule = await import('path');
|
|
2231
|
+
const fs = await import('fs/promises');
|
|
2232
|
+
const { loadConfig } = await import('./config/configLoader.js');
|
|
2233
|
+
const { getDefaultConfig, DEFAULT_CODE_INDEX_CONFIG } = await import('./config/defaults.js');
|
|
2234
|
+
const { computeEffectiveConfig, computeTopLevelConfig, } = await import('./config/effectiveConfig.js');
|
|
2235
|
+
const { getPreset, PRESET_IDS } = await import('./presets/presets.js');
|
|
2236
|
+
const projectRoot = pathModule.resolve(options.path);
|
|
2237
|
+
const filePath = pathModule.resolve(file);
|
|
2238
|
+
const configPath = pathModule.join(projectRoot, '.codeauditor.json');
|
|
2239
|
+
// Pristine defaults, normalized against projectRoot the same way loadConfig
|
|
2240
|
+
// normalizes the real config, so path keys compare cleanly instead of
|
|
2241
|
+
// reading as "user-set" because one side is absolute and the other is not.
|
|
2242
|
+
const pristine = { ...getDefaultConfig(), codeIndex: { ...DEFAULT_CODE_INDEX_CONFIG } };
|
|
2243
|
+
if (pristine.outputDirectory) {
|
|
2244
|
+
pristine.outputDirectory = pathModule.resolve(projectRoot, pristine.outputDirectory);
|
|
2245
|
+
}
|
|
2246
|
+
pristine.includePaths = (pristine.includePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
|
|
2247
|
+
pristine.excludePaths = (pristine.excludePaths ?? []).map((p) => pathModule.isAbsolute(p) ? p : pathModule.resolve(projectRoot, p));
|
|
2248
|
+
// Load the project config (defaults when no config file exists).
|
|
2249
|
+
let config;
|
|
2250
|
+
try {
|
|
2251
|
+
await fs.access(configPath);
|
|
2252
|
+
config = await loadConfig({ configPath });
|
|
2253
|
+
}
|
|
2254
|
+
catch {
|
|
2255
|
+
config = pristine;
|
|
2256
|
+
}
|
|
2257
|
+
const analyzerConfigs = (config.analyzerConfigs ?? {});
|
|
2258
|
+
const pathProfiles = (config.pathProfiles ?? []);
|
|
2259
|
+
const enabledAnalyzers = (config.enabledAnalyzers ?? []);
|
|
2260
|
+
// Resolve --preset ids to ordered preset objects (Spec 38 R4). Unknown
|
|
2261
|
+
// ids are a hard error, not a silent skip.
|
|
2262
|
+
const presetIds = Array.isArray(options.preset) ? options.preset : (options.preset ? [options.preset] : []);
|
|
2263
|
+
for (const id of presetIds) {
|
|
2264
|
+
if (!getPreset(id)) {
|
|
2265
|
+
console.error(chalk.red(`Error: unknown preset "${id}". Available: ${PRESET_IDS.join(', ')}`));
|
|
2266
|
+
process.exit(1);
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
const presets = presetIds.map((id) => getPreset(id));
|
|
2270
|
+
const effective = computeEffectiveConfig({
|
|
2271
|
+
filePath,
|
|
2272
|
+
projectRoot,
|
|
2273
|
+
analyzerConfigs,
|
|
2274
|
+
pathProfiles,
|
|
2275
|
+
enabledAnalyzers,
|
|
2276
|
+
presets,
|
|
2277
|
+
});
|
|
2278
|
+
const topLevel = computeTopLevelConfig(pristine, config);
|
|
2279
|
+
if (options.json) {
|
|
2280
|
+
process.stdout.write(JSON.stringify({
|
|
2281
|
+
file: filePath,
|
|
2282
|
+
projectRoot,
|
|
2283
|
+
configPath,
|
|
2284
|
+
presets: presetIds,
|
|
2285
|
+
matchedProfiles: effective.matchedProfiles,
|
|
2286
|
+
excludeFromGate: effective.excludeFromGate ?? null,
|
|
2287
|
+
topLevel,
|
|
2288
|
+
analyzers: effective.analyzers,
|
|
2289
|
+
}, null, 2) + '\n');
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
console.log(chalk.blue('š§ Effective Config'));
|
|
2293
|
+
console.log(chalk.gray('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
2294
|
+
console.log(`${chalk.bold('file:')} ${effective.relativePath}`);
|
|
2295
|
+
console.log(`${chalk.bold('project root:')} ${projectRoot}`);
|
|
2296
|
+
console.log(`${chalk.bold('presets:')} ${presetIds.length > 0 ? presetIds.join(', ') : chalk.gray('(none)')}`);
|
|
2297
|
+
console.log(`${chalk.bold('profiles:')} ${effective.matchedProfiles.length > 0 ? effective.matchedProfiles.join(', ') : chalk.gray('(none)')}`);
|
|
2298
|
+
if (effective.excludeFromGate) {
|
|
2299
|
+
console.log(`${chalk.bold('excludeFromGate:')} ${chalk.yellow('true')}`);
|
|
2300
|
+
}
|
|
2301
|
+
// Top-level keys
|
|
2302
|
+
console.log(chalk.bold('\nāā Top-level āā'));
|
|
2303
|
+
for (const key of topLevel) {
|
|
2304
|
+
const flag = key.differsFromDefault ? chalk.yellow(' [differs from default]') : '';
|
|
2305
|
+
console.log(`${key.key.padEnd(24)} ${chalk.dim(JSON.stringify(key.value))} ${chalk.cyan(key.source)}${flag}`);
|
|
2306
|
+
}
|
|
2307
|
+
// Per-analyzer keys
|
|
2308
|
+
for (const analyzer of effective.analyzers) {
|
|
2309
|
+
console.log(chalk.bold(`\nāā ${analyzer.namespace} āā`));
|
|
2310
|
+
for (const key of analyzer.keys) {
|
|
2311
|
+
const flag = key.differsFromDefault
|
|
2312
|
+
? chalk.yellow(` [differs from default: ${JSON.stringify(key.defaultValue)}]`)
|
|
2313
|
+
: '';
|
|
2314
|
+
console.log(`${key.key.padEnd(32)} ${chalk.dim(JSON.stringify(key.value))} ${chalk.cyan(key.source)}${flag}`);
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
catch (error) {
|
|
2319
|
+
console.error(chalk.red('Error:'), error);
|
|
2320
|
+
process.exit(1);
|
|
2321
|
+
}
|
|
2322
|
+
});
|
|
2323
|
+
// Outstanding command (Spec 36 R8) ā "what is outstanding" is re-derived from
|
|
2324
|
+
// the codebase on every invocation, never read from a stored list. The answer a
|
|
2325
|
+
// compacted agent gets is identical to an uncompacted one because the only input
|
|
2326
|
+
// is the code itself: run the audit, list the findings. No progress file, no
|
|
2327
|
+
// tracked count, no state that can drift from the code.
|
|
2328
|
+
program
|
|
2329
|
+
.command('outstanding')
|
|
2330
|
+
.description('List outstanding findings, re-derived from the codebase (never a stored list)')
|
|
2331
|
+
.option('-p, --path <path>', 'Path to audit', process.cwd())
|
|
2332
|
+
.option('--json', 'Output findings as machine-readable JSON')
|
|
2333
|
+
.action(async (options) => {
|
|
2334
|
+
try {
|
|
2335
|
+
await initParsers();
|
|
2336
|
+
const runner = createAuditRunner({ projectRoot: options.path });
|
|
2337
|
+
const result = await runner.run();
|
|
2338
|
+
const violations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
2339
|
+
// Deterministic order ā same answer every run, independent of analyzer
|
|
2340
|
+
// registration order or any prior agent state. Sort by file, then line,
|
|
2341
|
+
// then rule. This is what makes a compacted and uncompacted agent agree.
|
|
2342
|
+
const sorted = [...violations].sort((a, b) => {
|
|
2343
|
+
const fa = a.file ?? '';
|
|
2344
|
+
const fb = b.file ?? '';
|
|
2345
|
+
if (fa !== fb)
|
|
2346
|
+
return fa < fb ? -1 : 1;
|
|
2347
|
+
const la = a.line ?? 0;
|
|
2348
|
+
const lb = b.line ?? 0;
|
|
2349
|
+
if (la !== lb)
|
|
2350
|
+
return la - lb;
|
|
2351
|
+
const ra = a.rule ?? '';
|
|
2352
|
+
const rb = b.rule ?? '';
|
|
2353
|
+
if (ra !== rb)
|
|
2354
|
+
return ra < rb ? -1 : 1;
|
|
2355
|
+
return 0;
|
|
2356
|
+
});
|
|
2357
|
+
if (options.json) {
|
|
2358
|
+
const projectDir = resolve(options.path || process.cwd());
|
|
2359
|
+
const out = sorted.map((v) => {
|
|
2360
|
+
let filePath = v.file || '';
|
|
2361
|
+
if (filePath.startsWith('/')) {
|
|
2362
|
+
const rel = relative(projectDir, filePath);
|
|
2363
|
+
if (!rel.startsWith('..') && !isAbsolute(rel))
|
|
2364
|
+
filePath = rel;
|
|
2365
|
+
}
|
|
2366
|
+
return {
|
|
2367
|
+
rule: v.rule || v.type || '',
|
|
2368
|
+
analyzer: v.analyzer || '',
|
|
2369
|
+
severity: v.severity,
|
|
2370
|
+
file: filePath,
|
|
2371
|
+
line: v.line ?? v.start?.line,
|
|
2372
|
+
message: v.message,
|
|
2373
|
+
resolution: v.resolution ?? null,
|
|
2374
|
+
suppressed: v.suppressed === true,
|
|
2375
|
+
...(v.suppressionReason && { suppressionReason: v.suppressionReason }),
|
|
2376
|
+
};
|
|
2377
|
+
});
|
|
2378
|
+
process.stdout.write(JSON.stringify(out, null, 2) + '\n');
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
2381
|
+
// Spec 36 R3 ā findings, never a total. Each line is a file:line finding;
|
|
2382
|
+
// there is no count anywhere in this output to route around.
|
|
2383
|
+
for (const v of sorted) {
|
|
2384
|
+
const icon = v.severity === 'critical' ? 'š“' :
|
|
2385
|
+
v.severity === 'warning' ? 'š”' : 'šµ';
|
|
2386
|
+
const suppressed = v.suppressed ? chalk.dim(' [suppressed]') : '';
|
|
2387
|
+
console.log(`${icon} ${chalk.bold(v.file)}${v.line ? `:${v.line}` : ''} [${v.rule || v.severity}] ${v.message}${suppressed}`);
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
catch (error) {
|
|
2391
|
+
console.error(chalk.red('Error:'), error);
|
|
2392
|
+
process.exit(1);
|
|
2393
|
+
}
|
|
2394
|
+
});
|
|
2120
2395
|
/**
|
|
2121
2396
|
* Render a simple ASCII bar chart.
|
|
2122
2397
|
*/
|
|
@@ -2280,7 +2555,7 @@ async function generateConfigurations(options) {
|
|
|
2280
2555
|
console.log(chalk.blue('\nNext steps:'));
|
|
2281
2556
|
console.log(chalk.gray(' 1. Edit .codeauditor.json to match your codebase conventions'));
|
|
2282
2557
|
console.log(chalk.gray(' 2. Run ') + chalk.cyan('code-audit') + chalk.gray(' to enforce your rules'));
|
|
2283
|
-
console.log(chalk.gray(' 3. Use ') + chalk.cyan('code-audit changed
|
|
2558
|
+
console.log(chalk.gray(' 3. Use ') + chalk.cyan('code-audit changed') + chalk.gray(' in your agent hook (gating rules block on new findings)'));
|
|
2284
2559
|
}
|
|
2285
2560
|
/**
|
|
2286
2561
|
* Interactive rule builder ā walks the user through adding rules one at a time.
|