@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.
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Reads ALL platform config files from a single project and builds a unified
5
5
  * understanding of instructions, MCP servers, trust posture, and governance
6
- * across Claude, Codex, Gemini, Copilot, and Cursor.
6
+ * across Claude, Codex, Gemini, Copilot, Cursor, Windsurf, Aider, and OpenCode.
7
7
  */
8
8
 
9
9
  const fs = require('fs');
@@ -13,10 +13,17 @@ const { CodexProjectContext } = require('../codex/context');
13
13
  const { GeminiProjectContext } = require('../gemini/context');
14
14
  const { CopilotProjectContext } = require('../copilot/context');
15
15
  const { CursorProjectContext } = require('../cursor/context');
16
+ const { WindsurfProjectContext } = require('../windsurf/context');
17
+ const { AiderProjectContext } = require('../aider/context');
18
+ const { OpenCodeProjectContext } = require('../opencode/context');
16
19
  const { getCodexGovernanceSummary } = require('../codex/governance');
17
20
  const { getGeminiGovernanceSummary } = require('../gemini/governance');
18
21
  const { getCopilotGovernanceSummary } = require('../copilot/governance');
19
22
  const { getCursorGovernanceSummary } = require('../cursor/governance');
23
+ const { getWindsurfGovernanceSummary } = require('../windsurf/governance');
24
+ const { getAiderGovernanceSummary } = require('../aider/governance');
25
+ const { getOpenCodeGovernanceSummary } = require('../opencode/governance');
26
+ const { tryParseJsonc } = require('../opencode/config-parser');
20
27
 
21
28
  // ─── Platform detection signatures ──────────────────────────────────────────
22
29
 
@@ -96,6 +103,52 @@ const PLATFORM_SIGNATURES = {
96
103
  rulesDir: '.cursor/rules',
97
104
  hooksDir: null,
98
105
  },
106
+ windsurf: {
107
+ label: 'Windsurf',
108
+ detect: (dir) => {
109
+ try {
110
+ if (fs.existsSync(path.join(dir, '.windsurfrules'))) return true;
111
+ if (fs.existsSync(path.join(dir, '.windsurf'))) return true;
112
+ return false;
113
+ } catch { return false; }
114
+ },
115
+ instructionFiles: ['.windsurfrules'],
116
+ configFiles: ['.windsurfrules', '.windsurf/mcp.json', '.cascadeignore'],
117
+ mcpFiles: ['.windsurf/mcp.json'],
118
+ rulesDir: '.windsurf/rules',
119
+ hooksDir: '.windsurf/workflows',
120
+ },
121
+ aider: {
122
+ label: 'Aider',
123
+ detect: (dir) => {
124
+ try {
125
+ if (fs.existsSync(path.join(dir, '.aider.conf.yml'))) return true;
126
+ if (fs.existsSync(path.join(dir, '.aiderignore'))) return true;
127
+ return false;
128
+ } catch { return false; }
129
+ },
130
+ instructionFiles: ['CONVENTIONS.md', '.aider.conventions.md'],
131
+ configFiles: ['.aider.conf.yml', '.aider.model.settings.yml', '.aiderignore'],
132
+ mcpFiles: [],
133
+ rulesDir: null,
134
+ hooksDir: null,
135
+ },
136
+ opencode: {
137
+ label: 'OpenCode',
138
+ detect: (dir) => {
139
+ try {
140
+ if (fs.existsSync(path.join(dir, 'opencode.json'))) return true;
141
+ if (fs.existsSync(path.join(dir, 'opencode.jsonc'))) return true;
142
+ if (fs.existsSync(path.join(dir, '.opencode'))) return true;
143
+ return false;
144
+ } catch { return false; }
145
+ },
146
+ instructionFiles: ['AGENTS.md', 'CLAUDE.md'],
147
+ configFiles: ['opencode.json', 'opencode.jsonc'],
148
+ mcpFiles: ['opencode.json', 'opencode.jsonc'],
149
+ rulesDir: null,
150
+ hooksDir: null,
151
+ },
99
152
  };
100
153
 
101
154
  // ─── Context builders per platform ──────────────────────────────────────────
@@ -106,6 +159,9 @@ const CONTEXT_BUILDERS = {
106
159
  gemini: (dir) => new GeminiProjectContext(dir),
107
160
  copilot: (dir) => new CopilotProjectContext(dir),
108
161
  cursor: (dir) => new CursorProjectContext(dir),
162
+ windsurf: (dir) => new WindsurfProjectContext(dir),
163
+ aider: (dir) => new AiderProjectContext(dir),
164
+ opencode: (dir) => new OpenCodeProjectContext(dir),
109
165
  };
110
166
 
111
167
  const GOVERNANCE_GETTERS = {
@@ -113,6 +169,9 @@ const GOVERNANCE_GETTERS = {
113
169
  gemini: getGeminiGovernanceSummary,
114
170
  copilot: getCopilotGovernanceSummary,
115
171
  cursor: getCursorGovernanceSummary,
172
+ windsurf: getWindsurfGovernanceSummary,
173
+ aider: getAiderGovernanceSummary,
174
+ opencode: getOpenCodeGovernanceSummary,
116
175
  };
117
176
 
118
177
  // ─── Helpers ────────────────────────────────────────────────────────────────
@@ -133,6 +192,11 @@ function safeParseJson(content) {
133
192
  }
134
193
  }
135
194
 
195
+ function safeParseJsonc(content) {
196
+ const parsed = tryParseJsonc(content);
197
+ return parsed.ok ? parsed.data : null;
198
+ }
199
+
136
200
  /**
137
201
  * Extract instruction text from a platform's instruction files.
138
202
  * Returns array of { file, content } for each found file.
@@ -177,6 +241,17 @@ function extractMcpFromMcpJson(content) {
177
241
  }));
178
242
  }
179
243
 
244
+ function extractMcpFromOpenCodeConfig(content) {
245
+ const json = safeParseJsonc(content);
246
+ if (!json) return [];
247
+ const servers = json.mcpServers || json.servers || {};
248
+ return Object.keys(servers).map(name => ({
249
+ name,
250
+ command: servers[name].command || null,
251
+ args: servers[name].args || [],
252
+ }));
253
+ }
254
+
180
255
  /**
181
256
  * Extract MCP server names from Gemini settings.json.
182
257
  */
@@ -205,6 +280,8 @@ function readMcpServers(dir, platform, mcpFiles) {
205
280
  extracted = extractMcpFromClaudeSettings(content);
206
281
  } else if (platform === 'gemini') {
207
282
  extracted = extractMcpFromGeminiSettings(content);
283
+ } else if (platform === 'opencode') {
284
+ extracted = extractMcpFromOpenCodeConfig(content);
208
285
  } else {
209
286
  extracted = extractMcpFromMcpJson(content);
210
287
  }
@@ -268,6 +345,27 @@ function detectTrustPosture(platform, ctx) {
268
345
  return 'no-sandbox';
269
346
  }
270
347
 
348
+ if (platform === 'windsurf') {
349
+ if (ctx.fileContent('.cascadeignore')) return 'guarded';
350
+ if (ctx.fileContent('.windsurf/mcp.json')) return 'team-managed';
351
+ return 'foreground';
352
+ }
353
+
354
+ if (platform === 'aider') {
355
+ const config = ctx.configContent ? (ctx.configContent() || '') : '';
356
+ if (/^\s*(yes|yes-always)\s*:\s*true\b/m.test(config)) return 'full-auto';
357
+ if (/^\s*auto-commits\s*:\s*true\b/m.test(config)) return 'git-guarded';
358
+ return 'manual-review';
359
+ }
360
+
361
+ if (platform === 'opencode') {
362
+ const config = ctx.configContent ? (ctx.configContent() || '') : '';
363
+ if (/"\*"\s*:\s*"allow"/.test(config)) return 'unrestricted';
364
+ if (/"(?:bash|edit|task|external_directory)"\s*:\s*"deny"/.test(config)) return 'locked-down';
365
+ if (/"(?:bash|edit|task|external_directory)"\s*:\s*"ask"/.test(config)) return 'prompted';
366
+ return 'standard';
367
+ }
368
+
271
369
  return 'unknown';
272
370
  }
273
371
 
@@ -68,6 +68,27 @@ function collectPlatformAudits(dir) {
68
68
  if (result) results.push({ platform: 'cursor', ...result });
69
69
  } catch (_e) { /* platform not available */ }
70
70
 
71
+ // Try Windsurf audit
72
+ try {
73
+ const { audit } = require('../audit');
74
+ const result = audit({ dir, silent: true, platform: 'windsurf' });
75
+ if (result) results.push({ platform: 'windsurf', ...result });
76
+ } catch (_e) { /* platform not available */ }
77
+
78
+ // Try Aider audit
79
+ try {
80
+ const { audit } = require('../audit');
81
+ const result = audit({ dir, silent: true, platform: 'aider' });
82
+ if (result) results.push({ platform: 'aider', ...result });
83
+ } catch (_e) { /* platform not available */ }
84
+
85
+ // Try OpenCode audit
86
+ try {
87
+ const { audit } = require('../audit');
88
+ const result = audit({ dir, silent: true, platform: 'opencode' });
89
+ if (result) results.push({ platform: 'opencode', ...result });
90
+ } catch (_e) { /* platform not available */ }
91
+
71
92
  return results;
72
93
  }
73
94
 
@@ -97,7 +97,7 @@ function detectMcpDrift(model) {
97
97
  const drifts = [];
98
98
  const allPlatforms = model.activePlatforms.map(p => p.platform);
99
99
  const mcpCapablePlatforms = allPlatforms.filter(p =>
100
- p === 'claude' || p === 'gemini' || p === 'copilot' || p === 'cursor'
100
+ p === 'claude' || p === 'gemini' || p === 'copilot' || p === 'cursor' || p === 'windsurf' || p === 'opencode'
101
101
  );
102
102
 
103
103
  if (mcpCapablePlatforms.length < 2) return drifts;
@@ -150,7 +150,7 @@ function detectRuleDrift(model) {
150
150
 
151
151
  // Platforms that support rules: claude (.claude/rules), copilot (.github/instructions), cursor (.cursor/rules)
152
152
  const ruleCapable = allPlatforms.filter(p =>
153
- p === 'claude' || p === 'copilot' || p === 'cursor'
153
+ p === 'claude' || p === 'copilot' || p === 'cursor' || p === 'windsurf'
154
154
  );
155
155
 
156
156
  if (ruleCapable.length < 2) return drifts;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * H7. Shared Memory / Knowledge Layer
3
3
  *
4
- * Cross-platform knowledge storage in .claudex/harmony/ directory.
4
+ * Cross-platform knowledge storage in .nerviq/harmony/ directory.
5
5
  * Persists canonical models, drift history, platform scores,
6
6
  * recommendation outcomes, and routing history.
7
7
  *
@@ -10,8 +10,12 @@
10
10
 
11
11
  const fs = require('fs');
12
12
  const path = require('path');
13
+ const {
14
+ resolveHarmonyStateReadPath,
15
+ ensureHarmonyStateDir,
16
+ } = require('../state-paths');
13
17
 
14
- const HARMONY_DIR = '.claudex/harmony';
18
+ const HARMONY_DIR = '.nerviq/harmony';
15
19
 
16
20
  const STATE_FILES = {
17
21
  canon: 'canon.json',
@@ -24,9 +28,7 @@ const STATE_FILES = {
24
28
  // ─── File I/O helpers ─────────────────────────────────────────────────────────
25
29
 
26
30
  function ensureHarmonyDir(dir) {
27
- const harmonyPath = path.join(dir, HARMONY_DIR);
28
- fs.mkdirSync(harmonyPath, { recursive: true });
29
- return harmonyPath;
31
+ return ensureHarmonyStateDir(dir);
30
32
  }
31
33
 
32
34
  function readJsonSafe(filePath) {
@@ -78,11 +80,10 @@ function saveHarmonyState(dir, state) {
78
80
  * @returns {Object} State object with all available keys populated
79
81
  */
80
82
  function loadHarmonyState(dir) {
81
- const harmonyPath = path.join(dir, HARMONY_DIR);
82
83
  const state = {};
83
84
 
84
85
  for (const [key, filename] of Object.entries(STATE_FILES)) {
85
- const filePath = path.join(harmonyPath, filename);
86
+ const filePath = resolveHarmonyStateReadPath(dir, filename);
86
87
  const data = readJsonSafe(filePath);
87
88
  if (data !== null) {
88
89
  state[key] = data;
@@ -90,7 +91,7 @@ function loadHarmonyState(dir) {
90
91
  }
91
92
 
92
93
  // Load manifest for metadata
93
- const manifest = readJsonSafe(path.join(harmonyPath, 'manifest.json'));
94
+ const manifest = readJsonSafe(resolveHarmonyStateReadPath(dir, 'manifest.json'));
94
95
  if (manifest) {
95
96
  state._manifest = manifest;
96
97
  }
package/src/insights.js CHANGED
@@ -21,7 +21,7 @@
21
21
  const https = require('https');
22
22
  const os = require('os');
23
23
 
24
- const INSIGHTS_ENDPOINT = 'https://claudex-insights.claudex.workers.dev/v1/report';
24
+ const INSIGHTS_ENDPOINT = 'https://insights.nerviq.net/v1/report';
25
25
  const TIMEOUT_MS = 3000;
26
26
 
27
27
  function shouldCollect() {
@@ -5,7 +5,7 @@
5
5
  * Provides: history, compare, trend, watch, feedback, insights.
6
6
  *
7
7
  * OpenCode snapshots are stored alongside Claude snapshots in
8
- * .claude/claudex-setup/snapshots/ but filtered by platform='opencode'.
8
+ * .nerviq/snapshots/ (legacy: .claude/claudex-setup/snapshots/) but filtered by platform='opencode'.
9
9
  */
10
10
 
11
11
  const path = require('path');
@@ -142,7 +142,7 @@ function applyPatch(dir, filePath, patchFn, options = {}) {
142
142
  return { success: true, reason: 'dry run', preview, unchanged: false };
143
143
  }
144
144
 
145
- const backupPath = fullPath + '.claudex-backup';
145
+ const backupPath = fullPath + '.nerviq-backup';
146
146
  fs.writeFileSync(backupPath, original, 'utf8');
147
147
  fs.writeFileSync(fullPath, patched, 'utf8');
148
148
 
@@ -21,11 +21,12 @@
21
21
  */
22
22
 
23
23
  const os = require('os');
24
- const path = require('path');
25
- const { EMBEDDED_SECRET_PATTERNS, containsEmbeddedSecret } = require('../secret-patterns');
26
- const { attachSourceUrls } = require('../source-urls');
27
- const { buildStackChecks } = require('../stack-checks');
28
- const { isApiProject, isDatabaseProject, isAuthProject, isMonitoringRelevant } = require('../supplemental-checks');
24
+ const path = require('path');
25
+ const { EMBEDDED_SECRET_PATTERNS, containsEmbeddedSecret } = require('../secret-patterns');
26
+ const { attachSourceUrls } = require('../source-urls');
27
+ const { buildStackChecks } = require('../stack-checks');
28
+ const { isApiProject, isDatabaseProject, isAuthProject, isMonitoringRelevant } = require('../supplemental-checks');
29
+ const { resolveProjectStateReadPath } = require('../state-paths');
29
30
 
30
31
  const DEFAULT_PROJECT_DOC_MAX_BYTES = 32768;
31
32
 
@@ -1458,14 +1459,15 @@ const OPENCODE_TECHNIQUES = {
1458
1459
  line: () => null,
1459
1460
  },
1460
1461
 
1461
- opencodePilotEvidence: {
1462
- id: 'OC-M03',
1463
- name: 'OpenCode setup has been audited at least once',
1464
- check: (ctx) => {
1465
- // Check for nerviq activity artifacts
1466
- const hasArtifacts = ctx.hasDir('.claude/claudex-setup');
1467
- return hasArtifacts ? true : null;
1468
- },
1462
+ opencodePilotEvidence: {
1463
+ id: 'OC-M03',
1464
+ name: 'OpenCode setup has been audited at least once',
1465
+ check: (ctx) => {
1466
+ // Check for nerviq activity artifacts
1467
+ const fs = require('fs');
1468
+ const hasArtifacts = fs.existsSync(resolveProjectStateReadPath(ctx.dir));
1469
+ return hasArtifacts ? true : null;
1470
+ },
1469
1471
  impact: 'low',
1470
1472
  rating: 2,
1471
1473
  category: 'governance',
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Canonical Platform Capability Matrix
3
+ *
4
+ * Single source of truth for platform strengths/weaknesses.
5
+ * Used by: harmony/advisor.js, synergy/routing.js, synergy/compensation.js
6
+ *
7
+ * Scale: 1 (weak) to 5 (best-in-class)
8
+ * All keys use camelCase for consistency.
9
+ */
10
+
11
+ const PLATFORM_CAPABILITIES = {
12
+ claude: {
13
+ label: 'Claude Code',
14
+ reasoning: 5,
15
+ refactoring: 5,
16
+ debugging: 5,
17
+ review: 5,
18
+ architecture: 5,
19
+ ci: 2,
20
+ ide: 2,
21
+ ui: 1,
22
+ async: 3,
23
+ inline: 3,
24
+ context: 4,
25
+ automation: 4,
26
+ sandbox: 3,
27
+ cloudTasks: 3,
28
+ cloudAgent: 2,
29
+ background: 3,
30
+ governance: 4,
31
+ },
32
+ codex: {
33
+ label: 'Codex',
34
+ reasoning: 4,
35
+ refactoring: 4,
36
+ debugging: 3,
37
+ review: 4,
38
+ architecture: 3,
39
+ ci: 5,
40
+ ide: 3,
41
+ ui: 2,
42
+ async: 5,
43
+ inline: 2,
44
+ context: 3,
45
+ automation: 4,
46
+ sandbox: 4,
47
+ cloudTasks: 5,
48
+ cloudAgent: 4,
49
+ background: 4,
50
+ governance: 3,
51
+ },
52
+ gemini: {
53
+ label: 'Gemini CLI',
54
+ reasoning: 4,
55
+ refactoring: 3,
56
+ debugging: 3,
57
+ review: 3,
58
+ architecture: 3,
59
+ ci: 3,
60
+ ide: 3,
61
+ ui: 2,
62
+ async: 4,
63
+ inline: 2,
64
+ context: 5,
65
+ automation: 3,
66
+ sandbox: 5,
67
+ cloudTasks: 4,
68
+ cloudAgent: 3,
69
+ background: 3,
70
+ governance: 2,
71
+ },
72
+ copilot: {
73
+ label: 'GitHub Copilot',
74
+ reasoning: 3,
75
+ refactoring: 3,
76
+ debugging: 3,
77
+ review: 3,
78
+ architecture: 2,
79
+ ci: 4,
80
+ ide: 4,
81
+ ui: 3,
82
+ async: 3,
83
+ inline: 5,
84
+ context: 3,
85
+ automation: 3,
86
+ sandbox: 2,
87
+ cloudTasks: 4,
88
+ cloudAgent: 4,
89
+ background: 3,
90
+ governance: 3,
91
+ },
92
+ cursor: {
93
+ label: 'Cursor',
94
+ reasoning: 3,
95
+ refactoring: 4,
96
+ debugging: 4,
97
+ review: 3,
98
+ architecture: 3,
99
+ ci: 2,
100
+ ide: 5,
101
+ ui: 5,
102
+ async: 2,
103
+ inline: 4,
104
+ context: 3,
105
+ automation: 4,
106
+ sandbox: 2,
107
+ cloudTasks: 2,
108
+ cloudAgent: 2,
109
+ background: 4,
110
+ governance: 2,
111
+ },
112
+ windsurf: {
113
+ label: 'Windsurf',
114
+ reasoning: 4,
115
+ refactoring: 4,
116
+ debugging: 4,
117
+ review: 3,
118
+ architecture: 3,
119
+ ci: 2,
120
+ ide: 5,
121
+ ui: 4,
122
+ async: 3,
123
+ inline: 4,
124
+ context: 3,
125
+ automation: 4,
126
+ sandbox: 2,
127
+ cloudTasks: 2,
128
+ cloudAgent: 2,
129
+ background: 2,
130
+ governance: 3,
131
+ },
132
+ aider: {
133
+ label: 'Aider',
134
+ reasoning: 4,
135
+ refactoring: 5,
136
+ debugging: 4,
137
+ review: 3,
138
+ architecture: 3,
139
+ ci: 4,
140
+ ide: 1,
141
+ ui: 1,
142
+ async: 2,
143
+ inline: 1,
144
+ context: 2,
145
+ automation: 2,
146
+ sandbox: 1,
147
+ cloudTasks: 1,
148
+ cloudAgent: 1,
149
+ background: 1,
150
+ governance: 2,
151
+ },
152
+ opencode: {
153
+ label: 'OpenCode',
154
+ reasoning: 4,
155
+ refactoring: 4,
156
+ debugging: 4,
157
+ review: 4,
158
+ architecture: 4,
159
+ ci: 4,
160
+ ide: 3,
161
+ ui: 2,
162
+ async: 4,
163
+ inline: 2,
164
+ context: 4,
165
+ automation: 4,
166
+ sandbox: 4,
167
+ cloudTasks: 3,
168
+ cloudAgent: 3,
169
+ background: 3,
170
+ governance: 4,
171
+ },
172
+ };
173
+
174
+ const CAPABILITY_LABELS = {
175
+ reasoning: 'Complex reasoning & analysis',
176
+ refactoring: 'Code refactoring',
177
+ debugging: 'Bug diagnosis & debugging',
178
+ review: 'Code review',
179
+ architecture: 'Architecture design',
180
+ ci: 'CI/CD pipeline integration',
181
+ ide: 'IDE integration',
182
+ ui: 'UI/frontend work',
183
+ async: 'Async/background tasks',
184
+ inline: 'Inline code completion',
185
+ context: 'Large context handling',
186
+ automation: 'Workflow automation',
187
+ sandbox: 'Sandboxed execution',
188
+ cloudTasks: 'Cloud-based tasks',
189
+ cloudAgent: 'Cloud agent mode',
190
+ background: 'Background processing',
191
+ governance: 'Governance & permissions',
192
+ };
193
+
194
+ module.exports = { PLATFORM_CAPABILITIES, CAPABILITY_LABELS };
@@ -0,0 +1,85 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const PROJECT_STATE_ROOT = ['.nerviq'];
5
+ const LEGACY_PROJECT_STATE_ROOT = ['.claude', 'claudex-setup'];
6
+ const HARMONY_STATE_ROOT = ['.nerviq', 'harmony'];
7
+ const LEGACY_HARMONY_STATE_ROOT = ['.claudex', 'harmony'];
8
+ const GEMINI_STATE_ROOT = ['.gemini', '.nerviq'];
9
+ const LEGACY_GEMINI_STATE_ROOT = ['.gemini', '.claudex'];
10
+
11
+ function buildPath(dir, rootSegments, segments) {
12
+ return path.join(dir, ...rootSegments, ...segments);
13
+ }
14
+
15
+ function resolveReadablePath(dir, preferredRoot, legacyRoot, ...segments) {
16
+ const preferredPath = buildPath(dir, preferredRoot, segments);
17
+ if (fs.existsSync(preferredPath)) return preferredPath;
18
+
19
+ const legacyPath = buildPath(dir, legacyRoot, segments);
20
+ if (fs.existsSync(legacyPath)) return legacyPath;
21
+
22
+ return preferredPath;
23
+ }
24
+
25
+ function ensureWritableDir(dir, rootSegments, ...segments) {
26
+ const targetPath = buildPath(dir, rootSegments, segments);
27
+ fs.mkdirSync(targetPath, { recursive: true });
28
+ return targetPath;
29
+ }
30
+
31
+ function resolveProjectStatePath(dir, ...segments) {
32
+ return buildPath(dir, PROJECT_STATE_ROOT, segments);
33
+ }
34
+
35
+ function resolveProjectStateReadPath(dir, ...segments) {
36
+ return resolveReadablePath(dir, PROJECT_STATE_ROOT, LEGACY_PROJECT_STATE_ROOT, ...segments);
37
+ }
38
+
39
+ function ensureProjectStateDir(dir, ...segments) {
40
+ return ensureWritableDir(dir, PROJECT_STATE_ROOT, ...segments);
41
+ }
42
+
43
+ function resolveHarmonyStatePath(dir, ...segments) {
44
+ return buildPath(dir, HARMONY_STATE_ROOT, segments);
45
+ }
46
+
47
+ function resolveHarmonyStateReadPath(dir, ...segments) {
48
+ return resolveReadablePath(dir, HARMONY_STATE_ROOT, LEGACY_HARMONY_STATE_ROOT, ...segments);
49
+ }
50
+
51
+ function ensureHarmonyStateDir(dir, ...segments) {
52
+ return ensureWritableDir(dir, HARMONY_STATE_ROOT, ...segments);
53
+ }
54
+
55
+ function resolveGeminiStatePath(dir, ...segments) {
56
+ return buildPath(dir, GEMINI_STATE_ROOT, segments);
57
+ }
58
+
59
+ function resolveGeminiStateReadPath(dir, ...segments) {
60
+ return resolveReadablePath(dir, GEMINI_STATE_ROOT, LEGACY_GEMINI_STATE_ROOT, ...segments);
61
+ }
62
+
63
+ function ensureGeminiStateDir(dir, ...segments) {
64
+ return ensureWritableDir(dir, GEMINI_STATE_ROOT, ...segments);
65
+ }
66
+
67
+ module.exports = {
68
+ PROJECT_STATE_ROOT,
69
+ LEGACY_PROJECT_STATE_ROOT,
70
+ HARMONY_STATE_ROOT,
71
+ LEGACY_HARMONY_STATE_ROOT,
72
+ GEMINI_STATE_ROOT,
73
+ LEGACY_GEMINI_STATE_ROOT,
74
+ resolveReadablePath,
75
+ ensureWritableDir,
76
+ resolveProjectStatePath,
77
+ resolveProjectStateReadPath,
78
+ ensureProjectStateDir,
79
+ resolveHarmonyStatePath,
80
+ resolveHarmonyStateReadPath,
81
+ ensureHarmonyStateDir,
82
+ resolveGeminiStatePath,
83
+ resolveGeminiStateReadPath,
84
+ ensureGeminiStateDir,
85
+ };
@@ -5,26 +5,7 @@
5
5
  * platform additions to fill coverage gaps.
6
6
  */
7
7
 
8
- const { PLATFORM_CAPABILITIES } = require('./routing');
9
-
10
- const AREA_LABELS = {
11
- reasoning: 'Complex reasoning & analysis',
12
- refactoring: 'Code refactoring',
13
- debugging: 'Bug diagnosis & debugging',
14
- CI: 'CI/CD pipeline integration',
15
- IDE: 'IDE integration',
16
- UI: 'UI/frontend work',
17
- async: 'Async/background tasks',
18
- review: 'Code review',
19
- architecture: 'Architecture design',
20
- inline: 'Inline code completion',
21
- cloudTasks: 'Cloud-based tasks',
22
- context: 'Large context handling',
23
- sandbox: 'Sandboxed execution',
24
- cloudAgent: 'Cloud agent mode',
25
- background: 'Background processing',
26
- automation: 'Workflow automation',
27
- };
8
+ const { PLATFORM_CAPABILITIES, CAPABILITY_LABELS: AREA_LABELS } = require('../shared/capabilities');
28
9
 
29
10
  const WEAKNESS_THRESHOLD = 3;
30
11
  const STRENGTH_THRESHOLD = 4;