@nerviq/cli 1.9.0 → 1.10.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 +28 -33
- package/bin/cli.js +161 -106
- package/package.json +1 -1
- package/src/activity.js +68 -12
- package/src/aider/freshness.js +168 -168
- package/src/anti-patterns.js +13 -11
- package/src/audit.js +6 -5
- package/src/auto-suggest.js +62 -9
- package/src/benchmark.js +52 -41
- package/src/dashboard.js +36 -14
- package/src/instruction-surfaces.js +185 -0
- package/src/locales/en.json +1 -1
- package/src/locales/es.json +1 -1
- package/src/stack-checks.js +1 -1
- package/src/synergy/report.js +1 -0
- package/src/techniques.js +61 -58
- package/src/workspace.js +51 -6
package/README.md
CHANGED
|
@@ -64,18 +64,26 @@ Nerviq scores your AI coding agent setup from 0 to 100, finds what's missing, an
|
|
|
64
64
|
Next: nerviq setup
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
## Quick Start
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
npx @nerviq/cli
|
|
71
|
-
npx @nerviq/cli audit
|
|
72
|
-
npx @nerviq/cli
|
|
73
|
-
npx @nerviq/cli
|
|
74
|
-
npx @nerviq/cli
|
|
75
|
-
npx @nerviq/cli
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
## Quick Start
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx @nerviq/cli --beginner # Show only the 5 starter commands
|
|
71
|
+
npx @nerviq/cli audit # Quick scan: score + top 3 actions
|
|
72
|
+
npx @nerviq/cli audit --full # Full audit with all checks + badge
|
|
73
|
+
npx @nerviq/cli audit --workspace packages/* # Monorepo: root governance + workspace average/package scores
|
|
74
|
+
npx @nerviq/cli setup # Generate starter-safe baseline
|
|
75
|
+
npx @nerviq/cli augment # Improvement plan, no writes
|
|
76
|
+
npx @nerviq/cli governance # Permission profiles + policy packs
|
|
77
|
+
npx @nerviq/cli benchmark # Baseline vs projected score in isolated copy
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
No install required. Zero dependencies.
|
|
81
|
+
|
|
82
|
+
If you want the shortest possible command list inside the terminal, start with:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx @nerviq/cli --beginner
|
|
86
|
+
```
|
|
79
87
|
|
|
80
88
|
## Get Started by Role
|
|
81
89
|
|
|
@@ -236,14 +244,14 @@ Levels:
|
|
|
236
244
|
| `nerviq plan` | Export proposal bundles with previews |
|
|
237
245
|
| `nerviq apply` | Apply proposals with rollback |
|
|
238
246
|
| `nerviq governance` | Permission profiles, hooks, policy packs |
|
|
239
|
-
| `nerviq benchmark` |
|
|
247
|
+
| `nerviq benchmark` | Baseline vs projected score in isolated temp copy |
|
|
240
248
|
| `nerviq check-health` | Detect regressions between audit snapshots |
|
|
241
249
|
| `nerviq deep-review` | AI-powered config review (opt-in) |
|
|
242
250
|
| `nerviq interactive` | Step-by-step guided wizard |
|
|
243
251
|
| `nerviq watch` | Live monitoring with score delta |
|
|
244
|
-
| `nerviq history` |
|
|
245
|
-
| `nerviq compare` | Compare latest vs previous |
|
|
246
|
-
| `nerviq trend` | Export trend report |
|
|
252
|
+
| `nerviq history` | Audit snapshot history from saved snapshots |
|
|
253
|
+
| `nerviq compare` | Compare latest vs previous audit snapshot |
|
|
254
|
+
| `nerviq trend` | Export audit snapshot trend report |
|
|
247
255
|
| `nerviq feedback` | Record recommendation outcomes |
|
|
248
256
|
| `nerviq anti-patterns` | Detect anti-patterns in current project |
|
|
249
257
|
| `nerviq freshness` | Show verification freshness for all checks |
|
|
@@ -281,7 +289,7 @@ Levels:
|
|
|
281
289
|
| `--only A,B` | Limit apply to selected proposal IDs |
|
|
282
290
|
| `--format sarif` | SARIF output for code scanning |
|
|
283
291
|
| `--platform NAME` | Target platform (claude, codex, gemini, copilot, cursor, windsurf, aider, opencode) |
|
|
284
|
-
| `--workspace GLOB` | Audit workspaces separately (e.g. packages/*) |
|
|
292
|
+
| `--workspace GLOB` | Audit workspaces separately as package-level live audits (e.g. packages/*) |
|
|
285
293
|
| `--external PATH` | Benchmark an external repo |
|
|
286
294
|
|
|
287
295
|
## Backed by Research
|
|
@@ -333,9 +341,9 @@ If Nerviq helped you, consider giving it a ⭐ on [GitHub](https://github.com/ne
|
|
|
333
341
|
|
|
334
342
|
**Not designed for:** Deeply customized setups with 20+ skills, agent teams, and bespoke MCP integrations. If you've already built advanced agent workflows, you may not need this.
|
|
335
343
|
|
|
336
|
-
**Strongest at:**
|
|
337
|
-
|
|
338
|
-
**Not a replacement for:** Deep architectural review of business logic, runtime performance profiling, or security penetration testing. Nerviq focuses on how your AI coding agents are configured and governed — not on what your application code does.
|
|
344
|
+
**Strongest at:** AI agent governance, configuration intelligence, workflow policy hygiene, cross-platform alignment, and setup standardization.
|
|
345
|
+
|
|
346
|
+
**Not a replacement for:** Deep architectural review of business logic, runtime performance profiling, full SAST coverage, secret scanning, or security penetration testing. Nerviq focuses on how your AI coding agents are configured and governed — not on what your application code does.
|
|
339
347
|
|
|
340
348
|
**Confidence levels:** Every check includes a `confidence` score (0.0–1.0) and a `sourceUrl` linking to primary documentation. Checks marked `heuristic` are pattern-based and may produce false positives on non-standard project structures.
|
|
341
349
|
|
|
@@ -347,16 +355,3 @@ If Nerviq helped you, consider giving it a ⭐ on [GitHub](https://github.com/ne
|
|
|
347
355
|
| `BETA` | Works but has limited real-world testing. API may change |
|
|
348
356
|
| `EXPERIMENTAL` | Early stage, static rules, results may vary |
|
|
349
357
|
|
|
350
|
-
## Previously nerviq-cli
|
|
351
|
-
|
|
352
|
-
Nerviq was previously published as `nerviq-cli`. If you were using it:
|
|
353
|
-
|
|
354
|
-
```bash
|
|
355
|
-
# Old
|
|
356
|
-
npx nerviq-cli
|
|
357
|
-
|
|
358
|
-
# New
|
|
359
|
-
npx @nerviq/cli audit
|
|
360
|
-
```
|
|
361
|
-
|
|
362
|
-
All features are preserved and expanded.
|
package/bin/cli.js
CHANGED
|
@@ -61,9 +61,9 @@ function suggestCommand(input) {
|
|
|
61
61
|
return bestDistance <= 3 ? best : null;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
function parseArgs(rawArgs) {
|
|
65
|
-
const flags = [];
|
|
66
|
-
let command = 'audit';
|
|
64
|
+
function parseArgs(rawArgs) {
|
|
65
|
+
const flags = [];
|
|
66
|
+
let command = 'audit';
|
|
67
67
|
let threshold = null;
|
|
68
68
|
let out = null;
|
|
69
69
|
let planFile = null;
|
|
@@ -89,10 +89,11 @@ function parseArgs(rawArgs) {
|
|
|
89
89
|
let migrateFrom = null;
|
|
90
90
|
let migrateTo = null;
|
|
91
91
|
let checkVersion = null;
|
|
92
|
-
let external = null;
|
|
93
|
-
let repos = [];
|
|
94
|
-
let teamProfile = null;
|
|
95
|
-
let lang = null;
|
|
92
|
+
let external = null;
|
|
93
|
+
let repos = [];
|
|
94
|
+
let teamProfile = null;
|
|
95
|
+
let lang = null;
|
|
96
|
+
let commandExplicit = false;
|
|
96
97
|
|
|
97
98
|
for (let i = 0; i < rawArgs.length; i++) {
|
|
98
99
|
const arg = rawArgs[i];
|
|
@@ -256,37 +257,47 @@ function parseArgs(rawArgs) {
|
|
|
256
257
|
continue;
|
|
257
258
|
}
|
|
258
259
|
|
|
259
|
-
if (!commandSet) {
|
|
260
|
-
command = arg;
|
|
261
|
-
commandSet = true;
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
if (!commandSet) {
|
|
261
|
+
command = arg;
|
|
262
|
+
commandSet = true;
|
|
263
|
+
commandExplicit = true;
|
|
264
|
+
} else {
|
|
265
|
+
extraArgs.push(arg);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
266
268
|
|
|
267
269
|
const normalizedCommand = COMMAND_ALIASES[command] || command;
|
|
268
270
|
|
|
269
|
-
return { flags, command, normalizedCommand, threshold, out, planFile, only, profile, mcpPacks, requireChecks, feedbackKey, feedbackStatus, feedbackEffect, feedbackNotes, feedbackSource, feedbackScoreDelta, platform, format, port, workspace, extraArgs, convertFrom, convertTo, migrateFrom, migrateTo, checkVersion, webhookUrl, external, repos, teamProfile, lang };
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function printWorkspaceSummary(summary, options) {
|
|
273
|
-
if (options.json) {
|
|
274
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
console.log(
|
|
282
|
-
console.log(
|
|
283
|
-
console.log(
|
|
284
|
-
console.log(`
|
|
285
|
-
console.log(
|
|
286
|
-
console.log(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
271
|
+
return { flags, command, commandExplicit, normalizedCommand, threshold, out, planFile, only, profile, mcpPacks, requireChecks, feedbackKey, feedbackStatus, feedbackEffect, feedbackNotes, feedbackSource, feedbackScoreDelta, platform, format, port, workspace, extraArgs, convertFrom, convertTo, migrateFrom, migrateTo, checkVersion, webhookUrl, external, repos, teamProfile, lang };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function printWorkspaceSummary(summary, options) {
|
|
275
|
+
if (options.json) {
|
|
276
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const rootScore = summary.rootGovernance?.score === null ? 'ERR' : `${summary.rootGovernance?.score ?? 0}/100`;
|
|
281
|
+
const workspaceAverage = summary.workspaceAggregate?.score ?? summary.averageScore;
|
|
282
|
+
|
|
283
|
+
console.log('');
|
|
284
|
+
console.log('\x1b[1m nerviq workspace audit\x1b[0m');
|
|
285
|
+
console.log('\x1b[2m ═══════════════════════════════════════\x1b[0m');
|
|
286
|
+
console.log(` Root: ${summary.rootDir}`);
|
|
287
|
+
console.log(` Platform: ${summary.platform}`);
|
|
288
|
+
console.log(` Workspaces: ${summary.workspaceCount}`);
|
|
289
|
+
if (summary.patterns?.length > 0) {
|
|
290
|
+
console.log(` Selection: ${summary.patterns.join(', ')}`);
|
|
291
|
+
}
|
|
292
|
+
console.log(` Root governance audit: \x1b[1m${rootScore}\x1b[0m`);
|
|
293
|
+
console.log(` Workspace audit average: \x1b[1m${workspaceAverage}/100\x1b[0m`);
|
|
294
|
+
console.log(' Score semantics: root governance shows shared repo policy health; workspace average shows package-level coverage across the selected workspaces.');
|
|
295
|
+
console.log(' Aggregate vs package: per-workspace scores can legitimately trail the root repo score in a monorepo.');
|
|
296
|
+
console.log('');
|
|
297
|
+
console.log('\x1b[1m Workspace Audit Pass Total Top action\x1b[0m');
|
|
298
|
+
console.log(' ' + '─'.repeat(72));
|
|
299
|
+
for (const item of summary.workspaces) {
|
|
300
|
+
const score = item.score === null ? 'ERR' : String(item.score);
|
|
290
301
|
const topAction = item.error || item.topAction || '-';
|
|
291
302
|
console.log(` ${item.workspace.padEnd(26)} ${score.padStart(5)} ${String(item.passed).padStart(5)} ${String(item.total).padStart(6)} ${topAction}`);
|
|
292
303
|
}
|
|
@@ -366,17 +377,18 @@ function printOrgSummary(summary, options) {
|
|
|
366
377
|
console.log('');
|
|
367
378
|
}
|
|
368
379
|
|
|
369
|
-
const HELP = `
|
|
370
|
-
nerviq v${version}
|
|
371
|
-
The intelligent nervous system for AI coding agents.
|
|
372
|
-
Audit, align, and amplify every platform on every project.
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
nerviq audit
|
|
377
|
-
nerviq audit --
|
|
378
|
-
nerviq audit --
|
|
379
|
-
nerviq audit --
|
|
380
|
+
const HELP = `
|
|
381
|
+
nerviq v${version}
|
|
382
|
+
The intelligent nervous system for AI coding agents.
|
|
383
|
+
Audit, align, and amplify every platform on every project.
|
|
384
|
+
New here? Run: nerviq --beginner
|
|
385
|
+
|
|
386
|
+
DISCOVER
|
|
387
|
+
nerviq audit Quick scan: score + top 3 gaps (default)
|
|
388
|
+
nerviq audit --full Full audit with all checks, weakest areas, badge
|
|
389
|
+
nerviq audit --platform X Audit specific platform (claude|codex|cursor|copilot|gemini|windsurf|aider|opencode)
|
|
390
|
+
nerviq audit --json Machine-readable JSON output (for CI)
|
|
391
|
+
nerviq audit --workspace packages/* Audit monorepo workspaces with root-vs-package score semantics
|
|
380
392
|
nerviq scan dir1 dir2 Compare multiple repos side-by-side
|
|
381
393
|
nerviq org scan dir1 dir2 Aggregate multiple repos into one score table
|
|
382
394
|
nerviq catalog Full check catalog (all 8 platforms)
|
|
@@ -413,30 +425,30 @@ const HELP = `
|
|
|
413
425
|
GOVERN
|
|
414
426
|
nerviq governance Permission profiles + hooks + policy packs
|
|
415
427
|
nerviq governance --json Machine-readable governance summary
|
|
416
|
-
nerviq benchmark
|
|
428
|
+
nerviq benchmark Baseline vs projected score in isolated temp copy
|
|
417
429
|
nerviq benchmark --external /path Benchmark an external repo
|
|
418
430
|
nerviq freshness Show verification freshness for all checks
|
|
419
431
|
nerviq certify Generate certification badge for your project
|
|
420
432
|
|
|
421
433
|
CROSS-PLATFORM
|
|
422
|
-
nerviq harmony-audit Drift detection across all active platforms
|
|
423
|
-
nerviq harmony-sync Preview cross-platform sync (dry run)
|
|
424
|
-
nerviq harmony-sync --fix Apply cross-platform sync (write files)
|
|
425
|
-
nerviq harmony-sync --json JSON output for CI/automation
|
|
426
|
-
nerviq harmony-add <platform> Add a new platform to the project
|
|
427
|
-
nerviq synergy-report
|
|
434
|
+
nerviq harmony-audit Drift detection across all active platforms (GA)
|
|
435
|
+
nerviq harmony-sync Preview cross-platform sync (dry run, GA)
|
|
436
|
+
nerviq harmony-sync --fix Apply cross-platform sync (write files, GA)
|
|
437
|
+
nerviq harmony-sync --json JSON output for CI/automation
|
|
438
|
+
nerviq harmony-add <platform> Add a new platform to the project
|
|
439
|
+
nerviq synergy-report [EXPERIMENTAL] Static-rule multi-agent amplification report
|
|
428
440
|
nerviq convert --from X --to Y Convert configs between platforms
|
|
429
441
|
nerviq migrate --platform X Platform version migration helper
|
|
430
442
|
nerviq migrate --platform cursor --from v2 --to v3
|
|
431
443
|
|
|
432
444
|
MONITOR
|
|
433
|
-
nerviq dashboard Generate static
|
|
445
|
+
nerviq dashboard Generate static dashboard from latest audit snapshot (or live audit if none)
|
|
434
446
|
nerviq dashboard --out F Save dashboard to custom file
|
|
435
447
|
nerviq dashboard --open Open dashboard in browser after generating
|
|
436
448
|
nerviq watch Live config monitoring (re-audits on file change)
|
|
437
|
-
nerviq history
|
|
438
|
-
nerviq compare Latest vs previous snapshot diff
|
|
439
|
-
nerviq trend
|
|
449
|
+
nerviq history Audit snapshot history from saved snapshots
|
|
450
|
+
nerviq compare Latest vs previous audit snapshot diff
|
|
451
|
+
nerviq trend Audit snapshot trend over time
|
|
440
452
|
nerviq trend --out report.md Export trend report as markdown
|
|
441
453
|
nerviq feedback Record recommendation outcomes
|
|
442
454
|
|
|
@@ -469,26 +481,28 @@ const HELP = `
|
|
|
469
481
|
--webhook URL Send audit results to a webhook (Slack/Discord/generic JSON)
|
|
470
482
|
--external PATH Benchmark an external repo instead of cwd
|
|
471
483
|
--port N Port for \`serve\` (default: 3000)
|
|
472
|
-
--workspace GLOBS Audit workspaces separately
|
|
484
|
+
--workspace GLOBS Audit workspaces separately and label root governance vs package scores
|
|
473
485
|
--snapshot Save snapshot artifact under .claude/nerviq/snapshots/
|
|
474
486
|
--full Show full audit output (all checks, weakest areas, badge)
|
|
475
487
|
--lite Short top-3 scan (default behavior since v1.5.2)
|
|
476
488
|
--dry-run Preview changes without writing files
|
|
477
489
|
--config-only Only write config files (.claude/, rules, hooks) — never source code
|
|
478
490
|
--verbose Full audit + medium-priority recommendations
|
|
479
|
-
--show-deprecated Show deprecated checks (excluded from scoring)
|
|
480
|
-
--json Output as JSON
|
|
481
|
-
--auto Apply all generated files without prompting
|
|
482
|
-
--
|
|
491
|
+
--show-deprecated Show deprecated checks (excluded from scoring)
|
|
492
|
+
--json Output as JSON
|
|
493
|
+
--auto Apply all generated files without prompting
|
|
494
|
+
--beginner Show only the 5 starter commands for first-time users
|
|
495
|
+
--key NAME Feedback: recommendation key (e.g. permissionDeny)
|
|
483
496
|
--status VALUE Feedback: accepted | rejected | deferred
|
|
484
497
|
--effect VALUE Feedback: positive | neutral | negative
|
|
485
498
|
--score-delta N Feedback: observed score delta
|
|
486
499
|
--help Show this help
|
|
487
500
|
--version Show version
|
|
488
501
|
|
|
489
|
-
EXAMPLES
|
|
490
|
-
npx nerviq
|
|
491
|
-
npx nerviq
|
|
502
|
+
EXAMPLES
|
|
503
|
+
npx nerviq --beginner
|
|
504
|
+
npx nerviq
|
|
505
|
+
npx nerviq --lite
|
|
492
506
|
npx nerviq --platform cursor
|
|
493
507
|
npx nerviq audit --workspace packages/*
|
|
494
508
|
npx nerviq --platform codex augment
|
|
@@ -505,9 +519,34 @@ const HELP = `
|
|
|
505
519
|
npx nerviq feedback --key permissionDeny --status accepted --effect positive
|
|
506
520
|
|
|
507
521
|
EXIT CODES
|
|
508
|
-
0 Success
|
|
509
|
-
1 Error, unknown command, or score below --threshold
|
|
510
|
-
`;
|
|
522
|
+
0 Success
|
|
523
|
+
1 Error, unknown command, or score below --threshold
|
|
524
|
+
`;
|
|
525
|
+
|
|
526
|
+
const BEGINNER_HELP = `
|
|
527
|
+
nerviq v${version}
|
|
528
|
+
Start here.
|
|
529
|
+
|
|
530
|
+
If this is your first time, learn just these 5 commands:
|
|
531
|
+
|
|
532
|
+
STARTER COMMANDS
|
|
533
|
+
nerviq audit Score the repo and show the top gaps
|
|
534
|
+
nerviq setup Generate a starter-safe baseline
|
|
535
|
+
nerviq fix Fix what can be fixed or show manual fix guidance
|
|
536
|
+
nerviq augment Show an improvement plan without writing
|
|
537
|
+
nerviq doctor Check install health, freshness, and platform detection
|
|
538
|
+
|
|
539
|
+
SIMPLE PATH
|
|
540
|
+
1. nerviq audit
|
|
541
|
+
2. nerviq setup --auto
|
|
542
|
+
3. nerviq fix --all-critical --auto
|
|
543
|
+
4. nerviq augment
|
|
544
|
+
5. nerviq doctor
|
|
545
|
+
|
|
546
|
+
WHEN YOU ARE READY
|
|
547
|
+
nerviq --help Show the full command set
|
|
548
|
+
Docs: https://nerviq.net/docs/getting-started
|
|
549
|
+
`;
|
|
511
550
|
|
|
512
551
|
async function main() {
|
|
513
552
|
let parsed;
|
|
@@ -518,22 +557,27 @@ async function main() {
|
|
|
518
557
|
process.exit(1);
|
|
519
558
|
}
|
|
520
559
|
|
|
521
|
-
const { flags, command, normalizedCommand } = parsed;
|
|
560
|
+
const { flags, command, commandExplicit, normalizedCommand } = parsed;
|
|
522
561
|
|
|
523
562
|
// Initialize i18n with --lang flag or NERVIQ_LANG env var
|
|
524
563
|
if (parsed.lang) {
|
|
525
564
|
initI18n(parsed.lang);
|
|
526
565
|
}
|
|
527
566
|
|
|
528
|
-
if (flags.includes('--
|
|
529
|
-
console.log(
|
|
530
|
-
process.exit(0);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
if (flags.includes('--
|
|
534
|
-
console.log(
|
|
535
|
-
process.exit(0);
|
|
536
|
-
}
|
|
567
|
+
if (flags.includes('--version') || command === 'version') {
|
|
568
|
+
console.log(version);
|
|
569
|
+
process.exit(0);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (flags.includes('--beginner') && (!commandExplicit || flags.includes('--help') || command === 'help')) {
|
|
573
|
+
console.log(BEGINNER_HELP);
|
|
574
|
+
process.exit(0);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (flags.includes('--help') || command === 'help') {
|
|
578
|
+
console.log(HELP);
|
|
579
|
+
process.exit(0);
|
|
580
|
+
}
|
|
537
581
|
|
|
538
582
|
const options = {
|
|
539
583
|
verbose: flags.includes('--verbose'),
|
|
@@ -746,9 +790,9 @@ async function main() {
|
|
|
746
790
|
process.exit(1);
|
|
747
791
|
}
|
|
748
792
|
process.exit(0);
|
|
749
|
-
} else if (normalizedCommand === 'history') {
|
|
750
|
-
const { formatHistory, readSnapshotIndex } = require('../src/activity');
|
|
751
|
-
// Handle --prune N
|
|
793
|
+
} else if (normalizedCommand === 'history') {
|
|
794
|
+
const { formatHistory, readSnapshotIndex } = require('../src/activity');
|
|
795
|
+
// Handle --prune N
|
|
752
796
|
const pruneIdx = flags.indexOf('--prune');
|
|
753
797
|
if (pruneIdx >= 0) {
|
|
754
798
|
const keepCount = parseInt(flags[pruneIdx + 1] || parsed.extraArgs[0], 10) || 10;
|
|
@@ -756,7 +800,7 @@ async function main() {
|
|
|
756
800
|
const pathMod = require('path');
|
|
757
801
|
const entries = readSnapshotIndex(options.dir);
|
|
758
802
|
if (entries.length <= keepCount) {
|
|
759
|
-
console.log(`\n Nothing to prune (${entries.length} snapshots, keeping ${keepCount}).\n`);
|
|
803
|
+
console.log(`\n Nothing to prune (${entries.length} audit snapshots, keeping ${keepCount}).\n`);
|
|
760
804
|
} else {
|
|
761
805
|
const toRemove = entries.slice(0, entries.length - keepCount);
|
|
762
806
|
let removed = 0;
|
|
@@ -767,42 +811,53 @@ async function main() {
|
|
|
767
811
|
const kept = entries.slice(entries.length - keepCount);
|
|
768
812
|
const indexPath = pathMod.join(options.dir, '.nerviq', 'snapshots', 'index.json');
|
|
769
813
|
try { fsMod.writeFileSync(indexPath, JSON.stringify(kept, null, 2), 'utf8'); } catch {}
|
|
770
|
-
console.log(`\n Pruned ${removed} snapshots, kept ${kept.length}.\n`);
|
|
814
|
+
console.log(`\n Pruned ${removed} audit snapshots, kept ${kept.length}.\n`);
|
|
771
815
|
}
|
|
772
816
|
process.exit(0);
|
|
773
817
|
}
|
|
774
818
|
console.log('');
|
|
775
|
-
console.log(formatHistory(options.dir));
|
|
776
|
-
console.log('');
|
|
777
|
-
process.exit(0);
|
|
778
|
-
} else if (normalizedCommand === 'compare') {
|
|
779
|
-
const { compareLatest } = require('../src/activity');
|
|
780
|
-
const result = compareLatest(options.dir);
|
|
781
|
-
if (!result) {
|
|
782
|
-
console.log('
|
|
783
|
-
|
|
784
|
-
|
|
819
|
+
console.log(formatHistory(options.dir));
|
|
820
|
+
console.log('');
|
|
821
|
+
process.exit(0);
|
|
822
|
+
} else if (normalizedCommand === 'compare') {
|
|
823
|
+
const { compareLatest, formatSnapshotBootstrap } = require('../src/activity');
|
|
824
|
+
const result = compareLatest(options.dir);
|
|
825
|
+
if (!result) {
|
|
826
|
+
console.log('');
|
|
827
|
+
console.log(formatSnapshotBootstrap(options.dir, 'compare'));
|
|
828
|
+
console.log('');
|
|
829
|
+
process.exit(0);
|
|
830
|
+
}
|
|
785
831
|
if (options.json) {
|
|
786
832
|
console.log(JSON.stringify(result, null, 2));
|
|
787
833
|
} else {
|
|
788
834
|
const sign = result.delta.score >= 0 ? '+' : '';
|
|
789
835
|
console.log('');
|
|
790
|
-
console.log(` Previous: ${result.previous.score}/100 (${result.previous.date?.split('T')[0]})`);
|
|
791
|
-
console.log(` Current: ${result.current.score}/100 (${result.current.date?.split('T')[0]})`);
|
|
792
|
-
console.log(`
|
|
793
|
-
console.log(` Trend: ${result.trend}`);
|
|
836
|
+
console.log(` Previous snapshot: ${result.previous.score}/100 (${result.previous.date?.split('T')[0]})`);
|
|
837
|
+
console.log(` Current snapshot: ${result.current.score}/100 (${result.current.date?.split('T')[0]})`);
|
|
838
|
+
console.log(` Snapshot delta: ${sign}${result.delta.score} points`);
|
|
839
|
+
console.log(` Trend: ${result.trend}`);
|
|
794
840
|
if (result.improvements.length > 0) console.log(` Fixed: ${result.improvements.join(', ')}`);
|
|
795
841
|
if (result.regressions.length > 0) console.log(` New gaps: ${result.regressions.join(', ')}`);
|
|
796
842
|
console.log('');
|
|
797
|
-
}
|
|
798
|
-
process.exit(0);
|
|
799
|
-
} else if (normalizedCommand === 'trend') {
|
|
800
|
-
const { exportTrendReport } = require('../src/activity');
|
|
801
|
-
const
|
|
802
|
-
if (
|
|
803
|
-
console.log('
|
|
804
|
-
|
|
805
|
-
|
|
843
|
+
}
|
|
844
|
+
process.exit(0);
|
|
845
|
+
} else if (normalizedCommand === 'trend') {
|
|
846
|
+
const { exportTrendReport, getHistory, formatSnapshotBootstrap } = require('../src/activity');
|
|
847
|
+
const auditHistory = getHistory(options.dir, 2);
|
|
848
|
+
if (auditHistory.length < 2) {
|
|
849
|
+
console.log('');
|
|
850
|
+
console.log(formatSnapshotBootstrap(options.dir, 'trend'));
|
|
851
|
+
console.log('');
|
|
852
|
+
process.exit(0);
|
|
853
|
+
}
|
|
854
|
+
const report = exportTrendReport(options.dir);
|
|
855
|
+
if (!report) {
|
|
856
|
+
console.log('');
|
|
857
|
+
console.log(formatSnapshotBootstrap(options.dir, 'trend'));
|
|
858
|
+
console.log('');
|
|
859
|
+
process.exit(0);
|
|
860
|
+
}
|
|
806
861
|
if (options.out) {
|
|
807
862
|
require('fs').writeFileSync(options.out, report, 'utf8');
|
|
808
863
|
console.log(`\n Trend report exported to ${options.out}\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nerviq/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "The intelligent nervous system for AI coding agents — 2,438 checks (8 platforms × ~300 governance rules), 10 languages, 62 domain packs. Audit, align, and amplify.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
package/src/activity.js
CHANGED
|
@@ -268,8 +268,9 @@ function compareLatest(dir) {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
return {
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
scoreType: 'audit-snapshot-score',
|
|
272
|
+
current: { date: current.createdAt, score: current.summary?.score, passed: current.summary?.passed, scoreType: 'audit-snapshot-score' },
|
|
273
|
+
previous: { date: previous.createdAt, score: previous.summary?.score, passed: previous.summary?.passed, scoreType: 'audit-snapshot-score' },
|
|
273
274
|
delta,
|
|
274
275
|
regressions,
|
|
275
276
|
improvements,
|
|
@@ -277,11 +278,60 @@ function compareLatest(dir) {
|
|
|
277
278
|
};
|
|
278
279
|
}
|
|
279
280
|
|
|
281
|
+
function formatSnapshotBootstrap(dir, goal = 'history') {
|
|
282
|
+
const snapshotCount = getHistory(dir, 50).length;
|
|
283
|
+
const lines = [];
|
|
284
|
+
const snapshotLabel = snapshotCount === 1
|
|
285
|
+
? '1 saved audit snapshot'
|
|
286
|
+
: `${snapshotCount} saved audit snapshots`;
|
|
287
|
+
|
|
288
|
+
if (goal === 'compare') {
|
|
289
|
+
lines.push(snapshotCount === 0
|
|
290
|
+
? 'Compare needs 2 audit snapshots.'
|
|
291
|
+
: 'Compare needs one more audit snapshot.');
|
|
292
|
+
} else if (goal === 'trend') {
|
|
293
|
+
lines.push(snapshotCount === 0
|
|
294
|
+
? 'Trend needs 2 audit snapshots to start.'
|
|
295
|
+
: 'Trend needs one more audit snapshot to become meaningful.');
|
|
296
|
+
} else {
|
|
297
|
+
lines.push(snapshotCount === 0
|
|
298
|
+
? 'No audit snapshots found yet.'
|
|
299
|
+
: 'History is initialized, but compare/trend still need one more snapshot.');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
lines.push(` Current state: ${snapshotLabel}.`);
|
|
303
|
+
|
|
304
|
+
if (snapshotCount === 0) {
|
|
305
|
+
lines.push(' Bootstrap it with:');
|
|
306
|
+
lines.push(' 1. Run `nerviq audit --snapshot` to save the baseline.');
|
|
307
|
+
lines.push(' 2. Make a meaningful repo change (`nerviq setup --auto` or `nerviq fix --all-critical --auto`).');
|
|
308
|
+
lines.push(' 3. Run `nerviq audit --snapshot` again to capture the next state.');
|
|
309
|
+
} else {
|
|
310
|
+
lines.push(' Next:');
|
|
311
|
+
lines.push(' 1. Make a meaningful repo change (`nerviq setup --auto` or `nerviq fix --all-critical --auto`).');
|
|
312
|
+
lines.push(' 2. Run `nerviq audit --snapshot` again.');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (goal === 'compare') {
|
|
316
|
+
lines.push(' Then rerun `nerviq compare`.');
|
|
317
|
+
} else if (goal === 'trend') {
|
|
318
|
+
lines.push(' Then rerun `nerviq trend`.');
|
|
319
|
+
} else {
|
|
320
|
+
lines.push(' Then rerun `nerviq history`, `nerviq compare`, or `nerviq trend`.');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return lines.join('\n');
|
|
324
|
+
}
|
|
325
|
+
|
|
280
326
|
function formatHistory(dir) {
|
|
281
327
|
const history = getHistory(dir, 10);
|
|
282
|
-
if (history.length === 0) return
|
|
328
|
+
if (history.length === 0) return formatSnapshotBootstrap(dir, 'history');
|
|
283
329
|
|
|
284
|
-
const lines = [
|
|
330
|
+
const lines = [
|
|
331
|
+
'Audit snapshot history (most recent first):',
|
|
332
|
+
' Score type: saved audit snapshot scores only (not live audits or benchmark projections).',
|
|
333
|
+
'',
|
|
334
|
+
];
|
|
285
335
|
for (const entry of history) {
|
|
286
336
|
const dateStr = entry.createdAt || 'unknown';
|
|
287
337
|
const date = dateStr.split('T')[0] || 'unknown';
|
|
@@ -290,14 +340,14 @@ function formatHistory(dir) {
|
|
|
290
340
|
const score = entry.summary?.score ?? '?';
|
|
291
341
|
const passed = entry.summary?.passed ?? '?';
|
|
292
342
|
const total = entry.summary?.checkCount ?? '?';
|
|
293
|
-
lines.push(` ${dateDisplay} ${score}/100 (${passed}/${total} passing)`);
|
|
343
|
+
lines.push(` ${dateDisplay} snapshot ${score}/100 (${passed}/${total} checks passing)`);
|
|
294
344
|
}
|
|
295
345
|
|
|
296
346
|
const comparison = compareLatest(dir);
|
|
297
347
|
if (comparison) {
|
|
298
348
|
lines.push('');
|
|
299
349
|
const sign = comparison.delta.score >= 0 ? '+' : '';
|
|
300
|
-
lines.push(`
|
|
350
|
+
lines.push(` Latest snapshot trend: ${comparison.trend} (${sign}${comparison.delta.score} since previous snapshot)`);
|
|
301
351
|
if (comparison.improvements.length > 0) {
|
|
302
352
|
lines.push(` Fixed: ${comparison.improvements.join(', ')}`);
|
|
303
353
|
}
|
|
@@ -306,6 +356,11 @@ function formatHistory(dir) {
|
|
|
306
356
|
}
|
|
307
357
|
}
|
|
308
358
|
|
|
359
|
+
if (history.length === 1) {
|
|
360
|
+
lines.push('');
|
|
361
|
+
lines.push(formatSnapshotBootstrap(dir, 'history'));
|
|
362
|
+
}
|
|
363
|
+
|
|
309
364
|
return lines.join('\n');
|
|
310
365
|
}
|
|
311
366
|
|
|
@@ -315,13 +370,13 @@ function exportTrendReport(dir) {
|
|
|
315
370
|
|
|
316
371
|
const comparison = compareLatest(dir);
|
|
317
372
|
const lines = [
|
|
318
|
-
'#
|
|
373
|
+
'# Nerviq Audit Snapshot Trend Report',
|
|
319
374
|
'',
|
|
320
375
|
`**Project:** ${path.basename(dir)}`,
|
|
321
376
|
`**Generated:** ${new Date().toISOString().split('T')[0]}`,
|
|
322
|
-
`**
|
|
377
|
+
`**Audit snapshots:** ${history.length}`,
|
|
323
378
|
'',
|
|
324
|
-
'##
|
|
379
|
+
'## Audit Snapshot History',
|
|
325
380
|
'',
|
|
326
381
|
'| Date | Score | Passed | Checks |',
|
|
327
382
|
'|------|-------|--------|--------|',
|
|
@@ -336,9 +391,9 @@ function exportTrendReport(dir) {
|
|
|
336
391
|
lines.push('');
|
|
337
392
|
lines.push('## Latest Comparison');
|
|
338
393
|
lines.push('');
|
|
339
|
-
lines.push(`- **Previous:** ${comparison.previous.score}/100 (${comparison.previous.date?.split('T')[0]})`);
|
|
340
|
-
lines.push(`- **Current:** ${comparison.current.score}/100 (${comparison.current.date?.split('T')[0]})`);
|
|
341
|
-
lines.push(`- **
|
|
394
|
+
lines.push(`- **Previous snapshot score:** ${comparison.previous.score}/100 (${comparison.previous.date?.split('T')[0]})`);
|
|
395
|
+
lines.push(`- **Current snapshot score:** ${comparison.current.score}/100 (${comparison.current.date?.split('T')[0]})`);
|
|
396
|
+
lines.push(`- **Snapshot delta:** ${comparison.delta.score >= 0 ? '+' : ''}${comparison.delta.score} points`);
|
|
342
397
|
lines.push(`- **Trend:** ${comparison.trend}`);
|
|
343
398
|
if (comparison.improvements.length > 0) lines.push(`- **Fixed:** ${comparison.improvements.join(', ')}`);
|
|
344
399
|
if (comparison.regressions.length > 0) lines.push(`- **New gaps:** ${comparison.regressions.join(', ')}`);
|
|
@@ -803,6 +858,7 @@ module.exports = {
|
|
|
803
858
|
readSnapshotIndex,
|
|
804
859
|
getHistory,
|
|
805
860
|
compareLatest,
|
|
861
|
+
formatSnapshotBootstrap,
|
|
806
862
|
formatHistory,
|
|
807
863
|
exportTrendReport,
|
|
808
864
|
readOutcomeIndex,
|