@nerviq/cli 1.2.2 → 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/README.md +1 -0
- package/package.json +2 -2
- package/src/activity.js +12 -12
- package/src/aider/activity.js +1 -1
- package/src/claudex-sync.json +3 -3
- package/src/codex/activity.js +1 -1
- package/src/codex/patch.js +1 -1
- package/src/codex/techniques.js +32 -34
- package/src/copilot/patch.js +1 -1
- package/src/copilot/premium.js +2 -1
- package/src/cursor/patch.js +1 -1
- package/src/cursor/premium.js +2 -1
- package/src/feedback.js +13 -8
- package/src/gemini/activity.js +1 -1
- package/src/gemini/patch.js +1 -1
- package/src/gemini/premium.js +3 -2
- package/src/gemini/techniques.js +14 -13
- package/src/harmony/advisor.js +5 -65
- package/src/harmony/audit.js +3 -0
- package/src/harmony/canon.js +99 -1
- package/src/harmony/cli.js +21 -0
- package/src/harmony/drift.js +2 -2
- package/src/harmony/memory.js +9 -8
- package/src/insights.js +1 -1
- package/src/opencode/activity.js +1 -1
- package/src/opencode/patch.js +1 -1
- package/src/opencode/techniques.js +15 -13
- package/src/shared/capabilities.js +194 -0
- package/src/state-paths.js +85 -0
- package/src/synergy/compensation.js +1 -20
- package/src/synergy/learning.js +20 -5
- package/src/synergy/routing.js +8 -14
- package/src/techniques.js +2981 -2929
- package/src/windsurf/patch.js +1 -1
- package/src/windsurf/premium.js +2 -1
package/src/harmony/canon.js
CHANGED
|
@@ -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
|
|
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
|
|
package/src/harmony/cli.js
CHANGED
|
@@ -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
|
|
package/src/harmony/drift.js
CHANGED
|
@@ -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;
|
package/src/harmony/memory.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* H7. Shared Memory / Knowledge Layer
|
|
3
3
|
*
|
|
4
|
-
* Cross-platform knowledge storage in .
|
|
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 = '.
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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://
|
|
24
|
+
const INSIGHTS_ENDPOINT = 'https://insights.nerviq.net/v1/report';
|
|
25
25
|
const TIMEOUT_MS = 3000;
|
|
26
26
|
|
|
27
27
|
function shouldCollect() {
|
package/src/opencode/activity.js
CHANGED
|
@@ -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');
|
package/src/opencode/patch.js
CHANGED
|
@@ -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 + '.
|
|
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
|
|
1467
|
-
|
|
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('
|
|
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;
|