@nerviq/cli 1.29.0 → 1.29.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/CHANGELOG.md +1527 -1493
- package/README.md +550 -538
- package/SECURITY.md +82 -82
- package/bin/cli.js +2562 -2558
- package/docs/api-reference.md +356 -356
- package/docs/audit-fix.md +109 -0
- package/docs/autofix.md +3 -62
- package/docs/getting-started.md +1 -1
- package/docs/index.html +592 -592
- package/docs/integration-contracts.md +287 -287
- package/docs/maintenance.md +128 -128
- package/docs/new-platform-guide.md +202 -202
- package/docs/release-process.md +63 -0
- package/docs/shallow-risk.md +244 -244
- package/docs/why-nerviq.md +82 -82
- package/package.json +67 -67
- package/src/aider/activity.js +226 -226
- package/src/aider/context.js +162 -162
- package/src/aider/freshness.js +123 -123
- package/src/aider/techniques.js +3465 -3465
- package/src/audit/layers.js +180 -180
- package/src/audit.js +1032 -1032
- package/src/benchmark.js +299 -299
- package/src/codex/activity.js +324 -324
- package/src/codex/freshness.js +142 -142
- package/src/codex/techniques.js +4895 -4895
- package/src/context.js +326 -326
- package/src/continuous-ops.js +11 -1
- package/src/convert.js +340 -340
- package/src/copilot/config-parser.js +280 -280
- package/src/copilot/context.js +218 -218
- package/src/copilot/freshness.js +177 -177
- package/src/copilot/patch.js +238 -238
- package/src/copilot/techniques.js +3578 -3578
- package/src/cursor/freshness.js +194 -194
- package/src/cursor/patch.js +243 -243
- package/src/cursor/techniques.js +3735 -3735
- package/src/doctor.js +201 -201
- package/src/fix-engine.js +511 -8
- package/src/formatters/csv.js +86 -86
- package/src/formatters/junit.js +123 -123
- package/src/formatters/markdown.js +164 -164
- package/src/formatters/otel.js +151 -151
- package/src/freshness.js +156 -156
- package/src/gemini/activity.js +402 -402
- package/src/gemini/context.js +290 -290
- package/src/gemini/freshness.js +183 -183
- package/src/gemini/patch.js +229 -229
- package/src/gemini/techniques.js +3811 -3811
- package/src/governance.js +533 -533
- package/src/harmony/audit.js +306 -306
- package/src/i18n.js +63 -63
- package/src/insights.js +119 -119
- package/src/integrations.js +134 -134
- package/src/locales/en.json +33 -33
- package/src/locales/es.json +33 -33
- package/src/migrate.js +354 -354
- package/src/opencode/activity.js +286 -286
- package/src/opencode/freshness.js +137 -137
- package/src/opencode/techniques.js +3450 -3450
- package/src/setup/analysis.js +12 -12
- package/src/setup.js +7 -6
- package/src/shallow-risk/index.js +56 -56
- package/src/shallow-risk/patterns/agent-config-cross-platform-drift.js +50 -50
- package/src/shallow-risk/patterns/agent-config-dangerous-autoapprove.js +46 -46
- package/src/shallow-risk/patterns/agent-config-deprecated-keys.js +46 -46
- package/src/shallow-risk/patterns/agent-config-missing-file.js +317 -317
- package/src/shallow-risk/patterns/agent-config-secret-literal.js +49 -49
- package/src/shallow-risk/patterns/agent-config-stack-contradiction.js +34 -34
- package/src/shallow-risk/patterns/hook-script-missing.js +70 -70
- package/src/shallow-risk/patterns/mcp-server-no-allowlist.js +52 -52
- package/src/shallow-risk/shared.js +648 -648
- package/src/source-urls.js +295 -295
- package/src/state-paths.js +85 -85
- package/src/supplemental-checks.js +805 -805
- package/src/telemetry.js +160 -160
- package/src/windsurf/context.js +359 -359
- package/src/windsurf/freshness.js +194 -194
- package/src/windsurf/patch.js +231 -231
- package/src/windsurf/techniques.js +3779 -3779
package/src/setup/analysis.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
3
|
// ============================================================
|
|
4
4
|
// Helper: detect project scripts from package.json
|
|
5
5
|
// ============================================================
|
|
@@ -607,13 +607,13 @@ function getFrameworkInstructions(stacks) {
|
|
|
607
607
|
return sections.join('\n\n');
|
|
608
608
|
}
|
|
609
609
|
|
|
610
|
-
|
|
611
|
-
module.exports = {
|
|
612
|
-
detectDependencies,
|
|
613
|
-
detectMainDirs,
|
|
614
|
-
detectProjectMetadata,
|
|
615
|
-
detectScripts,
|
|
616
|
-
generateMermaid,
|
|
617
|
-
getFrameworkInstructions,
|
|
618
|
-
};
|
|
619
|
-
|
|
610
|
+
|
|
611
|
+
module.exports = {
|
|
612
|
+
detectDependencies,
|
|
613
|
+
detectMainDirs,
|
|
614
|
+
detectProjectMetadata,
|
|
615
|
+
detectScripts,
|
|
616
|
+
generateMermaid,
|
|
617
|
+
getFrameworkInstructions,
|
|
618
|
+
};
|
|
619
|
+
|
package/src/setup.js
CHANGED
|
@@ -577,7 +577,7 @@ async function setup(options) {
|
|
|
577
577
|
const mcpPreflightWarnings = getMcpPackPreflight(options.mcpPacks || [])
|
|
578
578
|
.filter(item => item.missingEnvVars.length > 0);
|
|
579
579
|
|
|
580
|
-
const settingsSnapshotBefore = snapshotSettingsBeforeSetup(options.dir);
|
|
580
|
+
const settingsSnapshotBefore = snapshotSettingsBeforeSetup(options.dir);
|
|
581
581
|
|
|
582
582
|
|
|
583
583
|
function log(message = '') {
|
|
@@ -617,12 +617,13 @@ async function setup(options) {
|
|
|
617
617
|
writtenFiles = settingsMerge.writtenFiles;
|
|
618
618
|
preservedFiles = settingsMerge.preservedFiles;
|
|
619
619
|
log('');
|
|
620
|
-
|
|
620
|
+
const totalWritten = writtenFiles.length;
|
|
621
|
+
if (totalWritten === 0 && skipped > 0) {
|
|
621
622
|
log(` \x1b[32m${icon('ok')}\x1b[0m Your project is already well configured!`);
|
|
622
623
|
log(` \x1b[2m ${skipped} files already exist and were preserved.\x1b[0m`);
|
|
623
624
|
log(' \x1b[2m We never overwrite your existing config — your setup is kept.\x1b[0m');
|
|
624
|
-
} else if (
|
|
625
|
-
log(` \x1b[1m${
|
|
625
|
+
} else if (totalWritten > 0) {
|
|
626
|
+
log(` \x1b[1m${totalWritten} files written:\x1b[0m`);
|
|
626
627
|
for (const f of writtenFiles) {
|
|
627
628
|
log(` \x1b[32m + ${f}\x1b[0m`);
|
|
628
629
|
}
|
|
@@ -679,5 +680,5 @@ async function setup(options) {
|
|
|
679
680
|
}
|
|
680
681
|
|
|
681
682
|
module.exports = { setup, TEMPLATES };
|
|
682
|
-
|
|
683
|
-
|
|
683
|
+
|
|
684
|
+
|
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { buildFinding, SHALLOW_RISK_BANNER, SHALLOW_RISK_BANNER_LINES } = require('./shared');
|
|
4
|
-
|
|
5
|
-
const patterns = [
|
|
6
|
-
require('./patterns/agent-config-missing-file'),
|
|
7
|
-
require('./patterns/agent-config-stack-contradiction'),
|
|
8
|
-
require('./patterns/agent-config-cross-platform-drift'),
|
|
9
|
-
require('./patterns/mcp-server-no-allowlist'),
|
|
10
|
-
require('./patterns/hook-script-missing'),
|
|
11
|
-
require('./patterns/agent-config-secret-literal'),
|
|
12
|
-
require('./patterns/agent-config-deprecated-keys'),
|
|
13
|
-
require('./patterns/agent-config-dangerous-autoapprove'),
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
function runShallowRisk(ctx) {
|
|
17
|
-
if (!ctx || process.env.NERVIQ_SHALLOW_RISK === 'off') {
|
|
18
|
-
return [];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const findings = [];
|
|
22
|
-
const seen = new Set();
|
|
23
|
-
|
|
24
|
-
for (const pattern of patterns) {
|
|
25
|
-
let emitted = [];
|
|
26
|
-
try {
|
|
27
|
-
const next = pattern.run(ctx);
|
|
28
|
-
emitted = Array.isArray(next) ? next : [];
|
|
29
|
-
} catch {
|
|
30
|
-
emitted = [];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
for (const finding of emitted) {
|
|
34
|
-
const normalized = buildFinding(pattern, ctx, finding || {});
|
|
35
|
-
const dedupeKey = [
|
|
36
|
-
normalized.key,
|
|
37
|
-
normalized.file || '',
|
|
38
|
-
normalized.line || '',
|
|
39
|
-
normalized.fix || '',
|
|
40
|
-
].join('|');
|
|
41
|
-
|
|
42
|
-
if (seen.has(dedupeKey)) continue;
|
|
43
|
-
seen.add(dedupeKey);
|
|
44
|
-
findings.push(normalized);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return findings;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = {
|
|
52
|
-
patterns,
|
|
53
|
-
runShallowRisk,
|
|
54
|
-
SHALLOW_RISK_BANNER,
|
|
55
|
-
SHALLOW_RISK_BANNER_LINES,
|
|
56
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { buildFinding, SHALLOW_RISK_BANNER, SHALLOW_RISK_BANNER_LINES } = require('./shared');
|
|
4
|
+
|
|
5
|
+
const patterns = [
|
|
6
|
+
require('./patterns/agent-config-missing-file'),
|
|
7
|
+
require('./patterns/agent-config-stack-contradiction'),
|
|
8
|
+
require('./patterns/agent-config-cross-platform-drift'),
|
|
9
|
+
require('./patterns/mcp-server-no-allowlist'),
|
|
10
|
+
require('./patterns/hook-script-missing'),
|
|
11
|
+
require('./patterns/agent-config-secret-literal'),
|
|
12
|
+
require('./patterns/agent-config-deprecated-keys'),
|
|
13
|
+
require('./patterns/agent-config-dangerous-autoapprove'),
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
function runShallowRisk(ctx) {
|
|
17
|
+
if (!ctx || process.env.NERVIQ_SHALLOW_RISK === 'off') {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const findings = [];
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
|
|
24
|
+
for (const pattern of patterns) {
|
|
25
|
+
let emitted = [];
|
|
26
|
+
try {
|
|
27
|
+
const next = pattern.run(ctx);
|
|
28
|
+
emitted = Array.isArray(next) ? next : [];
|
|
29
|
+
} catch {
|
|
30
|
+
emitted = [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const finding of emitted) {
|
|
34
|
+
const normalized = buildFinding(pattern, ctx, finding || {});
|
|
35
|
+
const dedupeKey = [
|
|
36
|
+
normalized.key,
|
|
37
|
+
normalized.file || '',
|
|
38
|
+
normalized.line || '',
|
|
39
|
+
normalized.fix || '',
|
|
40
|
+
].join('|');
|
|
41
|
+
|
|
42
|
+
if (seen.has(dedupeKey)) continue;
|
|
43
|
+
seen.add(dedupeKey);
|
|
44
|
+
findings.push(normalized);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return findings;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
patterns,
|
|
53
|
+
runShallowRisk,
|
|
54
|
+
SHALLOW_RISK_BANNER,
|
|
55
|
+
SHALLOW_RISK_BANNER_LINES,
|
|
56
|
+
};
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
SHALLOW_RISK_DOC_URL,
|
|
5
|
-
collectStackClaims,
|
|
6
|
-
} = require('../shared');
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
key: 'agent-config-cross-platform-drift',
|
|
10
|
-
name: 'Cross-platform stack drift detected',
|
|
11
|
-
severity: 'high',
|
|
12
|
-
layer: 'shallow-risk',
|
|
13
|
-
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
14
|
-
run(ctx) {
|
|
15
|
-
const claims = collectStackClaims(ctx).filter((claim) => claim.platform !== 'agent');
|
|
16
|
-
if (claims.length < 2) return [];
|
|
17
|
-
|
|
18
|
-
const byPlatform = new Map();
|
|
19
|
-
for (const claim of claims) {
|
|
20
|
-
const bucket = byPlatform.get(claim.platform) || [];
|
|
21
|
-
bucket.push(claim);
|
|
22
|
-
byPlatform.set(claim.platform, bucket);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const representatives = [];
|
|
26
|
-
for (const bucket of byPlatform.values()) {
|
|
27
|
-
const uniqueKeys = [...new Set(bucket.map((claim) => claim.key))];
|
|
28
|
-
if (uniqueKeys.length !== 1) continue;
|
|
29
|
-
representatives.push(bucket[0]);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
representatives.sort((left, right) => left.file.localeCompare(right.file));
|
|
33
|
-
|
|
34
|
-
for (let index = 0; index < representatives.length; index++) {
|
|
35
|
-
for (let inner = index + 1; inner < representatives.length; inner++) {
|
|
36
|
-
const first = representatives[index];
|
|
37
|
-
const second = representatives[inner];
|
|
38
|
-
if (first.key === second.key) continue;
|
|
39
|
-
|
|
40
|
-
return [{
|
|
41
|
-
file: first.file,
|
|
42
|
-
line: first.line,
|
|
43
|
-
fix: `Drift detected: ${first.file} declares "${first.label}" while ${second.file} declares "${second.label}". Align the shared primary-language guidance or document an intentional platform-specific override.`,
|
|
44
|
-
}];
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return [];
|
|
49
|
-
},
|
|
50
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
SHALLOW_RISK_DOC_URL,
|
|
5
|
+
collectStackClaims,
|
|
6
|
+
} = require('../shared');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
key: 'agent-config-cross-platform-drift',
|
|
10
|
+
name: 'Cross-platform stack drift detected',
|
|
11
|
+
severity: 'high',
|
|
12
|
+
layer: 'shallow-risk',
|
|
13
|
+
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
14
|
+
run(ctx) {
|
|
15
|
+
const claims = collectStackClaims(ctx).filter((claim) => claim.platform !== 'agent');
|
|
16
|
+
if (claims.length < 2) return [];
|
|
17
|
+
|
|
18
|
+
const byPlatform = new Map();
|
|
19
|
+
for (const claim of claims) {
|
|
20
|
+
const bucket = byPlatform.get(claim.platform) || [];
|
|
21
|
+
bucket.push(claim);
|
|
22
|
+
byPlatform.set(claim.platform, bucket);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const representatives = [];
|
|
26
|
+
for (const bucket of byPlatform.values()) {
|
|
27
|
+
const uniqueKeys = [...new Set(bucket.map((claim) => claim.key))];
|
|
28
|
+
if (uniqueKeys.length !== 1) continue;
|
|
29
|
+
representatives.push(bucket[0]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
representatives.sort((left, right) => left.file.localeCompare(right.file));
|
|
33
|
+
|
|
34
|
+
for (let index = 0; index < representatives.length; index++) {
|
|
35
|
+
for (let inner = index + 1; inner < representatives.length; inner++) {
|
|
36
|
+
const first = representatives[index];
|
|
37
|
+
const second = representatives[inner];
|
|
38
|
+
if (first.key === second.key) continue;
|
|
39
|
+
|
|
40
|
+
return [{
|
|
41
|
+
file: first.file,
|
|
42
|
+
line: first.line,
|
|
43
|
+
fix: `Drift detected: ${first.file} declares "${first.label}" while ${second.file} declares "${second.label}". Align the shared primary-language guidance or document an intentional platform-specific override.`,
|
|
44
|
+
}];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return [];
|
|
49
|
+
},
|
|
50
|
+
};
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { SHALLOW_RISK_DOC_URL, escapeRegExp } = require('../shared');
|
|
4
|
-
|
|
5
|
-
const DANGEROUS_ALLOW_PATTERNS = [
|
|
6
|
-
/\brm\b[\s\S]{0,40}-r/i,
|
|
7
|
-
/\bgit\s+push\s+--force\b/i,
|
|
8
|
-
/\bdrop\s+(?:database|table)\b/i,
|
|
9
|
-
/\btruncate\s+table\b/i,
|
|
10
|
-
/\bdelete\s+from\b/i,
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
function isDangerousAllowRule(rule) {
|
|
14
|
-
if (typeof rule !== 'string') return false;
|
|
15
|
-
if (/\bdelete\s+from\b/i.test(rule)) {
|
|
16
|
-
return !/\bwhere\b/i.test(rule) || /\bwhere\s*1\s*=\s*1\b/i.test(rule);
|
|
17
|
-
}
|
|
18
|
-
return DANGEROUS_ALLOW_PATTERNS.some((pattern) => {
|
|
19
|
-
pattern.lastIndex = 0;
|
|
20
|
-
return pattern.test(rule);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
module.exports = {
|
|
25
|
-
key: 'agent-config-dangerous-autoapprove',
|
|
26
|
-
name: 'Agent config auto-approves destructive commands',
|
|
27
|
-
severity: 'critical',
|
|
28
|
-
layer: 'shallow-risk',
|
|
29
|
-
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
30
|
-
run(ctx) {
|
|
31
|
-
const file = '.claude/settings.json';
|
|
32
|
-
const config = ctx.jsonFile(file);
|
|
33
|
-
const allowRules = config && config.permissions && Array.isArray(config.permissions.allow)
|
|
34
|
-
? config.permissions.allow
|
|
35
|
-
: [];
|
|
36
|
-
if (allowRules.length === 0) return [];
|
|
37
|
-
|
|
38
|
-
return allowRules
|
|
39
|
-
.filter(isDangerousAllowRule)
|
|
40
|
-
.map((rule) => ({
|
|
41
|
-
file,
|
|
42
|
-
line: ctx.lineNumber(file, new RegExp(escapeRegExp(rule))) || 1,
|
|
43
|
-
fix: `${file} pre-approves the destructive rule \`${rule}\`. Remove it from the allow-list so destructive commands always require explicit review.`,
|
|
44
|
-
}));
|
|
45
|
-
},
|
|
46
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { SHALLOW_RISK_DOC_URL, escapeRegExp } = require('../shared');
|
|
4
|
+
|
|
5
|
+
const DANGEROUS_ALLOW_PATTERNS = [
|
|
6
|
+
/\brm\b[\s\S]{0,40}-r/i,
|
|
7
|
+
/\bgit\s+push\s+--force\b/i,
|
|
8
|
+
/\bdrop\s+(?:database|table)\b/i,
|
|
9
|
+
/\btruncate\s+table\b/i,
|
|
10
|
+
/\bdelete\s+from\b/i,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
function isDangerousAllowRule(rule) {
|
|
14
|
+
if (typeof rule !== 'string') return false;
|
|
15
|
+
if (/\bdelete\s+from\b/i.test(rule)) {
|
|
16
|
+
return !/\bwhere\b/i.test(rule) || /\bwhere\s*1\s*=\s*1\b/i.test(rule);
|
|
17
|
+
}
|
|
18
|
+
return DANGEROUS_ALLOW_PATTERNS.some((pattern) => {
|
|
19
|
+
pattern.lastIndex = 0;
|
|
20
|
+
return pattern.test(rule);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = {
|
|
25
|
+
key: 'agent-config-dangerous-autoapprove',
|
|
26
|
+
name: 'Agent config auto-approves destructive commands',
|
|
27
|
+
severity: 'critical',
|
|
28
|
+
layer: 'shallow-risk',
|
|
29
|
+
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
30
|
+
run(ctx) {
|
|
31
|
+
const file = '.claude/settings.json';
|
|
32
|
+
const config = ctx.jsonFile(file);
|
|
33
|
+
const allowRules = config && config.permissions && Array.isArray(config.permissions.allow)
|
|
34
|
+
? config.permissions.allow
|
|
35
|
+
: [];
|
|
36
|
+
if (allowRules.length === 0) return [];
|
|
37
|
+
|
|
38
|
+
return allowRules
|
|
39
|
+
.filter(isDangerousAllowRule)
|
|
40
|
+
.map((rule) => ({
|
|
41
|
+
file,
|
|
42
|
+
line: ctx.lineNumber(file, new RegExp(escapeRegExp(rule))) || 1,
|
|
43
|
+
fix: `${file} pre-approves the destructive rule \`${rule}\`. Remove it from the allow-list so destructive commands always require explicit review.`,
|
|
44
|
+
}));
|
|
45
|
+
},
|
|
46
|
+
};
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { AIDER_P0_SOURCES, SHALLOW_RISK_DOC_URL, hasLegacyAiderPin } = require('../shared');
|
|
4
|
-
|
|
5
|
-
const DEPRECATED_AIDER_KEYS = [
|
|
6
|
-
{
|
|
7
|
-
key: 'auto-commit',
|
|
8
|
-
replacement: 'auto-commits',
|
|
9
|
-
pattern: /^\s*auto-commit\s*:/i,
|
|
10
|
-
note: 'removed in Aider 0.60+',
|
|
11
|
-
},
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
module.exports = {
|
|
15
|
-
key: 'agent-config-deprecated-keys',
|
|
16
|
-
name: 'Agent config uses deprecated keys',
|
|
17
|
-
severity: 'medium',
|
|
18
|
-
layer: 'shallow-risk',
|
|
19
|
-
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
20
|
-
run(ctx) {
|
|
21
|
-
if (!Array.isArray(AIDER_P0_SOURCES) || AIDER_P0_SOURCES.length < 2) {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const file = ctx.fileContent('.aider.conf.yml') !== null ? '.aider.conf.yml'
|
|
26
|
-
: (ctx.fileContent('.aider.conf.yaml') !== null ? '.aider.conf.yaml' : null);
|
|
27
|
-
if (!file || hasLegacyAiderPin(ctx)) return [];
|
|
28
|
-
|
|
29
|
-
const findings = [];
|
|
30
|
-
const content = ctx.fileContent(file) || '';
|
|
31
|
-
const lines = content.split(/\r?\n/);
|
|
32
|
-
|
|
33
|
-
for (const keyDef of DEPRECATED_AIDER_KEYS) {
|
|
34
|
-
const lineIndex = lines.findIndex((line) => keyDef.pattern.test(line));
|
|
35
|
-
if (lineIndex === -1) continue;
|
|
36
|
-
findings.push({
|
|
37
|
-
file,
|
|
38
|
-
line: lineIndex + 1,
|
|
39
|
-
fix: `${file} uses deprecated Aider key \`${keyDef.key}\` (${keyDef.note}). Replace it with \`${keyDef.replacement}\` or remove it if the repo intentionally stays on an older Aider release.`,
|
|
40
|
-
sourceUrl: AIDER_P0_SOURCES.find((source) => source.key === 'aider-config-reference')?.url || SHALLOW_RISK_DOC_URL,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return findings;
|
|
45
|
-
},
|
|
46
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { AIDER_P0_SOURCES, SHALLOW_RISK_DOC_URL, hasLegacyAiderPin } = require('../shared');
|
|
4
|
+
|
|
5
|
+
const DEPRECATED_AIDER_KEYS = [
|
|
6
|
+
{
|
|
7
|
+
key: 'auto-commit',
|
|
8
|
+
replacement: 'auto-commits',
|
|
9
|
+
pattern: /^\s*auto-commit\s*:/i,
|
|
10
|
+
note: 'removed in Aider 0.60+',
|
|
11
|
+
},
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
key: 'agent-config-deprecated-keys',
|
|
16
|
+
name: 'Agent config uses deprecated keys',
|
|
17
|
+
severity: 'medium',
|
|
18
|
+
layer: 'shallow-risk',
|
|
19
|
+
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
20
|
+
run(ctx) {
|
|
21
|
+
if (!Array.isArray(AIDER_P0_SOURCES) || AIDER_P0_SOURCES.length < 2) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const file = ctx.fileContent('.aider.conf.yml') !== null ? '.aider.conf.yml'
|
|
26
|
+
: (ctx.fileContent('.aider.conf.yaml') !== null ? '.aider.conf.yaml' : null);
|
|
27
|
+
if (!file || hasLegacyAiderPin(ctx)) return [];
|
|
28
|
+
|
|
29
|
+
const findings = [];
|
|
30
|
+
const content = ctx.fileContent(file) || '';
|
|
31
|
+
const lines = content.split(/\r?\n/);
|
|
32
|
+
|
|
33
|
+
for (const keyDef of DEPRECATED_AIDER_KEYS) {
|
|
34
|
+
const lineIndex = lines.findIndex((line) => keyDef.pattern.test(line));
|
|
35
|
+
if (lineIndex === -1) continue;
|
|
36
|
+
findings.push({
|
|
37
|
+
file,
|
|
38
|
+
line: lineIndex + 1,
|
|
39
|
+
fix: `${file} uses deprecated Aider key \`${keyDef.key}\` (${keyDef.note}). Replace it with \`${keyDef.replacement}\` or remove it if the repo intentionally stays on an older Aider release.`,
|
|
40
|
+
sourceUrl: AIDER_P0_SOURCES.find((source) => source.key === 'aider-config-reference')?.url || SHALLOW_RISK_DOC_URL,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return findings;
|
|
45
|
+
},
|
|
46
|
+
};
|