@nerviq/cli 1.25.0 → 1.26.0

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 CHANGED
@@ -234,8 +234,8 @@ All successful operational responses are wrapped in a JSON envelope:
234
234
  {
235
235
  "data": {},
236
236
  "meta": {
237
- "version": "1.25.0",
238
- "timestamp": "2026-04-15T10:00:00.000Z"
237
+ "version": "1.26.0",
238
+ "timestamp": "2026-04-15T14:00:00.000Z"
239
239
  }
240
240
  }
241
241
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerviq/cli",
3
- "version": "1.25.0",
3
+ "version": "1.26.0",
4
4
  "description": "The intelligent nervous system for AI coding agents — 2,441 checks (8 platforms × ~300 governance rules), 10 languages, 62 domain packs. Audit, align, and amplify.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -6,21 +6,37 @@ const TEST_COMMAND_PATTERNS = [
6
6
  /\bpython\s+manage\.py\s+test\b/i,
7
7
  /\bdjango-admin\s+test\b/i,
8
8
  /\bpython\s+-m\s+unittest\b/i,
9
+ /\bpoetry\s+run\s+(?:pytest|test)\b/i,
10
+ /\buv\s+run\s+(?:pytest|test)\b/i,
11
+ /\bpdm\s+run\s+(?:pytest|test)\b/i,
12
+ /\bhatch\s+run\s+(?:test|pytest)\b/i,
13
+ /\brye\s+run\s+(?:test|pytest)\b/i,
14
+ /\btox(?:\s|$)/i,
15
+ /\bnox(?:\s|$)/i,
9
16
  /\bgo\s+test(?:\s|$)/i,
10
17
  /\bcargo\s+test\b/i,
11
- /\bmake\s+test\b/i,
18
+ /\bmake\s+(?:test|check|ci)\b/i,
19
+ /\bjust\s+test\b/i,
12
20
  /\bmix\s+test\b/i,
13
21
  /\bbundle\s+exec\s+rspec\b/i,
14
22
  /\brspec\b/i,
15
23
  /\bphpunit\b/i,
16
24
  /\bdotnet\s+test(?:\s|$)/i,
17
25
  /\bflutter\s+test\b/i,
26
+ /\bfvm\s+flutter\s+test\b/i,
18
27
  /\bswift\s+test\b/i,
19
28
  /\bxcodebuild\b[^\n\r`]{0,200}\btest\b/i,
20
- /\bgradlew?\s+test\b/i,
29
+ /\bfastlane\s+(?:test|scan)\b/i,
30
+ /\bxctest\b/i,
31
+ /\bgradlew?\s+(?:test|check|connectedAndroidTest)\b/i,
21
32
  /\bmvn(?:w)?\s+test\b/i,
22
33
  /\bplaywright\s+test\b/i,
23
34
  /\bcypress\s+run\b/i,
35
+ // pyproject.toml / setup.cfg tool configuration signals
36
+ /\[tool\.pytest\.ini_options\]/i,
37
+ /\[tool:pytest\]/i,
38
+ // Manifest / config signals that testing is wired up
39
+ /\bpytest\s*[=:]/i,
24
40
  ];
25
41
 
26
42
  const LINT_COMMAND_PATTERNS = [
@@ -33,6 +49,7 @@ const LINT_COMMAND_PATTERNS = [
33
49
  /\bpylint\b/i,
34
50
  /\bmypy\b/i,
35
51
  /\bpyright\b/i,
52
+ /\bpre-commit\s+run\b/i,
36
53
  /\bgo\s+vet\b/i,
37
54
  /\bgofmt\b/i,
38
55
  /\bgofumpt\b/i,
@@ -41,11 +58,19 @@ const LINT_COMMAND_PATTERNS = [
41
58
  /\bcargo\s+clippy\b/i,
42
59
  /\bflutter\s+analyze\b/i,
43
60
  /\bdart\s+analyze\b/i,
61
+ /\bdart\s+format\b/i,
44
62
  /\bswiftlint\b/i,
45
63
  /\bswift(?:-|\s+)format\b/i,
46
64
  /\bdotnet\s+format(?:\s|$)/i,
47
- /\bgradlew?\s+lint\b/i,
65
+ /\bgradlew?\s+(?:lint|ktlintCheck|detekt|spotless(?:Check|Apply)?)\b/i,
48
66
  /\bmvn(?:w)?\s+(?:checkstyle:check|spotbugs:check|verify)\b/i,
67
+ // pyproject.toml / config signals
68
+ /\[tool\.ruff\]/i,
69
+ /\[tool\.black\]/i,
70
+ /\[tool\.mypy\]/i,
71
+ /\[tool\.pyright\]/i,
72
+ /\[tool\.flake8\]/i,
73
+ /\[tool\.pylint\b/i,
49
74
  ];
50
75
 
51
76
  const BUILD_COMMAND_PATTERNS = [
@@ -53,16 +78,20 @@ const BUILD_COMMAND_PATTERNS = [
53
78
  /\btsc(?:\s|$)/i,
54
79
  /\bgo\s+build(?:\s|$)/i,
55
80
  /\bcargo\s+(?:build|check)\b/i,
56
- /\bmake\s+build\b/i,
81
+ /\bmake\s+(?:build|all)\b/i,
82
+ /\bjust\s+build\b/i,
57
83
  /\bdotnet\s+(?:build|publish)(?:\s|$)/i,
58
84
  /\bmsbuild\b/i,
59
85
  /\bflutter\s+build(?:\s|$)/i,
60
86
  /\bswift\s+build\b/i,
61
87
  /\bxcodebuild\b/i,
62
- /\bgradlew?\s+(?:build|assemble)\b/i,
88
+ /\bgradlew?\s+(?:build|assemble|assembleDebug|assembleRelease)\b/i,
63
89
  /\bmvn(?:w)?\s+(?:compile|package|verify|install)\b/i,
64
90
  /\bpython\s+-m\s+build\b/i,
65
91
  /\bpoetry\s+build\b/i,
92
+ /\buv\s+build\b/i,
93
+ /\bhatch\s+build\b/i,
94
+ /\bpdm\s+build\b/i,
66
95
  ];
67
96
 
68
97
  function normalizePath(filePath) {
@@ -118,6 +147,33 @@ function buildSurfaceList(ctx, scope) {
118
147
  if (includeReadme) {
119
148
  addSurface(ctx, surfaces, seen, 'README.md');
120
149
  addSurface(ctx, surfaces, seen, 'CONTRIBUTING.md');
150
+ // CTO-07: framework-native verification surfaces. When a repo has
151
+ // `flutter test` in CONTRIBUTING.md, pytest configured in
152
+ // pyproject.toml, xcodebuild wired in a workflow, or gradle test in
153
+ // build.gradle, that is legitimate evidence of verification — an
154
+ // agent working in this repo can observe these files directly.
155
+ addSurface(ctx, surfaces, seen, 'pyproject.toml');
156
+ addSurface(ctx, surfaces, seen, 'setup.cfg');
157
+ addSurface(ctx, surfaces, seen, 'tox.ini');
158
+ addSurface(ctx, surfaces, seen, 'noxfile.py');
159
+ addSurface(ctx, surfaces, seen, 'Pipfile');
160
+ addSurface(ctx, surfaces, seen, 'Makefile');
161
+ addSurface(ctx, surfaces, seen, 'justfile');
162
+ addSurface(ctx, surfaces, seen, 'Justfile');
163
+ addSurface(ctx, surfaces, seen, 'Rakefile');
164
+ addSurface(ctx, surfaces, seen, 'pubspec.yaml');
165
+ addSurface(ctx, surfaces, seen, 'analysis_options.yaml');
166
+ addSurface(ctx, surfaces, seen, 'Package.swift');
167
+ addSurface(ctx, surfaces, seen, 'Podfile');
168
+ addSurface(ctx, surfaces, seen, 'Cartfile');
169
+ addSurface(ctx, surfaces, seen, 'fastlane/Fastfile');
170
+ addSurface(ctx, surfaces, seen, 'build.gradle');
171
+ addSurface(ctx, surfaces, seen, 'build.gradle.kts');
172
+ addSurface(ctx, surfaces, seen, 'settings.gradle');
173
+ addSurface(ctx, surfaces, seen, 'settings.gradle.kts');
174
+ addSurface(ctx, surfaces, seen, '.pre-commit-config.yaml');
175
+ addSurface(ctx, surfaces, seen, '.pre-commit-config.yml');
176
+ addDirSurfaces(ctx, surfaces, seen, '.github/workflows', /\.ya?ml$/i);
121
177
  }
122
178
 
123
179
  return surfaces;
@@ -202,6 +202,106 @@ function isDotnetProject(ctx) {
202
202
  return ctx.__nerviqIsDotnet;
203
203
  }
204
204
 
205
+ // ─── CTO-07 Framework-native verification signals ───────────────────────
206
+ // Memoized on ctx. These are "this stack has verification wired up"
207
+ // signals that augment documentation-surface detection.
208
+
209
+ function hasIosXcodeProject(ctx) {
210
+ if (ctx.__nerviqHasIosXcode !== undefined) return ctx.__nerviqHasIosXcode;
211
+ ctx.__nerviqHasIosXcode =
212
+ hasCoreProjectFile(ctx, /\.xcodeproj\//i) ||
213
+ hasCoreProjectFile(ctx, /\.xcworkspace\//i) ||
214
+ hasCoreRootFile(ctx, /(^|\/)Package\.swift$/i);
215
+ return ctx.__nerviqHasIosXcode;
216
+ }
217
+
218
+ function hasAndroidGradle(ctx) {
219
+ if (ctx.__nerviqHasAndroidGradle !== undefined) return ctx.__nerviqHasAndroidGradle;
220
+ ctx.__nerviqHasAndroidGradle =
221
+ hasCoreRootFile(ctx, /(^|\/)build\.gradle(\.kts)?$/i) ||
222
+ hasCoreRootFile(ctx, /(^|\/)settings\.gradle(\.kts)?$/i);
223
+ return ctx.__nerviqHasAndroidGradle;
224
+ }
225
+
226
+ function hasFlutterProject(ctx) {
227
+ if (ctx.__nerviqHasFlutter !== undefined) return ctx.__nerviqHasFlutter;
228
+ const pubspec = ctx.fileContent('pubspec.yaml') || '';
229
+ ctx.__nerviqHasFlutter = /\bflutter:\s*\n/i.test(pubspec) || /\bsdk:\s*flutter\b/i.test(pubspec);
230
+ return ctx.__nerviqHasFlutter;
231
+ }
232
+
233
+ function _pyProjectText(ctx) {
234
+ return getPythonProjectText(ctx);
235
+ }
236
+
237
+ function hasPythonPoetry(ctx) {
238
+ if (ctx.__nerviqHasPoetry !== undefined) return ctx.__nerviqHasPoetry;
239
+ const text = _pyProjectText(ctx);
240
+ ctx.__nerviqHasPoetry = /\[tool\.poetry\]/i.test(text) || !!ctx.fileContent('poetry.lock');
241
+ return ctx.__nerviqHasPoetry;
242
+ }
243
+
244
+ function hasPythonUv(ctx) {
245
+ if (ctx.__nerviqHasUv !== undefined) return ctx.__nerviqHasUv;
246
+ const text = _pyProjectText(ctx);
247
+ ctx.__nerviqHasUv = /\[tool\.uv\]/i.test(text) || !!ctx.fileContent('uv.lock');
248
+ return ctx.__nerviqHasUv;
249
+ }
250
+
251
+ function hasPythonPdm(ctx) {
252
+ if (ctx.__nerviqHasPdm !== undefined) return ctx.__nerviqHasPdm;
253
+ const text = _pyProjectText(ctx);
254
+ ctx.__nerviqHasPdm = /\[tool\.pdm\b/i.test(text) || !!ctx.fileContent('pdm.lock');
255
+ return ctx.__nerviqHasPdm;
256
+ }
257
+
258
+ function hasPythonHatch(ctx) {
259
+ if (ctx.__nerviqHasHatch !== undefined) return ctx.__nerviqHasHatch;
260
+ const text = _pyProjectText(ctx);
261
+ ctx.__nerviqHasHatch = /\[tool\.hatch\b/i.test(text);
262
+ return ctx.__nerviqHasHatch;
263
+ }
264
+
265
+ function hasFastApiProject(ctx) {
266
+ if (ctx.__nerviqHasFastApi !== undefined) return ctx.__nerviqHasFastApi;
267
+ const text = _pyProjectText(ctx);
268
+ ctx.__nerviqHasFastApi = /\bfastapi\b/i.test(text);
269
+ return ctx.__nerviqHasFastApi;
270
+ }
271
+
272
+ const ML_DEP_PATTERN = /\b(pytorch|torch|tensorflow|keras|scikit-learn|sklearn|jax|transformers|datasets|huggingface|accelerate|xgboost|lightgbm)\b/i;
273
+
274
+ function hasMlScaffolding(ctx) {
275
+ if (ctx.__nerviqHasMl !== undefined) return ctx.__nerviqHasMl;
276
+ const text = _pyProjectText(ctx);
277
+ if (ML_DEP_PATTERN.test(text)) {
278
+ ctx.__nerviqHasMl = true;
279
+ return true;
280
+ }
281
+ // Heuristic: notebooks/ or experiments/ dir with actual .ipynb files
282
+ const hasNotebooks = findProjectFiles(ctx, /\.ipynb$/i).length > 0;
283
+ ctx.__nerviqHasMl = hasNotebooks;
284
+ return ctx.__nerviqHasMl;
285
+ }
286
+
287
+ /**
288
+ * Checks whether a Python tool is actively configured in pyproject.toml /
289
+ * setup.cfg (e.g., `[tool.ruff]`, `[tool.pytest.ini_options]`,
290
+ * `[tool.mypy]`). When configured, any verification-surface check for that
291
+ * tool should pass: an agent working in this repo can run the tool.
292
+ */
293
+ function hasConfiguredTooling(ctx, toolName) {
294
+ const text = _pyProjectText(ctx);
295
+ if (!text) return false;
296
+ const name = String(toolName || '').toLowerCase();
297
+ const sectionRe = new RegExp(`\\[tool\\.${name.replace(/[-_.]/g, '[-_.]')}(?:[.\\]]|\\s|$)`, 'i');
298
+ if (sectionRe.test(text)) return true;
299
+ if (name === 'pytest') {
300
+ return /\[tool\.pytest\.ini_options\]/i.test(text) || /\[tool:pytest\]/i.test(text);
301
+ }
302
+ return false;
303
+ }
304
+
205
305
  /**
206
306
  * Map category names to their project detection function.
207
307
  * Used by the audit to skip entire categories when the stack isn't detected.
@@ -418,6 +518,16 @@ module.exports = {
418
518
  isPhpProject,
419
519
  isDotnetProject,
420
520
  STACK_CATEGORY_DETECTORS,
521
+ hasIosXcodeProject,
522
+ hasAndroidGradle,
523
+ hasFlutterProject,
524
+ hasPythonPoetry,
525
+ hasPythonUv,
526
+ hasPythonPdm,
527
+ hasPythonHatch,
528
+ hasFastApiProject,
529
+ hasMlScaffolding,
530
+ hasConfiguredTooling,
421
531
  getPythonFiles,
422
532
  getMainPythonFiles,
423
533
  getPythonProjectText,