@nerviq/cli 1.27.1 → 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 -1407
- 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 -72
- 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 -520
- 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
|
@@ -1,70 +1,70 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
SHALLOW_RISK_DOC_URL,
|
|
5
|
-
escapeRegExp,
|
|
6
|
-
getHookCommandPath,
|
|
7
|
-
resolveRepoPath,
|
|
8
|
-
} = require('../shared');
|
|
9
|
-
|
|
10
|
-
const HOOK_EVENTS = new Set([
|
|
11
|
-
'PreToolUse',
|
|
12
|
-
'PostToolUse',
|
|
13
|
-
'Stop',
|
|
14
|
-
'UserPromptSubmit',
|
|
15
|
-
'SessionStart',
|
|
16
|
-
]);
|
|
17
|
-
|
|
18
|
-
function collectHookCommands(node, output = []) {
|
|
19
|
-
if (Array.isArray(node)) {
|
|
20
|
-
for (const item of node) collectHookCommands(item, output);
|
|
21
|
-
return output;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (!node || typeof node !== 'object') {
|
|
25
|
-
return output;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (node.type === 'command' && typeof node.command === 'string') {
|
|
29
|
-
output.push(node.command);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
for (const value of Object.values(node)) {
|
|
33
|
-
collectHookCommands(value, output);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return output;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
module.exports = {
|
|
40
|
-
key: 'hook-script-missing',
|
|
41
|
-
name: 'Configured hook script is missing',
|
|
42
|
-
severity: 'high',
|
|
43
|
-
layer: 'shallow-risk',
|
|
44
|
-
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
45
|
-
run(ctx) {
|
|
46
|
-
const file = '.claude/settings.json';
|
|
47
|
-
const config = ctx.jsonFile(file);
|
|
48
|
-
if (!config || !config.hooks || typeof config.hooks !== 'object') {
|
|
49
|
-
return [];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const findings = [];
|
|
53
|
-
for (const [eventName, entries] of Object.entries(config.hooks)) {
|
|
54
|
-
if (!HOOK_EVENTS.has(eventName)) continue;
|
|
55
|
-
for (const command of collectHookCommands(entries)) {
|
|
56
|
-
const scriptPath = getHookCommandPath(command);
|
|
57
|
-
if (!scriptPath) continue;
|
|
58
|
-
const resolvedPath = resolveRepoPath(ctx, file, scriptPath, 'repo-root');
|
|
59
|
-
if (!resolvedPath || ctx.fileContent(resolvedPath) !== null) continue;
|
|
60
|
-
findings.push({
|
|
61
|
-
file,
|
|
62
|
-
line: ctx.lineNumber(file, new RegExp(escapeRegExp(command))) || ctx.lineNumber(file, new RegExp(`"${escapeRegExp(eventName)}"`)) || 1,
|
|
63
|
-
fix: `${file} declares a ${eventName} hook at \`${resolvedPath}\`, but the script is missing. Create the hook file or remove the dead hook reference.`,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return findings;
|
|
69
|
-
},
|
|
70
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
SHALLOW_RISK_DOC_URL,
|
|
5
|
+
escapeRegExp,
|
|
6
|
+
getHookCommandPath,
|
|
7
|
+
resolveRepoPath,
|
|
8
|
+
} = require('../shared');
|
|
9
|
+
|
|
10
|
+
const HOOK_EVENTS = new Set([
|
|
11
|
+
'PreToolUse',
|
|
12
|
+
'PostToolUse',
|
|
13
|
+
'Stop',
|
|
14
|
+
'UserPromptSubmit',
|
|
15
|
+
'SessionStart',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function collectHookCommands(node, output = []) {
|
|
19
|
+
if (Array.isArray(node)) {
|
|
20
|
+
for (const item of node) collectHookCommands(item, output);
|
|
21
|
+
return output;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!node || typeof node !== 'object') {
|
|
25
|
+
return output;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (node.type === 'command' && typeof node.command === 'string') {
|
|
29
|
+
output.push(node.command);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const value of Object.values(node)) {
|
|
33
|
+
collectHookCommands(value, output);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
key: 'hook-script-missing',
|
|
41
|
+
name: 'Configured hook script is missing',
|
|
42
|
+
severity: 'high',
|
|
43
|
+
layer: 'shallow-risk',
|
|
44
|
+
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
45
|
+
run(ctx) {
|
|
46
|
+
const file = '.claude/settings.json';
|
|
47
|
+
const config = ctx.jsonFile(file);
|
|
48
|
+
if (!config || !config.hooks || typeof config.hooks !== 'object') {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const findings = [];
|
|
53
|
+
for (const [eventName, entries] of Object.entries(config.hooks)) {
|
|
54
|
+
if (!HOOK_EVENTS.has(eventName)) continue;
|
|
55
|
+
for (const command of collectHookCommands(entries)) {
|
|
56
|
+
const scriptPath = getHookCommandPath(command);
|
|
57
|
+
if (!scriptPath) continue;
|
|
58
|
+
const resolvedPath = resolveRepoPath(ctx, file, scriptPath, 'repo-root');
|
|
59
|
+
if (!resolvedPath || ctx.fileContent(resolvedPath) !== null) continue;
|
|
60
|
+
findings.push({
|
|
61
|
+
file,
|
|
62
|
+
line: ctx.lineNumber(file, new RegExp(escapeRegExp(command))) || ctx.lineNumber(file, new RegExp(`"${escapeRegExp(eventName)}"`)) || 1,
|
|
63
|
+
fix: `${file} declares a ${eventName} hook at \`${resolvedPath}\`, but the script is missing. Create the hook file or remove the dead hook reference.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return findings;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
SHALLOW_RISK_DOC_URL,
|
|
5
|
-
escapeRegExp,
|
|
6
|
-
isClearlyLocalMcpBinary,
|
|
7
|
-
} = require('../shared');
|
|
8
|
-
|
|
9
|
-
function hasBroadOpenPermissions(server) {
|
|
10
|
-
if (!server || typeof server !== 'object') return false;
|
|
11
|
-
if (Array.isArray(server.permissions) && server.permissions.length === 0) return true;
|
|
12
|
-
if (server.allow === '*') return true;
|
|
13
|
-
if (Array.isArray(server.allow) && server.allow.includes('*')) return true;
|
|
14
|
-
if (server.permissions && typeof server.permissions === 'object') {
|
|
15
|
-
if (server.permissions.allow === '*') return true;
|
|
16
|
-
if (Array.isArray(server.permissions.allow) && server.permissions.allow.includes('*')) return true;
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
module.exports = {
|
|
22
|
-
key: 'mcp-server-no-allowlist',
|
|
23
|
-
name: 'MCP server has no allowlist',
|
|
24
|
-
severity: 'critical',
|
|
25
|
-
layer: 'shallow-risk',
|
|
26
|
-
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
27
|
-
run(ctx) {
|
|
28
|
-
const findings = [];
|
|
29
|
-
const candidates = ['.claude/settings.json', '.mcp.json'];
|
|
30
|
-
|
|
31
|
-
for (const file of candidates) {
|
|
32
|
-
const config = ctx.jsonFile(file);
|
|
33
|
-
const servers = config && config.mcpServers && typeof config.mcpServers === 'object'
|
|
34
|
-
? config.mcpServers
|
|
35
|
-
: null;
|
|
36
|
-
if (!servers) continue;
|
|
37
|
-
|
|
38
|
-
for (const [serverName, server] of Object.entries(servers)) {
|
|
39
|
-
if (!hasBroadOpenPermissions(server)) continue;
|
|
40
|
-
const command = typeof server.command === 'string' ? server.command : '';
|
|
41
|
-
findings.push({
|
|
42
|
-
severity: isClearlyLocalMcpBinary(command) ? 'high' : 'critical',
|
|
43
|
-
file,
|
|
44
|
-
line: ctx.lineNumber(file, new RegExp(`"${escapeRegExp(serverName)}"`)) || 1,
|
|
45
|
-
fix: `MCP server "${serverName}" in ${file} has broad access without an allowlist. Add a narrow allow/permissions list before relying on it in CI or production repos.`,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return findings;
|
|
51
|
-
},
|
|
52
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
SHALLOW_RISK_DOC_URL,
|
|
5
|
+
escapeRegExp,
|
|
6
|
+
isClearlyLocalMcpBinary,
|
|
7
|
+
} = require('../shared');
|
|
8
|
+
|
|
9
|
+
function hasBroadOpenPermissions(server) {
|
|
10
|
+
if (!server || typeof server !== 'object') return false;
|
|
11
|
+
if (Array.isArray(server.permissions) && server.permissions.length === 0) return true;
|
|
12
|
+
if (server.allow === '*') return true;
|
|
13
|
+
if (Array.isArray(server.allow) && server.allow.includes('*')) return true;
|
|
14
|
+
if (server.permissions && typeof server.permissions === 'object') {
|
|
15
|
+
if (server.permissions.allow === '*') return true;
|
|
16
|
+
if (Array.isArray(server.permissions.allow) && server.permissions.allow.includes('*')) return true;
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = {
|
|
22
|
+
key: 'mcp-server-no-allowlist',
|
|
23
|
+
name: 'MCP server has no allowlist',
|
|
24
|
+
severity: 'critical',
|
|
25
|
+
layer: 'shallow-risk',
|
|
26
|
+
sourceUrl: SHALLOW_RISK_DOC_URL,
|
|
27
|
+
run(ctx) {
|
|
28
|
+
const findings = [];
|
|
29
|
+
const candidates = ['.claude/settings.json', '.mcp.json'];
|
|
30
|
+
|
|
31
|
+
for (const file of candidates) {
|
|
32
|
+
const config = ctx.jsonFile(file);
|
|
33
|
+
const servers = config && config.mcpServers && typeof config.mcpServers === 'object'
|
|
34
|
+
? config.mcpServers
|
|
35
|
+
: null;
|
|
36
|
+
if (!servers) continue;
|
|
37
|
+
|
|
38
|
+
for (const [serverName, server] of Object.entries(servers)) {
|
|
39
|
+
if (!hasBroadOpenPermissions(server)) continue;
|
|
40
|
+
const command = typeof server.command === 'string' ? server.command : '';
|
|
41
|
+
findings.push({
|
|
42
|
+
severity: isClearlyLocalMcpBinary(command) ? 'high' : 'critical',
|
|
43
|
+
file,
|
|
44
|
+
line: ctx.lineNumber(file, new RegExp(`"${escapeRegExp(serverName)}"`)) || 1,
|
|
45
|
+
fix: `MCP server "${serverName}" in ${file} has broad access without an allowlist. Add a narrow allow/permissions list before relying on it in CI or production repos.`,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return findings;
|
|
51
|
+
},
|
|
52
|
+
};
|