@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerviq/cli",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "The intelligent nervous system for AI coding agents — audit, align, and amplify every platform on every project.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1,17 +1,20 @@
1
1
  /**
2
- * Aider Technique Database — 68 checks (AD-A01 through AD-P03)
2
+ * Aider Technique Database — 71 checks (AD-A01 through AD-P06)
3
3
  *
4
4
  * Aider is fundamentally different from IDE platforms:
5
5
  * - Git-first CLI tool: git is the ONLY safety mechanism
6
6
  * - No hooks, no MCP, no skills, no agents
7
7
  * - Config: .aider.conf.yml (YAML), .aider.model.settings.yml, .env
8
8
  * - 3 model roles: main (coding), editor (applying), weak (commit messages)
9
- * - Architect mode (2-model workflow)
10
- * - Convention files must be explicitly passed (no auto-discovery)
9
+ * - Architect mode (2-model workflow, ~1.73x cost vs standard)
10
+ * - Convention files must be EXPLICITLY passed AND referenced in prompts (no auto-discovery)
11
11
  * - 4-level config precedence: env vars > CLI args > .aider.conf.yml > defaults
12
+ * - Key gotcha: default auto-commit bypasses pre-commit hooks (use --git-commit-verify)
13
+ * - Key gotcha: exit code 0 returned even on auth failure in headless mode
14
+ * - Key gotcha: Playwright auto-scrapes URLs in messages (unexpected side effect)
12
15
  *
13
- * Categories: Config(8), Git Safety(8), Model Config(8), Conventions(6),
14
- * Architecture(4), Security(6), CI(4), Quality(6) + M/N/O/P expansion (18)
16
+ * Categories: Config(8), Git Safety(10), Model Config(8), Conventions(6),
17
+ * Architecture(4), Security(6), CI(4), Quality(6) + M/N/O/P expansion (19)
15
18
  *
16
19
  * Check ID prefix: AD-
17
20
  */
@@ -421,7 +424,7 @@ const AIDER_TECHNIQUES = {
421
424
  impact: 'high',
422
425
  rating: 4,
423
426
  category: 'model-config',
424
- fix: 'Set `architect: true` to use a 2-model workflow (architect plans, editor applies).',
427
+ fix: 'Set `architect: true` to use a 2-model workflow (architect plans, editor applies). NOTE: architect mode costs ~1.73x standard mode per edit ($0.00026 vs $0.00015 measured in live experiment). auto_accept_architect is on by default — no confirmation between steps.',
425
428
  template: 'aider-conf-yml',
426
429
  file: () => '.aider.conf.yml',
427
430
  line: (ctx) => firstLineMatching(configContent(ctx), /architect\s*:/i),
@@ -540,7 +543,7 @@ const AIDER_TECHNIQUES = {
540
543
  impact: 'high',
541
544
  rating: 4,
542
545
  category: 'conventions',
543
- fix: 'Add `read: [CONVENTIONS.md]` to .aider.conf.yml — Aider has NO auto-discovery.',
546
+ fix: 'Add `read: [CONVENTIONS.md]` to .aider.conf.yml — Aider has NO auto-discovery. Additionally, convention files are only followed when EXPLICITLY referenced in the prompt itself (confirmed by live experiment with gpt-4o-mini). Just loading them via --read is not enough; your prompts must say "follow the conventions in CONVENTIONS.md".',
544
547
  template: 'aider-conf-yml',
545
548
  file: () => '.aider.conf.yml',
546
549
  line: (ctx) => firstLineMatching(configContent(ctx), /read\s*:/i),
@@ -867,15 +870,64 @@ const AIDER_TECHNIQUES = {
867
870
  Boolean(ctx.fileContent('.husky/pre-commit')) ||
868
871
  Boolean(ctx.fileContent('.lefthook.yml'));
869
872
  },
870
- impact: 'medium',
871
- rating: 3,
873
+ impact: 'high',
874
+ rating: 4,
872
875
  category: 'ci',
873
- fix: 'Add pre-commit hooks (pre-commit, husky, lefthook) to validate Aider changes.',
876
+ fix: 'Add pre-commit hooks (pre-commit, husky, lefthook). IMPORTANT: Aider default auto-commit BYPASSES pre-commit hooks (confirmed by live experiment). If hooks are critical, pass --git-commit-verify to Aider to restore hook enforcement.',
874
877
  template: null,
875
878
  file: () => null,
876
879
  line: () => null,
877
880
  },
878
881
 
882
+ aiderGitCommitVerify: {
883
+ id: 'AD-G05',
884
+ name: '--git-commit-verify recommended when pre-commit hooks exist',
885
+ check: (ctx) => {
886
+ // Only relevant if pre-commit hooks exist
887
+ const hasHooks = Boolean(ctx.fileContent('.pre-commit-config.yaml')) ||
888
+ Boolean(ctx.fileContent('.husky/pre-commit')) ||
889
+ Boolean(ctx.fileContent('.lefthook.yml'));
890
+ if (!hasHooks) return null;
891
+ const config = configContent(ctx);
892
+ if (!config) return false;
893
+ // Check if git-commit-verify is set in config or documented in conventions
894
+ return /git-commit-verify/i.test(config) ||
895
+ /git-commit-verify/i.test(conventionContent(ctx));
896
+ },
897
+ impact: 'high',
898
+ rating: 4,
899
+ category: 'ci',
900
+ fix: 'When pre-commit hooks exist, add --git-commit-verify to Aider invocations. Default Aider auto-commits SKIP pre-commit hooks entirely (experimentally confirmed). Without this flag, hooks that enforce security or quality checks are silently bypassed.',
901
+ template: 'aider-conf-yml',
902
+ file: () => '.aider.conf.yml',
903
+ line: (ctx) => firstLineMatching(configContent(ctx), /git-commit-verify/i),
904
+ },
905
+
906
+ aiderCiExitCodeUnreliable: {
907
+ id: 'AD-G06',
908
+ name: 'CI scripts handle exit code 0 on auth failure (unreliable exit code)',
909
+ check: (ctx) => {
910
+ const workflows = ctx.workflowFiles ? ctx.workflowFiles() : [];
911
+ if (workflows.length === 0) return null;
912
+ // Check if any workflow mentions aider and has output checking
913
+ for (const wf of workflows) {
914
+ const content = ctx.fileContent(wf) || '';
915
+ if (/aider/i.test(content)) {
916
+ // Good if it checks output, not just exit code
917
+ return /grep|check.*output|--json|error.*detect/i.test(content);
918
+ }
919
+ }
920
+ return null;
921
+ },
922
+ impact: 'high',
923
+ rating: 4,
924
+ category: 'ci',
925
+ fix: 'Aider returns exit code 0 even on auth failure (experimentally confirmed). CI scripts that use Aider MUST NOT rely solely on exit codes to detect failure. Check Aider output for error strings or use output parsing to detect real failures.',
926
+ template: null,
927
+ file: () => '.github/workflows/',
928
+ line: () => null,
929
+ },
930
+
879
931
  // =========================================================================
880
932
  // H — Quality (6 checks: AD-H01 .. AD-H06)
881
933
  // =========================================================================
@@ -1128,6 +1180,25 @@ const AIDER_TECHNIQUES = {
1128
1180
  line: (ctx) => firstLineMatching(configContent(ctx), /voice-language\s*:/i),
1129
1181
  },
1130
1182
 
1183
+ aiderPlaywrightUrlScraping: {
1184
+ id: 'AD-N05',
1185
+ name: 'Playwright URL auto-scraping side effect is expected',
1186
+ check: (ctx) => {
1187
+ const conventions = conventionContent(ctx);
1188
+ const config = configContent(ctx);
1189
+ // Check if team is aware of the Playwright auto-scraping behavior
1190
+ return /playwright|url.*scrap|scrape.*url|auto.*fetch|web.*fetch/i.test(conventions) ||
1191
+ /playwright|url.*scrap/i.test(config);
1192
+ },
1193
+ impact: 'medium',
1194
+ rating: 3,
1195
+ category: 'workflow-patterns',
1196
+ fix: 'Aider automatically scrapes URLs found in messages using Playwright (experimentally confirmed side effect). This causes unexpected network requests and delays. Document this in conventions, and avoid putting real URLs in messages unless scraping is intentional.',
1197
+ template: 'aider-conventions',
1198
+ file: () => 'CONVENTIONS.md',
1199
+ line: () => null,
1200
+ },
1201
+
1131
1202
  // =========================================================================
1132
1203
  // O — Editor Integration (4 checks: AD-O01 .. AD-O04)
1133
1204
  // =========================================================================
@@ -1294,7 +1365,7 @@ const AIDER_TECHNIQUES = {
1294
1365
  impact: 'medium',
1295
1366
  rating: 3,
1296
1367
  category: 'release-readiness',
1297
- fix: 'Enable prompt caching or configure separate weak/editor models for cost optimization.',
1368
+ fix: 'Enable prompt caching or configure separate weak/editor models for cost optimization. Cost reference (measured): standard edit ~$0.00015, architect mode edit ~$0.00026 (~1.73x). Set cache-prompts: true for repeated context, and weak-model for commit messages to reduce costs.',
1298
1369
  template: 'aider-conf-yml',
1299
1370
  file: () => '.aider.conf.yml',
1300
1371
  line: (ctx) => firstLineMatching(configContent(ctx), /cache-prompts\s*:|weak-model\s*:|editor-model\s*:/i),
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Copilot techniques module — CHECK CATALOG
3
3
  *
4
- * 82 checks across 16 categories:
4
+ * 86 checks across 17 categories:
5
5
  * v0.1 (38): A. Instructions(8), B. Config(6), C. Trust & Safety(9), D. MCP(5), E. Cloud Agent(5), F. Organization(5)
6
6
  * v0.5 (54): G. Prompt Files(4), H. Agents & Skills(4), I. VS Code IDE(4), J. CLI(4)
7
7
  * v1.0 (70): K. Cross-Surface(5), L. Enterprise(5), M. Quality Deep(6)
8
- * CP-08 (82): N. Advisory(4), O. Pack(4), P. Repeat(3), Q. Freshness(3)
8
+ * CP-08 (82): N. Advisory(4), O. Pack(4), P. Repeat(3)
9
+ * v1.1 (87): Q. Experiment-Verified CLI Fixes (CLI ingests AGENTS.md/CLAUDE.md, mcpServers key, VS Code settings not CLI-relevant, org policy MCP blocks, BYOK MCP caveat)
9
10
  *
10
11
  * Each check: { id, name, check(ctx), impact, rating, category, fix, template, file(), line() }
11
12
  */
@@ -313,18 +314,19 @@ const COPILOT_TECHNIQUES = {
313
314
 
314
315
  copilotVscodeSettingsExists: {
315
316
  id: 'CP-B01',
316
- name: '.vscode/settings.json has Copilot agent settings',
317
+ name: '.vscode/settings.json has Copilot agent settings (VS Code-only)',
317
318
  check: (ctx) => {
318
319
  const data = vscodeSettingsData(ctx);
319
320
  if (!data) return false;
320
321
  // Check for any Copilot or chat-related key
322
+ // NOTE: These settings affect VS Code only. Copilot CLI ignores them.
321
323
  const raw = vscodeSettingsRaw(ctx);
322
324
  return /github\.copilot|chat\./.test(raw);
323
325
  },
324
326
  impact: 'medium',
325
327
  rating: 4,
326
328
  category: 'config',
327
- fix: 'Add Copilot agent settings to .vscode/settings.json.',
329
+ fix: 'Add Copilot agent settings to .vscode/settings.json. NOTE: These are VS Code-only — Copilot CLI has its own configuration surface.',
328
330
  template: 'copilot-vscode-settings',
329
331
  file: () => '.vscode/settings.json',
330
332
  line: () => 1,
@@ -491,19 +493,20 @@ const COPILOT_TECHNIQUES = {
491
493
 
492
494
  copilotTerminalSandboxEnabled: {
493
495
  id: 'CP-C03',
494
- name: 'Terminal sandbox enabled (VS Code agent)',
496
+ name: 'Terminal sandbox enabled (VS Code-only — does NOT affect CLI)',
495
497
  check: (ctx) => {
496
498
  const data = vscodeSettingsData(ctx);
497
499
  if (!data) return false;
498
500
  const raw = vscodeSettingsRaw(ctx);
499
501
  // Check for chat.tools.terminal.sandbox.enabled = true
502
+ // NOTE: This setting is VS Code-specific. Copilot CLI ignores it entirely.
500
503
  if (raw.includes('terminal.sandbox') && raw.includes('true')) return true;
501
504
  return getCopilotSetting(ctx, 'chat.tools.terminal.sandbox.enabled') === true;
502
505
  },
503
506
  impact: 'high',
504
507
  rating: 5,
505
508
  category: 'trust',
506
- fix: 'Add "chat.tools.terminal.sandbox.enabled": true to .vscode/settings.json.',
509
+ fix: 'Add "chat.tools.terminal.sandbox.enabled": true to .vscode/settings.json. NOTE: This is VS Code-only — Copilot CLI uses its own permission flags, not VS Code settings.',
507
510
  template: 'copilot-vscode-settings',
508
511
  file: () => '.vscode/settings.json',
509
512
  line: (ctx) => {
@@ -533,12 +536,14 @@ const COPILOT_TECHNIQUES = {
533
536
 
534
537
  copilotAutoApprovalSpecific: {
535
538
  id: 'CP-C05',
536
- name: 'Auto-approval rules are specific (not wildcard)',
539
+ name: 'Auto-approval rules are specific (VS Code-only — CLI uses permission flags)',
537
540
  check: (ctx) => {
538
541
  const data = vscodeSettingsData(ctx);
539
542
  if (!data) return null;
540
543
  const raw = vscodeSettingsRaw(ctx);
541
544
  // Check for auto-approval patterns
545
+ // NOTE: autoApproval.terminalCommands is VS Code-specific.
546
+ // Copilot CLI uses its own --permission flags, not this setting.
542
547
  const autoApproval = getCopilotSetting(ctx, 'chat.agent.autoApproval.terminalCommands');
543
548
  if (!autoApproval || !Array.isArray(autoApproval)) return null;
544
549
  // Fail if any wildcard patterns
@@ -547,7 +552,7 @@ const COPILOT_TECHNIQUES = {
547
552
  impact: 'high',
548
553
  rating: 5,
549
554
  category: 'trust',
550
- fix: 'Replace wildcard auto-approval patterns with specific command patterns (e.g., "npm test", "npm run lint").',
555
+ fix: 'Replace wildcard auto-approval patterns with specific command patterns (e.g., "npm test", "npm run lint"). NOTE: This setting only affects VS Code — Copilot CLI approval is controlled by CLI permission flags.',
551
556
  template: null,
552
557
  file: () => '.vscode/settings.json',
553
558
  line: (ctx) => {
@@ -1051,11 +1056,13 @@ const COPILOT_TECHNIQUES = {
1051
1056
 
1052
1057
  copilotAgentsMdEnabled: {
1053
1058
  id: 'CP-H01',
1054
- name: 'If AGENTS.md exists, verify it is enabled in VS Code settings',
1059
+ name: 'If AGENTS.md exists, verify it is enabled in VS Code (CLI reads it automatically)',
1055
1060
  check: (ctx) => {
1056
1061
  const agentsMd = ctx.fileContent('AGENTS.md');
1057
1062
  if (!agentsMd) return null; // N/A
1058
- // AGENTS.md support needs explicit enabling
1063
+ // AGENTS.md support needs explicit enabling in VS Code
1064
+ // WARNING: Copilot CLI reads AGENTS.md (and CLAUDE.md) automatically without any setting!
1065
+ // Use --no-custom-instructions in CLI to prevent this
1059
1066
  const data = vscodeSettingsData(ctx);
1060
1067
  if (!data) return false;
1061
1068
  const raw = vscodeSettingsRaw(ctx);
@@ -1064,7 +1071,7 @@ const COPILOT_TECHNIQUES = {
1064
1071
  impact: 'critical',
1065
1072
  rating: 5,
1066
1073
  category: 'skills-agents',
1067
- fix: 'Enable AGENTS.md support in VS Code settings. It is off by default and silently ignored.',
1074
+ fix: 'Enable AGENTS.md in VS Code settings (off by default). WARNING: Copilot CLI reads AGENTS.md and CLAUDE.md automatically — use --no-custom-instructions to prevent cross-platform instruction leakage.',
1068
1075
  template: 'copilot-vscode-settings',
1069
1076
  file: () => '.vscode/settings.json',
1070
1077
  line: (ctx) => {
@@ -1815,6 +1822,110 @@ const COPILOT_TECHNIQUES = {
1815
1822
  file: () => null,
1816
1823
  line: () => null,
1817
1824
  },
1825
+
1826
+ // =============================================
1827
+ // Q. Experiment-Verified CLI Fixes (5 checks) — CP-Q01..CP-Q05
1828
+ // Added from runtime experiment findings (2026-04-05)
1829
+ // =============================================
1830
+
1831
+ copilotCliIngestsNonCopilotFiles: {
1832
+ id: 'CP-Q01',
1833
+ name: 'Aware that Copilot CLI ingests AGENTS.md and CLAUDE.md',
1834
+ check: (ctx) => {
1835
+ const agentsMd = ctx.fileContent('AGENTS.md');
1836
+ const claudeMd = ctx.fileContent('CLAUDE.md');
1837
+ if (!agentsMd && !claudeMd) return null; // No cross-platform files
1838
+ const instr = copilotInstructions(ctx) || '';
1839
+ // If non-Copilot instruction files exist, check that instructions acknowledge this
1840
+ return /copilot cli|--no-custom-instructions|cross.platform|AGENTS\.md|CLAUDE\.md/i.test(instr);
1841
+ },
1842
+ impact: 'high',
1843
+ rating: 4,
1844
+ category: 'quality-deep',
1845
+ fix: 'WARNING: Copilot CLI ingests AGENTS.md and CLAUDE.md alongside copilot-instructions.md. Document this or use --no-custom-instructions for clean runs.',
1846
+ template: null,
1847
+ file: () => '.github/copilot-instructions.md',
1848
+ line: () => null,
1849
+ },
1850
+
1851
+ copilotCliMcpUsesServerKey: {
1852
+ id: 'CP-Q02',
1853
+ name: 'CLI MCP config uses mcpServers key (not servers)',
1854
+ check: (ctx) => {
1855
+ const mcpData = mcpJsonData(ctx);
1856
+ if (!mcpData) return null;
1857
+ // CLI expects mcpServers, not servers
1858
+ if (mcpData.servers && !mcpData.mcpServers) return false;
1859
+ return true;
1860
+ },
1861
+ impact: 'high',
1862
+ rating: 4,
1863
+ category: 'ci-automation',
1864
+ fix: 'Copilot CLI MCP config expects the "mcpServers" key. "servers" alone may not work in CLI context.',
1865
+ template: null,
1866
+ file: () => '.vscode/mcp.json',
1867
+ line: () => 1,
1868
+ },
1869
+
1870
+ copilotVscodeSettingsNotCliRelevant: {
1871
+ id: 'CP-Q03',
1872
+ name: 'VS Code-specific settings not assumed to affect CLI',
1873
+ check: (ctx) => {
1874
+ const instr = copilotInstructions(ctx) || '';
1875
+ if (!instr) return null;
1876
+ // If instructions reference VS Code settings as if they affect CLI, flag it
1877
+ const mentionsCli = /copilot cli|gh copilot/i.test(instr);
1878
+ const mentionsVscodeForCli = /chat\.tools.*cli|terminal\.sandbox.*cli|autoApproval.*cli/i.test(instr);
1879
+ if (mentionsCli && mentionsVscodeForCli) return false;
1880
+ return true;
1881
+ },
1882
+ impact: 'medium',
1883
+ rating: 3,
1884
+ category: 'quality-deep',
1885
+ fix: 'VS Code settings (sandbox, autoApproval, instructionsFilesLocations) do not affect Copilot CLI. Document CLI-specific configuration separately.',
1886
+ template: null,
1887
+ file: () => '.github/copilot-instructions.md',
1888
+ line: () => null,
1889
+ },
1890
+
1891
+ copilotOrgPolicyBlocksMcp: {
1892
+ id: 'CP-Q04',
1893
+ name: 'Org policy MCP restrictions documented if applicable',
1894
+ check: (ctx) => {
1895
+ const instr = copilotInstructions(ctx) || '';
1896
+ const mcpData = mcpJsonData(ctx);
1897
+ if (!mcpData) return null;
1898
+ const servers = mcpData.servers || mcpData.mcpServers || {};
1899
+ if (Object.keys(servers).length === 0) return null;
1900
+ // If MCP servers are configured, check that org policy restrictions are documented
1901
+ return /org.policy|policy.block|third.party.*mcp|mcp.*restrict|Access denied/i.test(instr);
1902
+ },
1903
+ impact: 'medium',
1904
+ rating: 3,
1905
+ category: 'quality-deep',
1906
+ fix: 'Document that org policies can block third-party MCP servers even in local CLI sessions. Error: "Access denied by policy settings".',
1907
+ template: null,
1908
+ file: () => '.github/copilot-instructions.md',
1909
+ line: () => null,
1910
+ },
1911
+
1912
+ copilotByokMcpCaveat: {
1913
+ id: 'CP-Q05',
1914
+ name: 'BYOK mode MCP limitations documented',
1915
+ check: (ctx) => {
1916
+ const instr = copilotInstructions(ctx) || '';
1917
+ // Only relevant if BYOK is mentioned
1918
+ if (!/byok|bring your own key|openai.*key|COPILOT_.*KEY/i.test(instr)) return null;
1919
+ return /byok.*mcp|mcp.*byok|oauth.*broken|built.in.*github.*mcp/i.test(instr);
1920
+ },
1921
+ impact: 'medium',
1922
+ rating: 3,
1923
+ category: 'quality-deep',
1924
+ fix: 'Document that BYOK mode breaks built-in GitHub MCP server (OAuth auth unavailable). Third-party MCP may also be restricted by org policy.',
1925
+ template: null,
1926
+ file: () => '.github/copilot-instructions.md',
1927
+ line: () => null,
1928
+ },
1818
1929
  };
1819
1930
 
1820
1931
  module.exports = {
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Cursor techniques module — CHECK CATALOG
3
3
  *
4
- * 82 checks across 16 categories:
5
- * v0.1 (40): A. Rules(9), B. Config(7), C. Trust & Safety(9), D. Agent Mode(5), E. MCP(5), F. Instructions Quality(5)
6
- * v0.5 (55): G. Background Agents(5), H. Automations(5), I. Enterprise(5)
4
+ * 88 checks across 16 categories:
5
+ * v0.1 (40): A. Rules(9), B. Config(8), C. Trust & Safety(11), D. Agent Mode(5), E. MCP(5), F. Instructions Quality(5)
6
+ * v0.5 (55): G. Background Agents(5), H. Automations(6), I. Enterprise(5)
7
7
  * v1.0 (70): J. BugBot & Code Review(4), K. Cross-Surface(4), L. Quality Deep(7)
8
8
  * CP-08 (82): M. Advisory(4), N. Pack(4), O. Repeat(3), P. Freshness(3)
9
+ * Exp-fixes (88): +4 new checks from experiment findings
9
10
  *
10
11
  * Each check: { id, name, check(ctx), impact, rating, category, fix, template, file(), line() }
11
12
  */
@@ -180,7 +181,7 @@ const CURSOR_TECHNIQUES = {
180
181
  impact: 'critical',
181
182
  rating: 5,
182
183
  category: 'rules',
183
- fix: 'Migrate .cursorrules to .cursor/rules/*.mdc with proper frontmatter. AGENT MODE IGNORES .cursorrules completely!',
184
+ fix: 'Migrate .cursorrules to .cursor/rules/*.mdc with alwaysApply: true. AGENT MODE COMPLETELY IGNORES .cursorrules (confirmed by direct observation). 82% of projects have broken rules because of this — cursor-doctor audit.',
184
185
  template: 'cursor-legacy-migration',
185
186
  file: () => '.cursorrules',
186
187
  line: () => 1,
@@ -215,10 +216,10 @@ const CURSOR_TECHNIQUES = {
215
216
  return validation.valid;
216
217
  });
217
218
  },
218
- impact: 'high',
219
- rating: 4,
219
+ impact: 'critical',
220
+ rating: 5,
220
221
  category: 'rules',
221
- fix: 'Fix YAML frontmatter in .mdc files. Use only: description, globs, alwaysApply fields.',
222
+ fix: 'Fix YAML frontmatter in .mdc files. Invalid YAML silently skips the entire rule file — no error, no warning. Only 3 fields recognized: description, globs, alwaysApply. 82% of audited projects have broken rules from this issue.',
222
223
  template: null,
223
224
  file: () => '.cursor/rules/',
224
225
  line: () => 1,
@@ -476,8 +477,28 @@ const CURSOR_TECHNIQUES = {
476
477
  line: () => null,
477
478
  },
478
479
 
480
+ cursorMcpServersRootKey: {
481
+ id: 'CU-B08',
482
+ name: 'MCP config has required mcpServers root key',
483
+ check: (ctx) => {
484
+ const raw = mcpJsonRaw(ctx);
485
+ if (!raw) return null;
486
+ const data = mcpJsonData(ctx);
487
+ if (!data) return null;
488
+ // Must have mcpServers key at root — any other key causes silent failure
489
+ return Object.prototype.hasOwnProperty.call(data, 'mcpServers');
490
+ },
491
+ impact: 'critical',
492
+ rating: 5,
493
+ category: 'config',
494
+ fix: 'Ensure .cursor/mcp.json has the "mcpServers" root key. Using "servers" or any other key causes silent failure — zero tools load with no error shown (confirmed by experiment).',
495
+ template: null,
496
+ file: () => '.cursor/mcp.json',
497
+ line: () => 1,
498
+ },
499
+
479
500
  // =============================================
480
- // C. Trust & Safety (9 checks) — CU-C01..CU-C09
501
+ // C. Trust & Safety (11 checks) — CU-C01..CU-C11
481
502
  // =============================================
482
503
 
483
504
  cursorPrivacyMode: {
@@ -489,10 +510,10 @@ const CURSOR_TECHNIQUES = {
489
510
  const docs = docsBundle(ctx);
490
511
  return /privacy mode|zero.?retention|data retention|privacy.*enabled/i.test(docs);
491
512
  },
492
- impact: 'high',
513
+ impact: 'critical',
493
514
  rating: 5,
494
515
  category: 'trust',
495
- fix: 'Enable Privacy Mode in Cursor Settings for zero data retention, or document why it is disabled.',
516
+ fix: 'Privacy Mode is OFF by default — code is sent to all third-party providers (OpenAI, Anthropic, etc.) unless explicitly enabled. Enable in Cursor Settings Privacy Privacy Mode, or document the deliberate decision to keep it off.',
496
517
  template: null,
497
518
  file: () => '.cursor/rules/',
498
519
  line: () => null,
@@ -656,6 +677,44 @@ const CURSOR_TECHNIQUES = {
656
677
  line: () => null,
657
678
  },
658
679
 
680
+ cursorBackgroundAgentHomeDir: {
681
+ id: 'CU-C10',
682
+ name: 'Background agent home directory exposure documented',
683
+ check: (ctx) => {
684
+ const env = envJsonData(ctx);
685
+ if (!env) return null;
686
+ // If background agents are configured, check that the security risk is documented
687
+ const docs = docsBundle(ctx);
688
+ return /home.?dir|npmrc|aws.?credentials|ssh.*key|credential.*exposure|home.*access/i.test(docs);
689
+ },
690
+ impact: 'critical',
691
+ rating: 5,
692
+ category: 'trust',
693
+ fix: 'Background agents have FULL READ access to ~/.npmrc, ~/.aws/credentials, ~/.ssh/ (open security issue since Nov 2025). Document this risk and remove sensitive credentials from home directory before using background agents, or use environment variable references instead.',
694
+ template: null,
695
+ file: () => '.cursor/environment.json',
696
+ line: () => null,
697
+ },
698
+
699
+ cursorCursorignoreShellBypass: {
700
+ id: 'CU-C11',
701
+ name: '.cursorignore does not protect against shell command access',
702
+ check: (ctx) => {
703
+ const hasIgnore = Boolean(ctx.fileContent('.cursorignore'));
704
+ if (!hasIgnore) return null;
705
+ // If .cursorignore exists, check that docs acknowledge shell bypass gap
706
+ const docs = docsBundle(ctx);
707
+ return /cursorignore.*shell|shell.*bypass|terminal.*ignore|ignore.*terminal/i.test(docs);
708
+ },
709
+ impact: 'high',
710
+ rating: 4,
711
+ category: 'trust',
712
+ fix: '.cursorignore only protects files from @Codebase direct reads — agents can still access ignored files via terminal commands (cat, head, etc.). Do not rely on .cursorignore for security. Use proper OS-level file permissions for truly sensitive files.',
713
+ template: null,
714
+ file: () => '.cursorignore',
715
+ line: () => null,
716
+ },
717
+
659
718
  // =============================================
660
719
  // D. Agent Mode (5 checks) — CU-D01..CU-D05
661
720
  // =============================================
@@ -1140,6 +1199,27 @@ const CURSOR_TECHNIQUES = {
1140
1199
  line: () => null,
1141
1200
  },
1142
1201
 
1202
+ cursorAutomationFileSaveDebounce: {
1203
+ id: 'CU-H06',
1204
+ name: 'file_save automation triggers have debounce_ms set',
1205
+ check: (ctx) => {
1206
+ const configs = ctx.automationsConfig ? ctx.automationsConfig() : [];
1207
+ if (configs.length === 0) return null;
1208
+ const combined = configs.map(c => c.content).join('\n');
1209
+ // Only relevant if file_save trigger is used
1210
+ if (!/type:\s*file_save|file[_-]save/i.test(combined)) return null;
1211
+ // Must have debounce_ms set to avoid infinite loop
1212
+ return /debounce_ms|debounce-ms/i.test(combined);
1213
+ },
1214
+ impact: 'critical',
1215
+ rating: 5,
1216
+ category: 'automations',
1217
+ fix: 'Add debounce_ms: 30000 (minimum) to all file_save automation triggers. Without debounce, the automation saves a file → triggers itself → infinite loop that consumes your entire automation quota.',
1218
+ template: null,
1219
+ file: () => '.cursor/automations/',
1220
+ line: () => null,
1221
+ },
1222
+
1143
1223
  // =============================================
1144
1224
  // I. Enterprise (5 checks) — CU-I01..CU-I05
1145
1225
  // =============================================