@nerviq/cli 0.9.2 → 0.9.3
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/package.json +1 -1
- package/src/aider/techniques.js +82 -11
- package/src/copilot/techniques.js +122 -11
- package/src/cursor/techniques.js +90 -10
- package/src/gemini/techniques.js +174 -23
- package/src/opencode/techniques.js +70 -99
- package/src/windsurf/techniques.js +211 -138
package/src/gemini/techniques.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Gemini CLI techniques module — CHECK CATALOG
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 87 checks across 17 categories:
|
|
5
5
|
* v0.1 (40): A. Instructions, B. Config, C. Trust & Safety, D. Hooks, E. MCP, F. Sandbox & Policy
|
|
6
6
|
* v0.5 (54): G. Skills & Agents, H. CI & Automation, I. Extensions
|
|
7
7
|
* v1.0 (68): J. Review & Workflow, K. Quality Deep, L. Commands
|
|
8
|
+
* v1.1 (73): Q. Experiment-Verified Fixes (v0.36.0 findings: --json→-o json, model object format, --yolo in approval, plan mode, --allowed-tools deprecated, eager loading)
|
|
8
9
|
*
|
|
9
10
|
* Each check: { id, name, check(ctx), impact, rating, category, fix, template, file(), line() }
|
|
10
11
|
*/
|
|
@@ -357,7 +358,7 @@ const GEMINI_TECHNIQUES = {
|
|
|
357
358
|
impact: 'critical',
|
|
358
359
|
rating: 5,
|
|
359
360
|
category: 'config',
|
|
360
|
-
fix: 'Fix malformed JSON in .gemini/settings.json
|
|
361
|
+
fix: 'Fix malformed JSON in .gemini/settings.json. Invalid JSON causes exit code 52 — Gemini CLI will not start.',
|
|
361
362
|
template: null,
|
|
362
363
|
file: () => '.gemini/settings.json',
|
|
363
364
|
line: (ctx) => {
|
|
@@ -376,16 +377,21 @@ const GEMINI_TECHNIQUES = {
|
|
|
376
377
|
|
|
377
378
|
geminiModelExplicit: {
|
|
378
379
|
id: 'GM-B03',
|
|
379
|
-
name: 'Model is set explicitly
|
|
380
|
+
name: 'Model is set explicitly in object format (v0.36.0+)',
|
|
380
381
|
check: (ctx) => {
|
|
381
382
|
const data = settingsData(ctx);
|
|
382
383
|
if (!data) return null;
|
|
383
|
-
|
|
384
|
+
if (!data.model) return false;
|
|
385
|
+
// v0.36.0: model field MUST be an object { name: "..." }, not a string
|
|
386
|
+
// String format causes exit code 41: "Expected object, received string"
|
|
387
|
+
if (typeof data.model === 'string') return false;
|
|
388
|
+
if (typeof data.model === 'object' && data.model.name) return true;
|
|
389
|
+
return false;
|
|
384
390
|
},
|
|
385
|
-
impact: '
|
|
386
|
-
rating:
|
|
391
|
+
impact: 'critical',
|
|
392
|
+
rating: 5,
|
|
387
393
|
category: 'config',
|
|
388
|
-
fix: '
|
|
394
|
+
fix: 'CRITICAL: In v0.36.0+, model must be an object: {"model": {"name": "gemini-2.5-flash"}}. String format ({"model": "gemini-2.5-flash"}) causes exit code 41. Default model is now gemini-3-flash-preview.',
|
|
389
395
|
template: 'gemini-settings',
|
|
390
396
|
file: () => '.gemini/settings.json',
|
|
391
397
|
line: (ctx) => ctx.lineNumber('.gemini/settings.json', /"model"/),
|
|
@@ -483,13 +489,17 @@ const GEMINI_TECHNIQUES = {
|
|
|
483
489
|
|
|
484
490
|
geminiNoYolo: {
|
|
485
491
|
id: 'GM-C01',
|
|
486
|
-
name: 'No --yolo in project settings or
|
|
492
|
+
name: 'No --yolo in project settings, scripts, or approval field',
|
|
487
493
|
check: (ctx) => {
|
|
488
494
|
const raw = settingsRaw(ctx);
|
|
489
495
|
const gmd = geminiMd(ctx) || '';
|
|
490
496
|
const combined = `${raw}\n${gmd}`;
|
|
491
497
|
// Check settings and scripts for --yolo
|
|
492
498
|
if (/--yolo\b|\byolo\b.*:\s*true/i.test(raw)) return false;
|
|
499
|
+
// CRITICAL: v0.36.0 silently accepts "--yolo" as an approval value in settings.json
|
|
500
|
+
// {"approval": "--yolo"} passes validation without warning
|
|
501
|
+
const data = settingsData(ctx);
|
|
502
|
+
if (data && data.approval && /yolo/i.test(String(data.approval))) return false;
|
|
493
503
|
// Check package.json scripts
|
|
494
504
|
const pkg = ctx.jsonFile ? ctx.jsonFile('package.json') : null;
|
|
495
505
|
if (pkg && pkg.scripts) {
|
|
@@ -501,7 +511,7 @@ const GEMINI_TECHNIQUES = {
|
|
|
501
511
|
impact: 'critical',
|
|
502
512
|
rating: 5,
|
|
503
513
|
category: 'trust',
|
|
504
|
-
fix: 'Remove --yolo from project settings and scripts.
|
|
514
|
+
fix: 'Remove --yolo from project settings and scripts. WARNING: v0.36.0 silently accepts "--yolo" in the approval field without any validation error — this is a security risk.',
|
|
505
515
|
template: null,
|
|
506
516
|
file: () => '.gemini/settings.json',
|
|
507
517
|
line: (ctx) => {
|
|
@@ -1429,32 +1439,34 @@ const GEMINI_TECHNIQUES = {
|
|
|
1429
1439
|
|
|
1430
1440
|
geminiCiJsonOutput: {
|
|
1431
1441
|
id: 'GM-H04',
|
|
1432
|
-
name: 'Headless output
|
|
1442
|
+
name: 'Headless output uses -o json (not deprecated --json)',
|
|
1433
1443
|
check: (ctx) => {
|
|
1434
1444
|
for (const wf of workflowArtifacts(ctx)) {
|
|
1435
1445
|
if (!/\bgemini\b/i.test(wf.content)) continue;
|
|
1436
|
-
// If gemini is used in CI with -p (prompt), check for --json
|
|
1446
|
+
// If gemini is used in CI with -p (prompt), check for -o json (correct) and flag --json (removed in v0.36.0)
|
|
1437
1447
|
if (/gemini\s+.*-p\b/i.test(wf.content)) {
|
|
1438
|
-
|
|
1448
|
+
// CRITICAL: --json was removed in v0.36.0. Correct flag is -o json or --output-format json
|
|
1449
|
+
if (/--json\b/i.test(wf.content)) return false; // Using deprecated flag
|
|
1450
|
+
return /-o\s+json\b|--output-format\s+json\b/i.test(wf.content);
|
|
1439
1451
|
}
|
|
1440
1452
|
}
|
|
1441
1453
|
return null; // Not relevant if no headless usage
|
|
1442
1454
|
},
|
|
1443
|
-
impact: '
|
|
1444
|
-
rating:
|
|
1455
|
+
impact: 'critical',
|
|
1456
|
+
rating: 5,
|
|
1445
1457
|
category: 'automation',
|
|
1446
|
-
fix: '
|
|
1458
|
+
fix: 'CRITICAL: --json flag was removed in v0.36.0. Use `-o json` or `--output-format json` instead. Three formats available: text, json, stream-json.',
|
|
1447
1459
|
template: null,
|
|
1448
1460
|
file: (ctx) => {
|
|
1449
1461
|
for (const wf of workflowArtifacts(ctx)) {
|
|
1450
|
-
if (/gemini\s+.*-p\b/i.test(wf.content) &&
|
|
1462
|
+
if (/gemini\s+.*-p\b/i.test(wf.content) && (/--json\b/i.test(wf.content) || !/-o\s+json\b|--output-format\s+json\b/i.test(wf.content))) return wf.filePath;
|
|
1451
1463
|
}
|
|
1452
1464
|
return null;
|
|
1453
1465
|
},
|
|
1454
1466
|
line: (ctx) => {
|
|
1455
1467
|
for (const wf of workflowArtifacts(ctx)) {
|
|
1456
1468
|
const line = firstLineMatching(wf.content, /gemini\s+.*-p\b/i);
|
|
1457
|
-
if (line &&
|
|
1469
|
+
if (line && (/--json\b/i.test(wf.content) || !/-o\s+json\b|--output-format\s+json\b/i.test(wf.content))) return line;
|
|
1458
1470
|
}
|
|
1459
1471
|
return null;
|
|
1460
1472
|
},
|
|
@@ -1772,7 +1784,7 @@ const GEMINI_TECHNIQUES = {
|
|
|
1772
1784
|
impact: 'low',
|
|
1773
1785
|
rating: 2,
|
|
1774
1786
|
category: 'quality-deep',
|
|
1775
|
-
fix: 'For monorepos, add component-level GEMINI.md files in package subdirectories
|
|
1787
|
+
fix: 'For monorepos, add component-level GEMINI.md files in package subdirectories. NOTE: v0.36.0 loads ALL subdirectory GEMINI.md files eagerly at startup (not JIT) — watch for token bloat in large monorepos.',
|
|
1776
1788
|
template: null,
|
|
1777
1789
|
file: () => 'GEMINI.md',
|
|
1778
1790
|
line: () => 1,
|
|
@@ -1785,7 +1797,9 @@ const GEMINI_TECHNIQUES = {
|
|
|
1785
1797
|
const gmd = geminiMd(ctx) || '';
|
|
1786
1798
|
const data = settingsData(ctx);
|
|
1787
1799
|
if (!data || !data.model) return null;
|
|
1788
|
-
|
|
1800
|
+
// v0.36.0: model is an object { name: "..." } or could be a legacy string
|
|
1801
|
+
const modelName = (typeof data.model === 'object' && data.model.name) ? data.model.name : String(data.model);
|
|
1802
|
+
const model = modelName.toLowerCase();
|
|
1789
1803
|
// If using a specific model, check that implications are documented
|
|
1790
1804
|
if (/flash|pro/i.test(model)) {
|
|
1791
1805
|
return /\bflash\b|\bpro\b|\bmodel\b.*\b(fast|cheap|accurate|expensive|quality)\b/i.test(gmd);
|
|
@@ -2064,10 +2078,17 @@ const GEMINI_TECHNIQUES = {
|
|
|
2064
2078
|
template: 'gemini-md', file: () => 'GEMINI.md', line: () => 1,
|
|
2065
2079
|
},
|
|
2066
2080
|
geminiSourceFreshness: {
|
|
2067
|
-
id: 'GM-P02', name: 'Config
|
|
2068
|
-
check: (ctx) => {
|
|
2069
|
-
|
|
2070
|
-
|
|
2081
|
+
id: 'GM-P02', name: 'Config and docs reference current Gemini features (no deprecated flags)',
|
|
2082
|
+
check: (ctx) => {
|
|
2083
|
+
const s = ctx.settingsJson();
|
|
2084
|
+
const g = ctx.geminiMdContent() || '';
|
|
2085
|
+
const combined = (s ? JSON.stringify(s) : '') + '\n' + g;
|
|
2086
|
+
if (!s && !g) return null;
|
|
2087
|
+
// Deprecated: chat_model, notepads, old_format, --json (use -o json), --allowed-tools (use policy.toml)
|
|
2088
|
+
return !/chat_model|notepads|old_format/i.test(combined) && !/--json\b/i.test(combined) && !/--allowed-tools\b/i.test(combined);
|
|
2089
|
+
},
|
|
2090
|
+
impact: 'high', rating: 4, category: 'release-freshness',
|
|
2091
|
+
fix: 'Update deprecated references: --json → -o json (v0.36.0), --allowed-tools → policy.toml, chat_model/notepads → removed.',
|
|
2071
2092
|
template: 'gemini-settings', file: () => '.gemini/settings.json', line: () => 1,
|
|
2072
2093
|
},
|
|
2073
2094
|
geminiPropagationCompleteness: {
|
|
@@ -2077,6 +2098,136 @@ const GEMINI_TECHNIQUES = {
|
|
|
2077
2098
|
fix: 'Ensure all surfaces mentioned in GEMINI.md have corresponding definition files.',
|
|
2078
2099
|
template: 'gemini-md', file: () => 'GEMINI.md', line: () => 1,
|
|
2079
2100
|
},
|
|
2101
|
+
|
|
2102
|
+
// =============================================
|
|
2103
|
+
// Q. Experiment-Verified Fixes (5 checks) — GM-Q01..GM-Q05
|
|
2104
|
+
// Added from v0.36.0 experiment findings (2026-04-05)
|
|
2105
|
+
// =============================================
|
|
2106
|
+
|
|
2107
|
+
geminiApprovalFieldValidation: {
|
|
2108
|
+
id: 'GM-Q01',
|
|
2109
|
+
name: 'Approval field in settings.json has valid value (not --yolo)',
|
|
2110
|
+
check: (ctx) => {
|
|
2111
|
+
const data = settingsData(ctx);
|
|
2112
|
+
if (!data || !data.approval) return null;
|
|
2113
|
+
const approval = String(data.approval).toLowerCase();
|
|
2114
|
+
// v0.36.0: "--yolo" is silently accepted in approval field without validation
|
|
2115
|
+
// Valid values: suggest, auto_fix, auto_edit, plan
|
|
2116
|
+
const validValues = ['suggest', 'auto_fix', 'auto_edit', 'plan'];
|
|
2117
|
+
if (/yolo/i.test(approval)) return false;
|
|
2118
|
+
return validValues.includes(approval);
|
|
2119
|
+
},
|
|
2120
|
+
impact: 'critical',
|
|
2121
|
+
rating: 5,
|
|
2122
|
+
category: 'trust',
|
|
2123
|
+
fix: 'SECURITY: v0.36.0 silently accepts "--yolo" in the approval field. Use valid values: suggest, auto_fix, auto_edit, or plan (read-only mode).',
|
|
2124
|
+
template: 'gemini-settings',
|
|
2125
|
+
file: () => '.gemini/settings.json',
|
|
2126
|
+
line: (ctx) => ctx.lineNumber('.gemini/settings.json', /"approval"/),
|
|
2127
|
+
},
|
|
2128
|
+
|
|
2129
|
+
geminiPlanModeDocumented: {
|
|
2130
|
+
id: 'GM-Q02',
|
|
2131
|
+
name: 'Plan mode (read-only 4th approval mode) documented if used',
|
|
2132
|
+
check: (ctx) => {
|
|
2133
|
+
const data = settingsData(ctx);
|
|
2134
|
+
if (!data) return null;
|
|
2135
|
+
const approval = data.approval || data.approvalMode || data.approval_mode;
|
|
2136
|
+
if (!approval || String(approval).toLowerCase() !== 'plan') return null;
|
|
2137
|
+
// If plan mode is active, check it's documented
|
|
2138
|
+
const gmd = geminiMd(ctx) || '';
|
|
2139
|
+
return /\bplan\s*mode\b|\bread.only\b|\bplan\b.*approval/i.test(gmd);
|
|
2140
|
+
},
|
|
2141
|
+
impact: 'medium',
|
|
2142
|
+
rating: 3,
|
|
2143
|
+
category: 'config',
|
|
2144
|
+
fix: 'Document that plan mode is a read-only approval mode (undocumented 4th mode in v0.36.0) that prevents all file modifications.',
|
|
2145
|
+
template: 'gemini-md',
|
|
2146
|
+
file: () => 'GEMINI.md',
|
|
2147
|
+
line: () => 1,
|
|
2148
|
+
},
|
|
2149
|
+
|
|
2150
|
+
geminiNoAllowedToolsDeprecated: {
|
|
2151
|
+
id: 'GM-Q03',
|
|
2152
|
+
name: 'No deprecated --allowed-tools flag (use policy.toml)',
|
|
2153
|
+
check: (ctx) => {
|
|
2154
|
+
const gmd = geminiMd(ctx) || '';
|
|
2155
|
+
const raw = settingsRaw(ctx);
|
|
2156
|
+
// Check workflow files
|
|
2157
|
+
for (const wf of workflowArtifacts(ctx)) {
|
|
2158
|
+
if (/--allowed-tools\b/i.test(wf.content)) return false;
|
|
2159
|
+
}
|
|
2160
|
+
// Check docs and settings
|
|
2161
|
+
if (/--allowed-tools\b/i.test(gmd)) return false;
|
|
2162
|
+
if (/--allowed-tools\b|allowedTools/i.test(raw)) return false;
|
|
2163
|
+
// Check package.json scripts
|
|
2164
|
+
const pkg = ctx.jsonFile ? ctx.jsonFile('package.json') : null;
|
|
2165
|
+
if (pkg && pkg.scripts) {
|
|
2166
|
+
const scriptValues = Object.values(pkg.scripts).join('\n');
|
|
2167
|
+
if (/--allowed-tools\b/i.test(scriptValues)) return false;
|
|
2168
|
+
}
|
|
2169
|
+
return true;
|
|
2170
|
+
},
|
|
2171
|
+
impact: 'high',
|
|
2172
|
+
rating: 4,
|
|
2173
|
+
category: 'release-freshness',
|
|
2174
|
+
fix: '--allowed-tools is DEPRECATED in v0.36.0. Migrate to the Policy Engine with policy.toml files under .gemini/policy/.',
|
|
2175
|
+
template: null,
|
|
2176
|
+
file: (ctx) => {
|
|
2177
|
+
for (const wf of workflowArtifacts(ctx)) {
|
|
2178
|
+
if (/--allowed-tools\b/i.test(wf.content)) return wf.filePath;
|
|
2179
|
+
}
|
|
2180
|
+
const gmd = geminiMd(ctx) || '';
|
|
2181
|
+
if (/--allowed-tools\b/i.test(gmd)) return 'GEMINI.md';
|
|
2182
|
+
return '.gemini/settings.json';
|
|
2183
|
+
},
|
|
2184
|
+
line: (ctx) => {
|
|
2185
|
+
const gmd = geminiMd(ctx) || '';
|
|
2186
|
+
return firstLineMatching(gmd, /--allowed-tools/i) || firstLineMatching(settingsRaw(ctx), /allowed.?tools/i);
|
|
2187
|
+
},
|
|
2188
|
+
},
|
|
2189
|
+
|
|
2190
|
+
geminiEagerLoadingAwareness: {
|
|
2191
|
+
id: 'GM-Q04',
|
|
2192
|
+
name: 'GEMINI.md hierarchy loading behavior is correctly documented',
|
|
2193
|
+
check: (ctx) => {
|
|
2194
|
+
const gmd = geminiMd(ctx) || '';
|
|
2195
|
+
if (!gmd) return null;
|
|
2196
|
+
// Flag if docs mention JIT/lazy loading — this is falsified in v0.36.0
|
|
2197
|
+
if (/\bjit\b|\blazy.load|\bload.*on.demand|\bdynamic.*load/i.test(gmd)) return false;
|
|
2198
|
+
return true;
|
|
2199
|
+
},
|
|
2200
|
+
impact: 'medium',
|
|
2201
|
+
rating: 3,
|
|
2202
|
+
category: 'instructions',
|
|
2203
|
+
fix: 'Remove JIT/lazy-loading claims from GEMINI.md. v0.36.0 loads ALL subdirectory GEMINI.md files eagerly at startup — be mindful of token budget in monorepos.',
|
|
2204
|
+
template: 'gemini-md',
|
|
2205
|
+
file: () => 'GEMINI.md',
|
|
2206
|
+
line: (ctx) => {
|
|
2207
|
+
const gmd = geminiMd(ctx) || '';
|
|
2208
|
+
return firstLineMatching(gmd, /jit|lazy.load|on.demand/i);
|
|
2209
|
+
},
|
|
2210
|
+
},
|
|
2211
|
+
|
|
2212
|
+
geminiModelStringNotObject: {
|
|
2213
|
+
id: 'GM-Q05',
|
|
2214
|
+
name: 'Model field is not a bare string (v0.36.0 requires object)',
|
|
2215
|
+
check: (ctx) => {
|
|
2216
|
+
const raw = settingsRaw(ctx);
|
|
2217
|
+
if (!raw) return null;
|
|
2218
|
+
// Quick check: if "model" key exists and its value is a string, fail
|
|
2219
|
+
const match = raw.match(/"model"\s*:\s*"([^"]+)"/);
|
|
2220
|
+
if (match) return false; // String format detected — will cause exit code 41
|
|
2221
|
+
return true;
|
|
2222
|
+
},
|
|
2223
|
+
impact: 'critical',
|
|
2224
|
+
rating: 5,
|
|
2225
|
+
category: 'config',
|
|
2226
|
+
fix: 'BREAKING: v0.36.0 requires model as object: {"model": {"name": "gemini-2.5-flash"}}. String format causes exit code 41.',
|
|
2227
|
+
template: 'gemini-settings',
|
|
2228
|
+
file: () => '.gemini/settings.json',
|
|
2229
|
+
line: (ctx) => ctx.lineNumber('.gemini/settings.json', /"model"/),
|
|
2230
|
+
},
|
|
2080
2231
|
};
|
|
2081
2232
|
|
|
2082
2233
|
module.exports = {
|