@nerviq/cli 1.2.3 → 1.2.5

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": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "The intelligent nervous system for AI coding agents — 2,306 checks across 8 platforms and 10 languages. Audit, align, and amplify.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -18,7 +18,7 @@
18
18
  "test": "node test/run.js",
19
19
  "test:jest": "jest",
20
20
  "test:coverage": "jest --coverage",
21
- "test:all": "npm test && npx jest && node test/check-matrix.js && node test/codex-check-matrix.js && node test/gemini-check-matrix.js && node test/copilot-check-matrix.js && node test/cursor-check-matrix.js && node test/golden-matrix.js && node test/codex-golden-matrix.js && node test/gemini-golden-matrix.js && node test/copilot-golden-matrix.js && node test/cursor-golden-matrix.js",
21
+ "test:all": "npm test && npx jest && node test/check-matrix.js && node test/codex-check-matrix.js && node test/gemini-check-matrix.js && node test/copilot-check-matrix.js && node test/cursor-check-matrix.js && node test/windsurf-check-matrix.js && node test/aider-check-matrix.js && node test/opencode-check-matrix.js && node test/golden-matrix.js && node test/codex-golden-matrix.js && node test/gemini-golden-matrix.js && node test/copilot-golden-matrix.js && node test/cursor-golden-matrix.js && node test/windsurf-golden-matrix.js && node test/aider-golden-matrix.js && node test/opencode-golden-matrix.js",
22
22
  "benchmark:perf": "node tools/benchmark.js",
23
23
  "catalog": "node -e \"const {generateCatalog}=require('./src/catalog');console.log(JSON.stringify(generateCatalog(),null,2))\""
24
24
  },
package/src/activity.js CHANGED
@@ -2,6 +2,10 @@ const fs = require('fs');
2
2
  const os = require('os');
3
3
  const path = require('path');
4
4
  const { version } = require('../package.json');
5
+ const {
6
+ resolveProjectStateReadPath,
7
+ ensureProjectStateDir,
8
+ } = require('./state-paths');
5
9
 
6
10
  /**
7
11
  * Generate a machine-level user identity for audit tracking.
@@ -33,15 +37,11 @@ function timestampId() {
33
37
  }
34
38
 
35
39
  function ensureArtifactDirs(dir) {
36
- const root = path.join(dir, '.claude', 'claudex-setup');
37
- const activityDir = path.join(root, 'activity');
38
- const rollbackDir = path.join(root, 'rollbacks');
39
- const snapshotDir = path.join(root, 'snapshots');
40
- const outcomesDir = path.join(root, 'outcomes');
41
- fs.mkdirSync(activityDir, { recursive: true });
42
- fs.mkdirSync(rollbackDir, { recursive: true });
43
- fs.mkdirSync(snapshotDir, { recursive: true });
44
- fs.mkdirSync(outcomesDir, { recursive: true });
40
+ const root = ensureProjectStateDir(dir);
41
+ const activityDir = ensureProjectStateDir(dir, 'activity');
42
+ const rollbackDir = ensureProjectStateDir(dir, 'rollbacks');
43
+ const snapshotDir = ensureProjectStateDir(dir, 'snapshots');
44
+ const outcomesDir = ensureProjectStateDir(dir, 'outcomes');
45
45
  return { root, activityDir, rollbackDir, snapshotDir, outcomesDir };
46
46
  }
47
47
 
@@ -161,7 +161,7 @@ function updateSnapshotIndex(snapshotDir, record) {
161
161
  }
162
162
 
163
163
  /**
164
- * Write a normalized snapshot artifact to .claude/claudex-setup/snapshots/ and update the index.
164
+ * Write a normalized snapshot artifact to .nerviq/snapshots/ and update the index.
165
165
  * @param {string} dir - Project root directory.
166
166
  * @param {string} snapshotKind - Snapshot type ('audit', 'benchmark', 'governance', 'augment', 'suggest-only').
167
167
  * @param {Object} payload - Full result payload to persist.
@@ -208,7 +208,7 @@ function writeSnapshotArtifact(dir, snapshotKind, payload, meta = {}) {
208
208
  }
209
209
 
210
210
  function readSnapshotIndex(dir) {
211
- const indexPath = path.join(dir, '.claude', 'claudex-setup', 'snapshots', 'index.json');
211
+ const indexPath = resolveProjectStateReadPath(dir, 'snapshots', 'index.json');
212
212
  if (!fs.existsSync(indexPath)) return [];
213
213
  try {
214
214
  const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
@@ -344,7 +344,7 @@ function exportTrendReport(dir) {
344
344
  }
345
345
 
346
346
  function readOutcomeIndex(dir) {
347
- const indexPath = path.join(dir, '.claude', 'claudex-setup', 'outcomes', 'index.json');
347
+ const indexPath = resolveProjectStateReadPath(dir, 'outcomes', 'index.json');
348
348
  if (!fs.existsSync(indexPath)) return [];
349
349
  try {
350
350
  const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
@@ -4,7 +4,7 @@
4
4
  * Adapts the shared activity/snapshot backend for Aider platform.
5
5
  * Provides: history, compare, trend, watch, feedback, insights.
6
6
  *
7
- * Aider snapshots stored in .claude/claudex-setup/snapshots/ filtered by platform='aider'.
7
+ * Aider snapshots stored in .nerviq/snapshots/ (legacy: .claude/claudex-setup/snapshots/) filtered by platform='aider'.
8
8
  */
9
9
 
10
10
  const path = require('path');
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "synced_from": "claudex",
3
- "synced_at": "2026-04-02T15:12:04Z",
4
- "total_items": 1107,
3
+ "synced_at": "2026-04-05T17:08:58Z",
4
+ "total_items": 1118,
5
5
  "tested": 948,
6
- "last_id": 1157
6
+ "last_id": 1137
7
7
  }
@@ -5,7 +5,7 @@
5
5
  * Provides: history, compare, trend, watch, feedback, insights.
6
6
  *
7
7
  * Codex snapshots are stored alongside Claude snapshots in
8
- * .claude/claudex-setup/snapshots/ but filtered by platform='codex'.
8
+ * .nerviq/snapshots/ (legacy: .claude/claudex-setup/snapshots/) but filtered by platform='codex'.
9
9
  */
10
10
 
11
11
  const path = require('path');
@@ -166,7 +166,7 @@ function applyPatch(dir, filePath, patchFn, options = {}) {
166
166
  }
167
167
 
168
168
  // Backup + write
169
- const backupPath = fullPath + '.claudex-backup';
169
+ const backupPath = fullPath + '.nerviq-backup';
170
170
  fs.writeFileSync(backupPath, original, 'utf8');
171
171
  fs.writeFileSync(fullPath, patched, 'utf8');
172
172
 
@@ -1,8 +1,9 @@
1
1
  const os = require('os');
2
- const path = require('path');
3
- const { EMBEDDED_SECRET_PATTERNS, containsEmbeddedSecret } = require('../secret-patterns');
4
- const { attachSourceUrls } = require('../source-urls');
5
- const { buildSupplementalChecks } = require('../supplemental-checks');
2
+ const path = require('path');
3
+ const { EMBEDDED_SECRET_PATTERNS, containsEmbeddedSecret } = require('../secret-patterns');
4
+ const { attachSourceUrls } = require('../source-urls');
5
+ const { buildSupplementalChecks } = require('../supplemental-checks');
6
+ const { resolveProjectStateReadPath } = require('../state-paths');
6
7
 
7
8
  const CODEX_SUPPLEMENTAL_SOURCE_URLS = {
8
9
  'testing-strategy': 'https://developers.openai.com/codex/cli',
@@ -3097,16 +3098,15 @@ const CODEX_TECHNIQUES = {
3097
3098
  // CP-08: New checks (O. Repeat-Usage Hygiene)
3098
3099
  // =============================================
3099
3100
 
3100
- codexSnapshotRetention: {
3101
- id: 'CX-O01',
3102
- name: 'At least one prior audit snapshot exists for repeat-usage',
3103
- check: (ctx) => {
3104
- const snapshotDir = path.join(ctx.dir, '.claude', 'claudex-setup', 'snapshots');
3105
- try {
3106
- const indexPath = path.join(snapshotDir, 'index.json');
3107
- const fs = require('fs');
3108
- if (!fs.existsSync(indexPath)) return null; // No snapshots yet, not a failure
3109
- const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
3101
+ codexSnapshotRetention: {
3102
+ id: 'CX-O01',
3103
+ name: 'At least one prior audit snapshot exists for repeat-usage',
3104
+ check: (ctx) => {
3105
+ try {
3106
+ const indexPath = resolveProjectStateReadPath(ctx.dir, 'snapshots', 'index.json');
3107
+ const fs = require('fs');
3108
+ if (!fs.existsSync(indexPath)) return null; // No snapshots yet, not a failure
3109
+ const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
3110
3110
  return Array.isArray(entries) && entries.length > 0;
3111
3111
  } catch {
3112
3112
  return null;
@@ -3121,16 +3121,15 @@ const CODEX_TECHNIQUES = {
3121
3121
  line: () => null,
3122
3122
  },
3123
3123
 
3124
- codexFeedbackLoopHealth: {
3125
- id: 'CX-O02',
3126
- name: 'Feedback loop is functional when feedback has been submitted',
3127
- check: (ctx) => {
3128
- const outcomesDir = path.join(ctx.dir, '.claude', 'claudex-setup', 'outcomes');
3129
- try {
3130
- const indexPath = path.join(outcomesDir, 'index.json');
3131
- const fs = require('fs');
3132
- if (!fs.existsSync(indexPath)) return null; // No feedback yet, not a failure
3133
- const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
3124
+ codexFeedbackLoopHealth: {
3125
+ id: 'CX-O02',
3126
+ name: 'Feedback loop is functional when feedback has been submitted',
3127
+ check: (ctx) => {
3128
+ try {
3129
+ const indexPath = resolveProjectStateReadPath(ctx.dir, 'outcomes', 'index.json');
3130
+ const fs = require('fs');
3131
+ if (!fs.existsSync(indexPath)) return null; // No feedback yet, not a failure
3132
+ const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
3134
3133
  return Array.isArray(entries) && entries.length > 0;
3135
3134
  } catch {
3136
3135
  return null;
@@ -3145,16 +3144,15 @@ const CODEX_TECHNIQUES = {
3145
3144
  line: () => null,
3146
3145
  },
3147
3146
 
3148
- codexTrendDataAvailability: {
3149
- id: 'CX-O03',
3150
- name: 'Trend data is computable (2+ snapshots with compatible schemas)',
3151
- check: (ctx) => {
3152
- const snapshotDir = path.join(ctx.dir, '.claude', 'claudex-setup', 'snapshots');
3153
- try {
3154
- const indexPath = path.join(snapshotDir, 'index.json');
3155
- const fs = require('fs');
3156
- if (!fs.existsSync(indexPath)) return null;
3157
- const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
3147
+ codexTrendDataAvailability: {
3148
+ id: 'CX-O03',
3149
+ name: 'Trend data is computable (2+ snapshots with compatible schemas)',
3150
+ check: (ctx) => {
3151
+ try {
3152
+ const indexPath = resolveProjectStateReadPath(ctx.dir, 'snapshots', 'index.json');
3153
+ const fs = require('fs');
3154
+ if (!fs.existsSync(indexPath)) return null;
3155
+ const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
3158
3156
  const audits = (Array.isArray(entries) ? entries : []).filter(e => e.snapshotKind === 'audit');
3159
3157
  return audits.length >= 2;
3160
3158
  } catch {
@@ -196,7 +196,7 @@ function applyPatch(dir, filePath, patchFn, options = {}) {
196
196
  return { success: true, reason: 'dry run', preview, unchanged: false };
197
197
  }
198
198
 
199
- const backupPath = fullPath + '.claudex-backup';
199
+ const backupPath = fullPath + '.nerviq-backup';
200
200
  fs.writeFileSync(backupPath, original, 'utf8');
201
201
  fs.writeFileSync(fullPath, patched, 'utf8');
202
202
 
@@ -10,6 +10,7 @@
10
10
  const path = require('path');
11
11
  const { COPILOT_DOMAIN_PACKS } = require('./domain-packs');
12
12
  const { COPILOT_MCP_PACKS } = require('./mcp-packs');
13
+ const { resolveProjectStateReadPath } = require('../state-paths');
13
14
 
14
15
  // ---------------------------------------------------------------------------
15
16
  // 1. Multi-Pack Composition Engine
@@ -388,7 +389,7 @@ const GATE_THRESHOLDS = {
388
389
 
389
390
  function getCopilotHistory(dir, limit = 20) {
390
391
  const fs = require('fs');
391
- const snapshotDir = path.join(dir, '.claude', 'claudex-setup', 'snapshots');
392
+ const snapshotDir = resolveProjectStateReadPath(dir, 'snapshots');
392
393
  try {
393
394
  const files = fs.readdirSync(snapshotDir)
394
395
  .filter(f => f.endsWith('.json'))
@@ -201,7 +201,7 @@ function applyPatch(dir, filePath, patchFn, options = {}) {
201
201
  return { success: true, reason: 'dry run', preview, unchanged: false };
202
202
  }
203
203
 
204
- const backupPath = fullPath + '.claudex-backup';
204
+ const backupPath = fullPath + '.nerviq-backup';
205
205
  fs.writeFileSync(backupPath, original, 'utf8');
206
206
  fs.writeFileSync(fullPath, patched, 'utf8');
207
207
 
@@ -10,6 +10,7 @@
10
10
  const path = require('path');
11
11
  const { CURSOR_DOMAIN_PACKS } = require('./domain-packs');
12
12
  const { CURSOR_MCP_PACKS } = require('./mcp-packs');
13
+ const { resolveProjectStateReadPath } = require('../state-paths');
13
14
 
14
15
  // ---------------------------------------------------------------------------
15
16
  // 1. Multi-Pack Composition Engine (with MDC awareness)
@@ -406,7 +407,7 @@ const GATE_THRESHOLDS = {
406
407
 
407
408
  function getCursorHistory(dir, limit = 20) {
408
409
  const fs = require('fs');
409
- const snapshotDir = path.join(dir, '.claude', 'claudex-setup', 'snapshots');
410
+ const snapshotDir = resolveProjectStateReadPath(dir, 'snapshots');
410
411
  try {
411
412
  const files = fs.readdirSync(snapshotDir)
412
413
  .filter(f => f.endsWith('.json'))
package/src/feedback.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const readline = require('readline');
4
+ const { ensureProjectStateDir, resolveProjectStateReadPath } = require('./state-paths');
4
5
 
5
6
  let lastTimestamp = '';
6
7
  let counter = 0;
@@ -17,9 +18,11 @@ function timestampId() {
17
18
  }
18
19
 
19
20
  function ensureFeedbackDir(dir) {
20
- const feedbackDir = path.join(dir, '.claude', 'claudex-setup', 'feedback');
21
- fs.mkdirSync(feedbackDir, { recursive: true });
22
- return feedbackDir;
21
+ return ensureProjectStateDir(dir, 'feedback');
22
+ }
23
+
24
+ function resolveFeedbackDir(dir) {
25
+ return resolveProjectStateReadPath(dir, 'feedback');
23
26
  }
24
27
 
25
28
  function writeJson(filePath, payload) {
@@ -47,8 +50,10 @@ function saveFeedback(dir, payload) {
47
50
  }
48
51
 
49
52
  function getFeedbackSummary(dir) {
50
- const feedbackDir = ensureFeedbackDir(dir);
51
- const files = fs.readdirSync(feedbackDir).filter((name) => name.endsWith('.json'));
53
+ const feedbackDir = resolveFeedbackDir(dir);
54
+ const files = fs.existsSync(feedbackDir)
55
+ ? fs.readdirSync(feedbackDir).filter((name) => name.endsWith('.json'))
56
+ : [];
52
57
  const entries = [];
53
58
 
54
59
  for (const file of files) {
@@ -107,7 +112,7 @@ async function collectFeedback(dir, options = {}) {
107
112
  helpful: 0,
108
113
  unhelpful: 0,
109
114
  entries: [],
110
- relativeDir: path.relative(dir, ensureFeedbackDir(dir)),
115
+ relativeDir: path.relative(dir, resolveFeedbackDir(dir)),
111
116
  };
112
117
  }
113
118
 
@@ -119,7 +124,7 @@ async function collectFeedback(dir, options = {}) {
119
124
  helpful: 0,
120
125
  unhelpful: 0,
121
126
  entries: [],
122
- relativeDir: path.relative(dir, ensureFeedbackDir(dir)),
127
+ relativeDir: path.relative(dir, resolveFeedbackDir(dir)),
123
128
  };
124
129
  }
125
130
 
@@ -161,7 +166,7 @@ async function collectFeedback(dir, options = {}) {
161
166
  helpful,
162
167
  unhelpful,
163
168
  entries,
164
- relativeDir: path.relative(dir, ensureFeedbackDir(dir)),
169
+ relativeDir: path.relative(dir, resolveFeedbackDir(dir)),
165
170
  summary: getFeedbackSummary(dir),
166
171
  };
167
172
  }
@@ -5,7 +5,7 @@
5
5
  * Provides: history, compare, trend, feedback, insights.
6
6
  *
7
7
  * Gemini snapshots are stored alongside Claude snapshots in
8
- * .claude/claudex-setup/snapshots/ but filtered by platform='gemini'.
8
+ * .nerviq/snapshots/ (legacy: .claude/claudex-setup/snapshots/) but filtered by platform='gemini'.
9
9
  */
10
10
 
11
11
  const path = require('path');
@@ -187,7 +187,7 @@ function applyPatch(dir, filePath, patchFn, options = {}) {
187
187
  }
188
188
 
189
189
  // Backup + write
190
- const backupPath = fullPath + '.claudex-backup';
190
+ const backupPath = fullPath + '.nerviq-backup';
191
191
  fs.writeFileSync(backupPath, original, 'utf8');
192
192
  fs.writeFileSync(fullPath, patched, 'utf8');
193
193
 
@@ -10,6 +10,7 @@
10
10
  const path = require('path');
11
11
  const { GEMINI_DOMAIN_PACKS } = require('./domain-packs');
12
12
  const { GEMINI_MCP_PACKS } = require('./mcp-packs');
13
+ const { resolveGeminiStateReadPath } = require('../state-paths');
13
14
 
14
15
  // ---------------------------------------------------------------------------
15
16
  // 1. Multi-Pack Composition Engine
@@ -666,14 +667,14 @@ const GATE_THRESHOLDS = {
666
667
  };
667
668
 
668
669
  /**
669
- * Read Gemini audit snapshot history from the local .gemini/.claudex/ directory.
670
+ * Read Gemini audit snapshot history from the local .gemini/.nerviq/ directory.
670
671
  * @param {string} dir - Project directory
671
672
  * @param {number} limit - Max snapshots to read
672
673
  * @returns {object[]} Array of snapshot objects
673
674
  */
674
675
  function getGeminiHistory(dir, limit = 20) {
675
676
  const fs = require('fs');
676
- const snapshotDir = path.join(dir, '.gemini', '.claudex', 'snapshots');
677
+ const snapshotDir = resolveGeminiStateReadPath(dir, 'snapshots');
677
678
  try {
678
679
  const files = fs.readdirSync(snapshotDir)
679
680
  .filter(f => f.endsWith('.json'))
@@ -14,10 +14,11 @@
14
14
  const os = require('os');
15
15
  const path = require('path');
16
16
  const { GeminiProjectContext } = require('./context');
17
- const { EMBEDDED_SECRET_PATTERNS, containsEmbeddedSecret } = require('../secret-patterns');
18
- const { attachSourceUrls } = require('../source-urls');
19
- const { buildSupplementalChecks } = require('../supplemental-checks');
20
- const { buildStackChecks } = require('../stack-checks');
17
+ const { EMBEDDED_SECRET_PATTERNS, containsEmbeddedSecret } = require('../secret-patterns');
18
+ const { attachSourceUrls } = require('../source-urls');
19
+ const { buildSupplementalChecks } = require('../supplemental-checks');
20
+ const { buildStackChecks } = require('../stack-checks');
21
+ const { resolveProjectStateReadPath } = require('../state-paths');
21
22
 
22
23
  const GEMINI_SUPPLEMENTAL_SOURCE_URLS = {
23
24
  'testing-strategy': 'https://geminicli.com/docs/get-started/',
@@ -2062,23 +2063,23 @@ const GEMINI_TECHNIQUES = {
2062
2063
  },
2063
2064
 
2064
2065
  // CP-08: O. Repeat-Usage Hygiene (3 checks)
2065
- geminiSnapshotRetention: {
2066
- id: 'GM-O01', name: 'At least one prior audit snapshot exists',
2067
- check: (ctx) => { try { const fs = require('fs'); const p = require('path').join(ctx.dir, '.claude', 'claudex-setup', 'snapshots', 'index.json'); if (!fs.existsSync(p)) return null; const e = JSON.parse(fs.readFileSync(p, 'utf8')); return Array.isArray(e) && e.length > 0; } catch { return null; } },
2066
+ geminiSnapshotRetention: {
2067
+ id: 'GM-O01', name: 'At least one prior audit snapshot exists',
2068
+ check: (ctx) => { try { const fs = require('fs'); const p = resolveProjectStateReadPath(ctx.dir, 'snapshots', 'index.json'); if (!fs.existsSync(p)) return null; const e = JSON.parse(fs.readFileSync(p, 'utf8')); return Array.isArray(e) && e.length > 0; } catch { return null; } },
2068
2069
  impact: 'medium', rating: 3, category: 'repeat-usage',
2069
2070
  fix: 'Run `npx nerviq --platform gemini --snapshot` to save your first snapshot.',
2070
2071
  template: null, file: () => null, line: () => null,
2071
2072
  },
2072
- geminiFeedbackLoopHealth: {
2073
- id: 'GM-O02', name: 'Feedback loop functional when feedback submitted',
2074
- check: (ctx) => { try { const fs = require('fs'); const p = require('path').join(ctx.dir, '.claude', 'claudex-setup', 'outcomes', 'index.json'); if (!fs.existsSync(p)) return null; const e = JSON.parse(fs.readFileSync(p, 'utf8')); return Array.isArray(e) && e.length > 0; } catch { return null; } },
2073
+ geminiFeedbackLoopHealth: {
2074
+ id: 'GM-O02', name: 'Feedback loop functional when feedback submitted',
2075
+ check: (ctx) => { try { const fs = require('fs'); const p = resolveProjectStateReadPath(ctx.dir, 'outcomes', 'index.json'); if (!fs.existsSync(p)) return null; const e = JSON.parse(fs.readFileSync(p, 'utf8')); return Array.isArray(e) && e.length > 0; } catch { return null; } },
2075
2076
  impact: 'medium', rating: 3, category: 'repeat-usage',
2076
2077
  fix: 'Submit feedback using `npx nerviq --platform gemini feedback`.',
2077
2078
  template: null, file: () => null, line: () => null,
2078
2079
  },
2079
- geminiTrendDataAvailability: {
2080
- id: 'GM-O03', name: 'Trend data computable (2+ snapshots)',
2081
- check: (ctx) => { try { const fs = require('fs'); const p = require('path').join(ctx.dir, '.claude', 'claudex-setup', 'snapshots', 'index.json'); if (!fs.existsSync(p)) return null; const e = JSON.parse(fs.readFileSync(p, 'utf8')); return (Array.isArray(e) ? e : []).filter(x => x.snapshotKind === 'audit').length >= 2; } catch { return null; } },
2080
+ geminiTrendDataAvailability: {
2081
+ id: 'GM-O03', name: 'Trend data computable (2+ snapshots)',
2082
+ check: (ctx) => { try { const fs = require('fs'); const p = resolveProjectStateReadPath(ctx.dir, 'snapshots', 'index.json'); if (!fs.existsSync(p)) return null; const e = JSON.parse(fs.readFileSync(p, 'utf8')); return (Array.isArray(e) ? e : []).filter(x => x.snapshotKind === 'audit').length >= 2; } catch { return null; } },
2082
2083
  impact: 'low', rating: 2, category: 'repeat-usage',
2083
2084
  fix: 'Run at least 2 audits with --snapshot for trend tracking.',
2084
2085
  template: null, file: () => null, line: () => null,
@@ -8,69 +8,9 @@
8
8
  * Zero external dependencies - imports only from sibling/parent modules.
9
9
  */
10
10
 
11
- // ─── Platform Strength Matrix (from CLAUDEX research) ─────────────────────────
12
-
13
- const PLATFORM_STRENGTHS = {
14
- claude: {
15
- label: 'Claude Code',
16
- reasoning: 5,
17
- refactoring: 5,
18
- ci: 2,
19
- ide: 2,
20
- sandbox: 3,
21
- inline: 3,
22
- context: 4,
23
- automation: 4,
24
- },
25
- codex: {
26
- label: 'Codex',
27
- reasoning: 4,
28
- ci: 5,
29
- cloud: 5,
30
- ide: 3,
31
- sandbox: 4,
32
- refactoring: 4,
33
- inline: 2,
34
- context: 3,
35
- automation: 4,
36
- },
37
- gemini: {
38
- label: 'Gemini CLI',
39
- reasoning: 4,
40
- context: 5,
41
- sandbox: 5,
42
- ci: 3,
43
- ide: 3,
44
- refactoring: 3,
45
- inline: 2,
46
- cloud: 4,
47
- automation: 3,
48
- },
49
- copilot: {
50
- label: 'GitHub Copilot',
51
- inline: 5,
52
- 'cloud-agent': 4,
53
- ide: 4,
54
- ci: 4,
55
- governance: 3,
56
- reasoning: 3,
57
- refactoring: 3,
58
- context: 3,
59
- automation: 3,
60
- },
61
- cursor: {
62
- label: 'Cursor',
63
- ide: 5,
64
- ui: 5,
65
- background: 4,
66
- automation: 4,
67
- reasoning: 3,
68
- refactoring: 3,
69
- inline: 4,
70
- context: 3,
71
- ci: 2,
72
- },
73
- };
11
+ // ─── Platform Strength Matrix (canonical source: shared/capabilities.js) ─────
12
+
13
+ const { PLATFORM_CAPABILITIES: PLATFORM_STRENGTHS } = require('../shared/capabilities');
74
14
 
75
15
  // ─── Task-type to platform-strength mapping ───────────────────────────────────
76
16
 
@@ -82,7 +22,7 @@ const TASK_TYPE_PROFILES = {
82
22
  },
83
23
  'ci-review': {
84
24
  label: 'CI / Async Review',
85
- requiredStrengths: { ci: 0.5, cloud: 0.3, automation: 0.2 },
25
+ requiredStrengths: { ci: 0.4, cloudTasks: 0.3, async: 0.2, automation: 0.1 },
86
26
  description: 'Asynchronous code review and CI-integrated workflows.',
87
27
  },
88
28
  'ui-work': {
@@ -97,7 +37,7 @@ const TASK_TYPE_PROFILES = {
97
37
  },
98
38
  'infrastructure': {
99
39
  label: 'Infrastructure',
100
- requiredStrengths: { sandbox: 0.4, cloud: 0.3, reasoning: 0.3 },
40
+ requiredStrengths: { sandbox: 0.4, cloudTasks: 0.3, reasoning: 0.3 },
101
41
  description: 'Infrastructure, DevOps, and sandbox-heavy workflows.',
102
42
  },
103
43
  'refactoring': {
@@ -42,6 +42,9 @@ const PLATFORM_AUDIT_MAP = {
42
42
  gemini: 'gemini',
43
43
  copilot: 'copilot',
44
44
  cursor: 'cursor',
45
+ windsurf: 'windsurf',
46
+ aider: 'aider',
47
+ opencode: 'opencode',
45
48
  };
46
49
 
47
50
  // ─── Cross-platform recommendations ────────────────────────────────────────