@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.
Files changed (80) hide show
  1. package/CHANGELOG.md +1527 -1493
  2. package/README.md +550 -538
  3. package/SECURITY.md +82 -82
  4. package/bin/cli.js +2562 -2558
  5. package/docs/api-reference.md +356 -356
  6. package/docs/audit-fix.md +109 -0
  7. package/docs/autofix.md +3 -62
  8. package/docs/getting-started.md +1 -1
  9. package/docs/index.html +592 -592
  10. package/docs/integration-contracts.md +287 -287
  11. package/docs/maintenance.md +128 -128
  12. package/docs/new-platform-guide.md +202 -202
  13. package/docs/release-process.md +63 -0
  14. package/docs/shallow-risk.md +244 -244
  15. package/docs/why-nerviq.md +82 -82
  16. package/package.json +67 -67
  17. package/src/aider/activity.js +226 -226
  18. package/src/aider/context.js +162 -162
  19. package/src/aider/freshness.js +123 -123
  20. package/src/aider/techniques.js +3465 -3465
  21. package/src/audit/layers.js +180 -180
  22. package/src/audit.js +1032 -1032
  23. package/src/benchmark.js +299 -299
  24. package/src/codex/activity.js +324 -324
  25. package/src/codex/freshness.js +142 -142
  26. package/src/codex/techniques.js +4895 -4895
  27. package/src/context.js +326 -326
  28. package/src/continuous-ops.js +11 -1
  29. package/src/convert.js +340 -340
  30. package/src/copilot/config-parser.js +280 -280
  31. package/src/copilot/context.js +218 -218
  32. package/src/copilot/freshness.js +177 -177
  33. package/src/copilot/patch.js +238 -238
  34. package/src/copilot/techniques.js +3578 -3578
  35. package/src/cursor/freshness.js +194 -194
  36. package/src/cursor/patch.js +243 -243
  37. package/src/cursor/techniques.js +3735 -3735
  38. package/src/doctor.js +201 -201
  39. package/src/fix-engine.js +511 -8
  40. package/src/formatters/csv.js +86 -86
  41. package/src/formatters/junit.js +123 -123
  42. package/src/formatters/markdown.js +164 -164
  43. package/src/formatters/otel.js +151 -151
  44. package/src/freshness.js +156 -156
  45. package/src/gemini/activity.js +402 -402
  46. package/src/gemini/context.js +290 -290
  47. package/src/gemini/freshness.js +183 -183
  48. package/src/gemini/patch.js +229 -229
  49. package/src/gemini/techniques.js +3811 -3811
  50. package/src/governance.js +533 -533
  51. package/src/harmony/audit.js +306 -306
  52. package/src/i18n.js +63 -63
  53. package/src/insights.js +119 -119
  54. package/src/integrations.js +134 -134
  55. package/src/locales/en.json +33 -33
  56. package/src/locales/es.json +33 -33
  57. package/src/migrate.js +354 -354
  58. package/src/opencode/activity.js +286 -286
  59. package/src/opencode/freshness.js +137 -137
  60. package/src/opencode/techniques.js +3450 -3450
  61. package/src/setup/analysis.js +12 -12
  62. package/src/setup.js +7 -6
  63. package/src/shallow-risk/index.js +56 -56
  64. package/src/shallow-risk/patterns/agent-config-cross-platform-drift.js +50 -50
  65. package/src/shallow-risk/patterns/agent-config-dangerous-autoapprove.js +46 -46
  66. package/src/shallow-risk/patterns/agent-config-deprecated-keys.js +46 -46
  67. package/src/shallow-risk/patterns/agent-config-missing-file.js +317 -317
  68. package/src/shallow-risk/patterns/agent-config-secret-literal.js +49 -49
  69. package/src/shallow-risk/patterns/agent-config-stack-contradiction.js +34 -34
  70. package/src/shallow-risk/patterns/hook-script-missing.js +70 -70
  71. package/src/shallow-risk/patterns/mcp-server-no-allowlist.js +52 -52
  72. package/src/shallow-risk/shared.js +648 -648
  73. package/src/source-urls.js +295 -295
  74. package/src/state-paths.js +85 -85
  75. package/src/supplemental-checks.js +805 -805
  76. package/src/telemetry.js +160 -160
  77. package/src/windsurf/context.js +359 -359
  78. package/src/windsurf/freshness.js +194 -194
  79. package/src/windsurf/patch.js +231 -231
  80. 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
+ };