@nerviq/cli 1.2.3 → 1.2.6
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/bin/cli.js +105 -1
- package/package.json +2 -2
- package/src/activity.js +17 -13
- package/src/aider/activity.js +1 -1
- package/src/audit.js +19 -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 +15 -65
- package/src/harmony/audit.js +3 -0
- package/src/harmony/canon.js +122 -1
- package/src/harmony/cli.js +21 -0
- package/src/harmony/drift.js +2 -2
- package/src/harmony/memory.js +9 -8
- package/src/harmony/sync.js +18 -1
- 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 +3231 -2929
- package/src/windsurf/patch.js +1 -1
- package/src/windsurf/premium.js +2 -1
package/bin/cli.js
CHANGED
|
@@ -24,7 +24,7 @@ const COMMAND_ALIASES = {
|
|
|
24
24
|
gov: 'governance',
|
|
25
25
|
outcome: 'feedback',
|
|
26
26
|
};
|
|
27
|
-
const KNOWN_COMMANDS = ['audit', 'org', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'scan', 'feedback', 'doctor', 'convert', 'migrate', 'catalog', 'certify', 'serve', 'help', 'version'];
|
|
27
|
+
const KNOWN_COMMANDS = ['audit', 'org', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'scan', 'feedback', 'doctor', 'convert', 'migrate', 'catalog', 'certify', 'serve', 'harmony-audit', 'harmony-sync', 'harmony-drift', 'harmony-advise', 'harmony-watch', 'harmony-governance', 'synergy-report', 'help', 'version'];
|
|
28
28
|
|
|
29
29
|
function levenshtein(a, b) {
|
|
30
30
|
const matrix = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
|
|
@@ -312,6 +312,9 @@ const HELP = `
|
|
|
312
312
|
|
|
313
313
|
CROSS-PLATFORM
|
|
314
314
|
nerviq harmony-audit Drift detection across all active platforms
|
|
315
|
+
nerviq harmony-sync Preview cross-platform sync (dry run)
|
|
316
|
+
nerviq harmony-sync --fix Apply cross-platform sync (write files)
|
|
317
|
+
nerviq harmony-sync --json JSON output for CI/automation
|
|
315
318
|
nerviq synergy-report Multi-agent amplification opportunities
|
|
316
319
|
nerviq convert --from X --to Y Convert configs between platforms
|
|
317
320
|
nerviq migrate --platform X Platform version migration helper
|
|
@@ -408,6 +411,7 @@ async function main() {
|
|
|
408
411
|
lite: flags.includes('--lite'),
|
|
409
412
|
snapshot: flags.includes('--snapshot'),
|
|
410
413
|
feedback: flags.includes('--feedback'),
|
|
414
|
+
fix: flags.includes('--fix'),
|
|
411
415
|
dryRun: flags.includes('--dry-run'),
|
|
412
416
|
threshold: parsed.threshold !== null ? Number(parsed.threshold) : null,
|
|
413
417
|
out: parsed.out,
|
|
@@ -862,6 +866,106 @@ async function main() {
|
|
|
862
866
|
process.on('SIGINT', closeServer);
|
|
863
867
|
process.on('SIGTERM', closeServer);
|
|
864
868
|
return;
|
|
869
|
+
} else if (normalizedCommand === 'harmony-audit') {
|
|
870
|
+
const { runHarmonyAudit } = require('../src/harmony/cli');
|
|
871
|
+
await runHarmonyAudit(options);
|
|
872
|
+
process.exit(0);
|
|
873
|
+
} else if (normalizedCommand === 'harmony-sync') {
|
|
874
|
+
const { previewHarmonySync, applyHarmonySync } = require('../src/harmony/sync');
|
|
875
|
+
const dir = options.dir || process.cwd();
|
|
876
|
+
|
|
877
|
+
if (options.fix) {
|
|
878
|
+
// Apply mode: write files
|
|
879
|
+
const result = applyHarmonySync(dir);
|
|
880
|
+
if (options.json) {
|
|
881
|
+
console.log(JSON.stringify(result, null, 2));
|
|
882
|
+
} else {
|
|
883
|
+
console.log('');
|
|
884
|
+
console.log('\x1b[1m Harmony Sync — Apply\x1b[0m');
|
|
885
|
+
console.log('\x1b[2m ═══════════════════════════════════════\x1b[0m');
|
|
886
|
+
console.log('');
|
|
887
|
+
if (result.applied.length === 0 && result.skipped.length === 0) {
|
|
888
|
+
console.log(' \x1b[32mAll platforms are already in sync. Nothing to apply.\x1b[0m');
|
|
889
|
+
} else {
|
|
890
|
+
for (const item of result.applied) {
|
|
891
|
+
console.log(` \x1b[32m✓\x1b[0m ${item.action.padEnd(8)} ${item.platform.padEnd(12)} ${item.path}`);
|
|
892
|
+
}
|
|
893
|
+
for (const item of result.skipped) {
|
|
894
|
+
const reason = typeof item === 'string' ? item : (item.reason || item.path);
|
|
895
|
+
console.log(` \x1b[33m⚠\x1b[0m skipped ${reason}`);
|
|
896
|
+
}
|
|
897
|
+
console.log('');
|
|
898
|
+
if (result.summary) {
|
|
899
|
+
console.log(` Files: ${result.summary.totalFiles} (${result.summary.creates} created, ${result.summary.patches} patched)`);
|
|
900
|
+
console.log(` Platforms: ${result.summary.platforms.join(', ')}`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
904
|
+
console.log('');
|
|
905
|
+
for (const w of result.warnings) {
|
|
906
|
+
console.log(` \x1b[33m⚠\x1b[0m ${w}`);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
console.log('');
|
|
910
|
+
}
|
|
911
|
+
} else {
|
|
912
|
+
// Preview mode (dry run)
|
|
913
|
+
const plan = previewHarmonySync(dir);
|
|
914
|
+
if (options.json) {
|
|
915
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
916
|
+
} else {
|
|
917
|
+
console.log('');
|
|
918
|
+
console.log('\x1b[1m Harmony Sync — Preview\x1b[0m');
|
|
919
|
+
console.log('\x1b[2m ═══════════════════════════════════════\x1b[0m');
|
|
920
|
+
console.log('');
|
|
921
|
+
if (plan.files.length === 0) {
|
|
922
|
+
console.log(' \x1b[32mAll platforms are already in sync. No changes needed.\x1b[0m');
|
|
923
|
+
} else {
|
|
924
|
+
for (const file of plan.files) {
|
|
925
|
+
const actionColor = file.action === 'create' ? '\x1b[32m' : '\x1b[36m';
|
|
926
|
+
console.log(` ${actionColor}${file.action.padEnd(8)}\x1b[0m ${file.platform.padEnd(12)} ${file.path}`);
|
|
927
|
+
if (file.preview) {
|
|
928
|
+
console.log(` \x1b[2m${file.preview}\x1b[0m`);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
console.log('');
|
|
932
|
+
console.log(` Total: ${plan.summary.totalFiles} file(s) — ${plan.summary.creates} create, ${plan.summary.patches} patch`);
|
|
933
|
+
console.log(` Platforms: ${plan.summary.platforms.join(', ')}`);
|
|
934
|
+
if (plan.summary.recommendedTrust) {
|
|
935
|
+
console.log(` Recommended trust: ${plan.summary.recommendedTrust}`);
|
|
936
|
+
}
|
|
937
|
+
console.log('');
|
|
938
|
+
console.log(' Run \x1b[1mnerviq harmony-sync --fix\x1b[0m to apply these changes.');
|
|
939
|
+
}
|
|
940
|
+
if (plan.warnings && plan.warnings.length > 0) {
|
|
941
|
+
console.log('');
|
|
942
|
+
for (const w of plan.warnings) {
|
|
943
|
+
console.log(` \x1b[33m⚠\x1b[0m ${w}`);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
console.log('');
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
process.exit(0);
|
|
950
|
+
} else if (normalizedCommand === 'harmony-drift') {
|
|
951
|
+
const { runHarmonyDrift } = require('../src/harmony/cli');
|
|
952
|
+
await runHarmonyDrift(options);
|
|
953
|
+
process.exit(0);
|
|
954
|
+
} else if (normalizedCommand === 'harmony-advise') {
|
|
955
|
+
const { runHarmonyAdvise } = require('../src/harmony/cli');
|
|
956
|
+
await runHarmonyAdvise(options);
|
|
957
|
+
process.exit(0);
|
|
958
|
+
} else if (normalizedCommand === 'harmony-watch') {
|
|
959
|
+
const { runHarmonyWatch } = require('../src/harmony/cli');
|
|
960
|
+
await runHarmonyWatch(options);
|
|
961
|
+
} else if (normalizedCommand === 'harmony-governance') {
|
|
962
|
+
const { runHarmonyGovernance } = require('../src/harmony/cli');
|
|
963
|
+
await runHarmonyGovernance(options);
|
|
964
|
+
process.exit(0);
|
|
965
|
+
} else if (normalizedCommand === 'synergy-report') {
|
|
966
|
+
// Placeholder — synergy report is referenced but may not be implemented yet
|
|
967
|
+
console.log('\n Synergy report: coming soon.\n');
|
|
968
|
+
process.exit(0);
|
|
865
969
|
} else if (normalizedCommand === 'doctor') {
|
|
866
970
|
const { runDoctor } = require('../src/doctor');
|
|
867
971
|
const output = await runDoctor({ dir: options.dir, json: options.json, verbose: options.verbose });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nerviq/cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
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 =
|
|
37
|
-
const activityDir =
|
|
38
|
-
const rollbackDir =
|
|
39
|
-
const snapshotDir =
|
|
40
|
-
const outcomesDir =
|
|
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 .
|
|
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 =
|
|
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'));
|
|
@@ -228,7 +228,11 @@ function getHistory(dir, limit = 20) {
|
|
|
228
228
|
const entries = readSnapshotIndex(dir);
|
|
229
229
|
return entries
|
|
230
230
|
.filter(e => e.snapshotKind === 'audit')
|
|
231
|
-
.sort((a, b) =>
|
|
231
|
+
.sort((a, b) => {
|
|
232
|
+
const dateDiff = new Date(b.createdAt) - new Date(a.createdAt);
|
|
233
|
+
if (dateDiff !== 0) return dateDiff;
|
|
234
|
+
return (b.id || '').localeCompare(a.id || '');
|
|
235
|
+
})
|
|
232
236
|
.slice(0, limit);
|
|
233
237
|
}
|
|
234
238
|
|
|
@@ -344,7 +348,7 @@ function exportTrendReport(dir) {
|
|
|
344
348
|
}
|
|
345
349
|
|
|
346
350
|
function readOutcomeIndex(dir) {
|
|
347
|
-
const indexPath =
|
|
351
|
+
const indexPath = resolveProjectStateReadPath(dir, 'outcomes', 'index.json');
|
|
348
352
|
if (!fs.existsSync(indexPath)) return [];
|
|
349
353
|
try {
|
|
350
354
|
const entries = JSON.parse(fs.readFileSync(indexPath, 'utf8'));
|
package/src/aider/activity.js
CHANGED
|
@@ -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');
|
package/src/audit.js
CHANGED
|
@@ -1279,7 +1279,25 @@ async function audit(options) {
|
|
|
1279
1279
|
console.log('');
|
|
1280
1280
|
}
|
|
1281
1281
|
|
|
1282
|
-
|
|
1282
|
+
// Cross-platform synergy hint
|
|
1283
|
+
try {
|
|
1284
|
+
const { detectActivePlatforms } = require('./harmony/canon');
|
|
1285
|
+
const { analyzeCompensation } = require('./synergy/compensation');
|
|
1286
|
+
const { calculateSynergyScore } = require('./synergy/ranking');
|
|
1287
|
+
const detected = detectActivePlatforms(options.dir);
|
|
1288
|
+
const activePlatforms = (detected || []).filter(p => p.detected).map(p => p.platform);
|
|
1289
|
+
if (activePlatforms.length >= 2) {
|
|
1290
|
+
const comp = analyzeCompensation(activePlatforms);
|
|
1291
|
+
const synergyScore = calculateSynergyScore(activePlatforms);
|
|
1292
|
+
console.log(colorize(` Cross-platform synergy: ${activePlatforms.length} platforms detected`, 'blue'));
|
|
1293
|
+
console.log(colorize(` Platforms: ${activePlatforms.join(', ')}`, 'dim'));
|
|
1294
|
+
console.log(colorize(` Compensations: ${comp.compensations.length} | Gaps: ${comp.uncoveredGaps.length}`, 'dim'));
|
|
1295
|
+
console.log(colorize(` Run: npx nerviq harmony-audit for full cross-platform analysis`, 'dim'));
|
|
1296
|
+
console.log('');
|
|
1297
|
+
}
|
|
1298
|
+
} catch { /* synergy display is optional */ }
|
|
1299
|
+
|
|
1300
|
+
console.log(colorize(` Backed by NERVIQ research and evidence for ${spec.platformLabel}`, 'dim'));
|
|
1283
1301
|
console.log(colorize(' https://github.com/nerviq/nerviq', 'dim'));
|
|
1284
1302
|
console.log('');
|
|
1285
1303
|
|
package/src/claudex-sync.json
CHANGED
package/src/codex/activity.js
CHANGED
|
@@ -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');
|
package/src/codex/patch.js
CHANGED
|
@@ -166,7 +166,7 @@ function applyPatch(dir, filePath, patchFn, options = {}) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// Backup + write
|
|
169
|
-
const backupPath = fullPath + '.
|
|
169
|
+
const backupPath = fullPath + '.nerviq-backup';
|
|
170
170
|
fs.writeFileSync(backupPath, original, 'utf8');
|
|
171
171
|
fs.writeFileSync(fullPath, patched, 'utf8');
|
|
172
172
|
|
package/src/codex/techniques.js
CHANGED
|
@@ -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
|
-
|
|
3105
|
-
|
|
3106
|
-
const
|
|
3107
|
-
|
|
3108
|
-
|
|
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
|
-
|
|
3129
|
-
|
|
3130
|
-
const
|
|
3131
|
-
|
|
3132
|
-
|
|
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
|
-
|
|
3153
|
-
|
|
3154
|
-
const
|
|
3155
|
-
|
|
3156
|
-
|
|
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 {
|
package/src/copilot/patch.js
CHANGED
|
@@ -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 + '.
|
|
199
|
+
const backupPath = fullPath + '.nerviq-backup';
|
|
200
200
|
fs.writeFileSync(backupPath, original, 'utf8');
|
|
201
201
|
fs.writeFileSync(fullPath, patched, 'utf8');
|
|
202
202
|
|
package/src/copilot/premium.js
CHANGED
|
@@ -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 =
|
|
392
|
+
const snapshotDir = resolveProjectStateReadPath(dir, 'snapshots');
|
|
392
393
|
try {
|
|
393
394
|
const files = fs.readdirSync(snapshotDir)
|
|
394
395
|
.filter(f => f.endsWith('.json'))
|
package/src/cursor/patch.js
CHANGED
|
@@ -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 + '.
|
|
204
|
+
const backupPath = fullPath + '.nerviq-backup';
|
|
205
205
|
fs.writeFileSync(backupPath, original, 'utf8');
|
|
206
206
|
fs.writeFileSync(fullPath, patched, 'utf8');
|
|
207
207
|
|
package/src/cursor/premium.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 =
|
|
51
|
-
const files = fs.
|
|
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,
|
|
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,
|
|
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,
|
|
169
|
+
relativeDir: path.relative(dir, resolveFeedbackDir(dir)),
|
|
165
170
|
summary: getFeedbackSummary(dir),
|
|
166
171
|
};
|
|
167
172
|
}
|
package/src/gemini/activity.js
CHANGED
|
@@ -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');
|
package/src/gemini/patch.js
CHANGED
|
@@ -187,7 +187,7 @@ function applyPatch(dir, filePath, patchFn, options = {}) {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// Backup + write
|
|
190
|
-
const backupPath = fullPath + '.
|
|
190
|
+
const backupPath = fullPath + '.nerviq-backup';
|
|
191
191
|
fs.writeFileSync(backupPath, original, 'utf8');
|
|
192
192
|
fs.writeFileSync(fullPath, patched, 'utf8');
|
|
193
193
|
|
package/src/gemini/premium.js
CHANGED
|
@@ -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/.
|
|
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 =
|
|
677
|
+
const snapshotDir = resolveGeminiStateReadPath(dir, 'snapshots');
|
|
677
678
|
try {
|
|
678
679
|
const files = fs.readdirSync(snapshotDir)
|
|
679
680
|
.filter(f => f.endsWith('.json'))
|
package/src/gemini/techniques.js
CHANGED
|
@@ -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 =
|
|
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 =
|
|
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 =
|
|
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,
|