@nerviq/cli 1.29.0 → 1.29.1
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/CHANGELOG.md +1527 -1493
- package/README.md +550 -538
- package/SECURITY.md +82 -82
- package/bin/cli.js +2562 -2558
- package/docs/api-reference.md +356 -356
- package/docs/audit-fix.md +109 -0
- package/docs/autofix.md +3 -62
- package/docs/getting-started.md +1 -1
- package/docs/index.html +592 -592
- package/docs/integration-contracts.md +287 -287
- package/docs/maintenance.md +128 -128
- package/docs/new-platform-guide.md +202 -202
- package/docs/release-process.md +63 -0
- package/docs/shallow-risk.md +244 -244
- package/docs/why-nerviq.md +82 -82
- package/package.json +67 -67
- package/src/aider/activity.js +226 -226
- package/src/aider/context.js +162 -162
- package/src/aider/freshness.js +123 -123
- package/src/aider/techniques.js +3465 -3465
- package/src/audit/layers.js +180 -180
- package/src/audit.js +1032 -1032
- package/src/benchmark.js +299 -299
- package/src/codex/activity.js +324 -324
- package/src/codex/freshness.js +142 -142
- package/src/codex/techniques.js +4895 -4895
- package/src/context.js +326 -326
- package/src/continuous-ops.js +11 -1
- package/src/convert.js +340 -340
- package/src/copilot/config-parser.js +280 -280
- package/src/copilot/context.js +218 -218
- package/src/copilot/freshness.js +177 -177
- package/src/copilot/patch.js +238 -238
- package/src/copilot/techniques.js +3578 -3578
- package/src/cursor/freshness.js +194 -194
- package/src/cursor/patch.js +243 -243
- package/src/cursor/techniques.js +3735 -3735
- package/src/doctor.js +201 -201
- package/src/fix-engine.js +511 -8
- package/src/formatters/csv.js +86 -86
- package/src/formatters/junit.js +123 -123
- package/src/formatters/markdown.js +164 -164
- package/src/formatters/otel.js +151 -151
- package/src/freshness.js +156 -156
- package/src/gemini/activity.js +402 -402
- package/src/gemini/context.js +290 -290
- package/src/gemini/freshness.js +183 -183
- package/src/gemini/patch.js +229 -229
- package/src/gemini/techniques.js +3811 -3811
- package/src/governance.js +533 -533
- package/src/harmony/audit.js +306 -306
- package/src/i18n.js +63 -63
- package/src/insights.js +119 -119
- package/src/integrations.js +134 -134
- package/src/locales/en.json +33 -33
- package/src/locales/es.json +33 -33
- package/src/migrate.js +354 -354
- package/src/opencode/activity.js +286 -286
- package/src/opencode/freshness.js +137 -137
- package/src/opencode/techniques.js +3450 -3450
- package/src/setup/analysis.js +12 -12
- package/src/setup.js +7 -6
- package/src/shallow-risk/index.js +56 -56
- package/src/shallow-risk/patterns/agent-config-cross-platform-drift.js +50 -50
- package/src/shallow-risk/patterns/agent-config-dangerous-autoapprove.js +46 -46
- package/src/shallow-risk/patterns/agent-config-deprecated-keys.js +46 -46
- package/src/shallow-risk/patterns/agent-config-missing-file.js +317 -317
- package/src/shallow-risk/patterns/agent-config-secret-literal.js +49 -49
- package/src/shallow-risk/patterns/agent-config-stack-contradiction.js +34 -34
- package/src/shallow-risk/patterns/hook-script-missing.js +70 -70
- package/src/shallow-risk/patterns/mcp-server-no-allowlist.js +52 -52
- package/src/shallow-risk/shared.js +648 -648
- package/src/source-urls.js +295 -295
- package/src/state-paths.js +85 -85
- package/src/supplemental-checks.js +805 -805
- package/src/telemetry.js +160 -160
- package/src/windsurf/context.js +359 -359
- package/src/windsurf/freshness.js +194 -194
- package/src/windsurf/patch.js +231 -231
- package/src/windsurf/techniques.js +3779 -3779
package/src/telemetry.js
CHANGED
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Nerviq Opt-In Telemetry Foundation
|
|
3
|
-
*
|
|
4
|
-
* Collects anonymous usage events ONLY when NERVIQ_TELEMETRY=1 is set.
|
|
5
|
-
* No PII, no file contents, no absolute paths are ever stored.
|
|
6
|
-
* Events are stored locally in <projectDir>/.nerviq/telemetry.json.
|
|
7
|
-
*
|
|
8
|
-
* This module is the foundation layer — actual transmission to a dashboard
|
|
9
|
-
* is an explicit opt-in step configured separately.
|
|
10
|
-
*
|
|
11
|
-
* Privacy guarantees:
|
|
12
|
-
* - No usernames, emails, or identifiers
|
|
13
|
-
* - No file contents or code
|
|
14
|
-
* - No absolute paths (only hashed project fingerprint)
|
|
15
|
-
* - Stored only on local disk
|
|
16
|
-
* - Never sent anywhere without additional explicit configuration
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
'use strict';
|
|
20
|
-
|
|
21
|
-
const fs = require('fs');
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const os = require('os');
|
|
24
|
-
const crypto = require('crypto');
|
|
25
|
-
|
|
26
|
-
const TELEMETRY_FILE = path.join(os.homedir(), '.nerviq', 'telemetry.json');
|
|
27
|
-
const MAX_EVENTS = 500; // cap file size at ~500 events
|
|
28
|
-
|
|
29
|
-
// ─── Opt-in check ─────────────────────────────────────────────────────────────
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Returns true only when the user has explicitly set NERVIQ_TELEMETRY=1.
|
|
33
|
-
* Telemetry is opt-IN, not opt-out.
|
|
34
|
-
* @returns {boolean}
|
|
35
|
-
*/
|
|
36
|
-
function shouldCollectTelemetry() {
|
|
37
|
-
return process.env.NERVIQ_TELEMETRY === '1';
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ─── Anonymous fingerprinting ─────────────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Creates a one-way hash of the project directory.
|
|
44
|
-
* This allows grouping events by project without exposing the path.
|
|
45
|
-
* @param {string} dir
|
|
46
|
-
* @returns {string} 8-char hex fingerprint
|
|
47
|
-
*/
|
|
48
|
-
function hashProject(dir) {
|
|
49
|
-
try {
|
|
50
|
-
return crypto.createHash('sha256').update(dir).digest('hex').slice(0, 8);
|
|
51
|
-
} catch {
|
|
52
|
-
return 'unknown';
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// ─── Event collection ────────────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Collect an anonymous usage event and append it to the local telemetry file.
|
|
60
|
-
* Does nothing unless shouldCollectTelemetry() returns true.
|
|
61
|
-
*
|
|
62
|
-
* @param {string} event - Event name (e.g. 'audit', 'setup', 'convert')
|
|
63
|
-
* @param {object} [data] - Additional anonymous data
|
|
64
|
-
* @param {string} [data.platform] - Platform name (claude, codex, etc.)
|
|
65
|
-
* @param {number} [data.score] - Audit score
|
|
66
|
-
* @param {number} [data.checkCount] - Total checks evaluated
|
|
67
|
-
* @param {number} [data.durationMs] - Execution time in ms
|
|
68
|
-
* @param {string} [data.dir] - Project dir (hashed before storage)
|
|
69
|
-
* @returns {object|null} The recorded event object, or null if telemetry is off
|
|
70
|
-
*/
|
|
71
|
-
function collectAnonymousEvent(event, data = {}) {
|
|
72
|
-
if (!shouldCollectTelemetry()) return null;
|
|
73
|
-
|
|
74
|
-
const record = {
|
|
75
|
-
event: String(event),
|
|
76
|
-
platform: data.platform || null,
|
|
77
|
-
score: typeof data.score === 'number' ? data.score : null,
|
|
78
|
-
checkCount: typeof data.checkCount === 'number' ? data.checkCount : null,
|
|
79
|
-
durationMs: typeof data.durationMs === 'number' ? Math.round(data.durationMs) : null,
|
|
80
|
-
timestamp: new Date().toISOString(),
|
|
81
|
-
nodeVersion: process.version,
|
|
82
|
-
os: `${os.platform()}-${os.arch()}`,
|
|
83
|
-
projectFingerprint: data.dir ? hashProject(data.dir) : null,
|
|
84
|
-
// Explicitly omit: paths, file contents, usernames, email, tokens
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
const telemetryDir = path.dirname(TELEMETRY_FILE);
|
|
89
|
-
fs.mkdirSync(telemetryDir, { recursive: true });
|
|
90
|
-
|
|
91
|
-
let events = [];
|
|
92
|
-
if (fs.existsSync(TELEMETRY_FILE)) {
|
|
93
|
-
try {
|
|
94
|
-
const raw = fs.readFileSync(TELEMETRY_FILE, 'utf8');
|
|
95
|
-
const parsed = JSON.parse(raw);
|
|
96
|
-
events = Array.isArray(parsed.events) ? parsed.events : [];
|
|
97
|
-
} catch {
|
|
98
|
-
events = [];
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
events.push(record);
|
|
103
|
-
|
|
104
|
-
// Cap at MAX_EVENTS to prevent unbounded growth
|
|
105
|
-
if (events.length > MAX_EVENTS) {
|
|
106
|
-
events = events.slice(events.length - MAX_EVENTS);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const payload = {
|
|
110
|
-
version: 1,
|
|
111
|
-
telemetryOptIn: true,
|
|
112
|
-
note: 'Local telemetry only. Set NERVIQ_TELEMETRY=0 or unset to disable.',
|
|
113
|
-
events,
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
fs.writeFileSync(TELEMETRY_FILE, JSON.stringify(payload, null, 2), 'utf8');
|
|
117
|
-
} catch {
|
|
118
|
-
// Telemetry failures are always silent — never block main flow
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return record;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ─── Read local telemetry ─────────────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Read the local telemetry file.
|
|
128
|
-
* @returns {{ version: number, events: object[] } | null}
|
|
129
|
-
*/
|
|
130
|
-
function readLocalTelemetry() {
|
|
131
|
-
try {
|
|
132
|
-
if (!fs.existsSync(TELEMETRY_FILE)) return null;
|
|
133
|
-
return JSON.parse(fs.readFileSync(TELEMETRY_FILE, 'utf8'));
|
|
134
|
-
} catch {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Clear all local telemetry events.
|
|
141
|
-
* @returns {boolean} true if cleared successfully
|
|
142
|
-
*/
|
|
143
|
-
function clearLocalTelemetry() {
|
|
144
|
-
try {
|
|
145
|
-
if (fs.existsSync(TELEMETRY_FILE)) {
|
|
146
|
-
fs.writeFileSync(TELEMETRY_FILE, JSON.stringify({ version: 1, events: [] }, null, 2), 'utf8');
|
|
147
|
-
}
|
|
148
|
-
return true;
|
|
149
|
-
} catch {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
module.exports = {
|
|
155
|
-
shouldCollectTelemetry,
|
|
156
|
-
collectAnonymousEvent,
|
|
157
|
-
readLocalTelemetry,
|
|
158
|
-
clearLocalTelemetry,
|
|
159
|
-
TELEMETRY_FILE,
|
|
160
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Nerviq Opt-In Telemetry Foundation
|
|
3
|
+
*
|
|
4
|
+
* Collects anonymous usage events ONLY when NERVIQ_TELEMETRY=1 is set.
|
|
5
|
+
* No PII, no file contents, no absolute paths are ever stored.
|
|
6
|
+
* Events are stored locally in <projectDir>/.nerviq/telemetry.json.
|
|
7
|
+
*
|
|
8
|
+
* This module is the foundation layer — actual transmission to a dashboard
|
|
9
|
+
* is an explicit opt-in step configured separately.
|
|
10
|
+
*
|
|
11
|
+
* Privacy guarantees:
|
|
12
|
+
* - No usernames, emails, or identifiers
|
|
13
|
+
* - No file contents or code
|
|
14
|
+
* - No absolute paths (only hashed project fingerprint)
|
|
15
|
+
* - Stored only on local disk
|
|
16
|
+
* - Never sent anywhere without additional explicit configuration
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
'use strict';
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const os = require('os');
|
|
24
|
+
const crypto = require('crypto');
|
|
25
|
+
|
|
26
|
+
const TELEMETRY_FILE = path.join(os.homedir(), '.nerviq', 'telemetry.json');
|
|
27
|
+
const MAX_EVENTS = 500; // cap file size at ~500 events
|
|
28
|
+
|
|
29
|
+
// ─── Opt-in check ─────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Returns true only when the user has explicitly set NERVIQ_TELEMETRY=1.
|
|
33
|
+
* Telemetry is opt-IN, not opt-out.
|
|
34
|
+
* @returns {boolean}
|
|
35
|
+
*/
|
|
36
|
+
function shouldCollectTelemetry() {
|
|
37
|
+
return process.env.NERVIQ_TELEMETRY === '1';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ─── Anonymous fingerprinting ─────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates a one-way hash of the project directory.
|
|
44
|
+
* This allows grouping events by project without exposing the path.
|
|
45
|
+
* @param {string} dir
|
|
46
|
+
* @returns {string} 8-char hex fingerprint
|
|
47
|
+
*/
|
|
48
|
+
function hashProject(dir) {
|
|
49
|
+
try {
|
|
50
|
+
return crypto.createHash('sha256').update(dir).digest('hex').slice(0, 8);
|
|
51
|
+
} catch {
|
|
52
|
+
return 'unknown';
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Event collection ────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Collect an anonymous usage event and append it to the local telemetry file.
|
|
60
|
+
* Does nothing unless shouldCollectTelemetry() returns true.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} event - Event name (e.g. 'audit', 'setup', 'convert')
|
|
63
|
+
* @param {object} [data] - Additional anonymous data
|
|
64
|
+
* @param {string} [data.platform] - Platform name (claude, codex, etc.)
|
|
65
|
+
* @param {number} [data.score] - Audit score
|
|
66
|
+
* @param {number} [data.checkCount] - Total checks evaluated
|
|
67
|
+
* @param {number} [data.durationMs] - Execution time in ms
|
|
68
|
+
* @param {string} [data.dir] - Project dir (hashed before storage)
|
|
69
|
+
* @returns {object|null} The recorded event object, or null if telemetry is off
|
|
70
|
+
*/
|
|
71
|
+
function collectAnonymousEvent(event, data = {}) {
|
|
72
|
+
if (!shouldCollectTelemetry()) return null;
|
|
73
|
+
|
|
74
|
+
const record = {
|
|
75
|
+
event: String(event),
|
|
76
|
+
platform: data.platform || null,
|
|
77
|
+
score: typeof data.score === 'number' ? data.score : null,
|
|
78
|
+
checkCount: typeof data.checkCount === 'number' ? data.checkCount : null,
|
|
79
|
+
durationMs: typeof data.durationMs === 'number' ? Math.round(data.durationMs) : null,
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
nodeVersion: process.version,
|
|
82
|
+
os: `${os.platform()}-${os.arch()}`,
|
|
83
|
+
projectFingerprint: data.dir ? hashProject(data.dir) : null,
|
|
84
|
+
// Explicitly omit: paths, file contents, usernames, email, tokens
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const telemetryDir = path.dirname(TELEMETRY_FILE);
|
|
89
|
+
fs.mkdirSync(telemetryDir, { recursive: true });
|
|
90
|
+
|
|
91
|
+
let events = [];
|
|
92
|
+
if (fs.existsSync(TELEMETRY_FILE)) {
|
|
93
|
+
try {
|
|
94
|
+
const raw = fs.readFileSync(TELEMETRY_FILE, 'utf8');
|
|
95
|
+
const parsed = JSON.parse(raw);
|
|
96
|
+
events = Array.isArray(parsed.events) ? parsed.events : [];
|
|
97
|
+
} catch {
|
|
98
|
+
events = [];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
events.push(record);
|
|
103
|
+
|
|
104
|
+
// Cap at MAX_EVENTS to prevent unbounded growth
|
|
105
|
+
if (events.length > MAX_EVENTS) {
|
|
106
|
+
events = events.slice(events.length - MAX_EVENTS);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const payload = {
|
|
110
|
+
version: 1,
|
|
111
|
+
telemetryOptIn: true,
|
|
112
|
+
note: 'Local telemetry only. Set NERVIQ_TELEMETRY=0 or unset to disable.',
|
|
113
|
+
events,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
fs.writeFileSync(TELEMETRY_FILE, JSON.stringify(payload, null, 2), 'utf8');
|
|
117
|
+
} catch {
|
|
118
|
+
// Telemetry failures are always silent — never block main flow
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return record;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ─── Read local telemetry ─────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Read the local telemetry file.
|
|
128
|
+
* @returns {{ version: number, events: object[] } | null}
|
|
129
|
+
*/
|
|
130
|
+
function readLocalTelemetry() {
|
|
131
|
+
try {
|
|
132
|
+
if (!fs.existsSync(TELEMETRY_FILE)) return null;
|
|
133
|
+
return JSON.parse(fs.readFileSync(TELEMETRY_FILE, 'utf8'));
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Clear all local telemetry events.
|
|
141
|
+
* @returns {boolean} true if cleared successfully
|
|
142
|
+
*/
|
|
143
|
+
function clearLocalTelemetry() {
|
|
144
|
+
try {
|
|
145
|
+
if (fs.existsSync(TELEMETRY_FILE)) {
|
|
146
|
+
fs.writeFileSync(TELEMETRY_FILE, JSON.stringify({ version: 1, events: [] }, null, 2), 'utf8');
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = {
|
|
155
|
+
shouldCollectTelemetry,
|
|
156
|
+
collectAnonymousEvent,
|
|
157
|
+
readLocalTelemetry,
|
|
158
|
+
clearLocalTelemetry,
|
|
159
|
+
TELEMETRY_FILE,
|
|
160
|
+
};
|