@vibecheckai/cli 3.6.1 → 3.8.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 +135 -63
- package/bin/_deprecations.js +447 -19
- package/bin/_router.js +1 -1
- package/bin/registry.js +347 -280
- package/bin/runners/context/generators/cursor-enhanced.js +2439 -0
- package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
- package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
- package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
- package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
- package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
- package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
- package/bin/runners/lib/agent-firewall/index.js +200 -0
- package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
- package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
- package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +622 -0
- package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
- package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
- package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
- package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
- package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
- package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +31 -38
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +68 -3
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +4 -2
- package/bin/runners/lib/agent-firewall/risk/thresholds.js +5 -4
- package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
- package/bin/runners/lib/agent-firewall/session/index.js +26 -0
- package/bin/runners/lib/artifact-envelope.js +540 -0
- package/bin/runners/lib/auth-shared.js +977 -0
- package/bin/runners/lib/checkpoint.js +941 -0
- package/bin/runners/lib/cleanup/engine.js +571 -0
- package/bin/runners/lib/cleanup/index.js +53 -0
- package/bin/runners/lib/cleanup/output.js +375 -0
- package/bin/runners/lib/cleanup/rules.js +1060 -0
- package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
- package/bin/runners/lib/doctor/failure-signatures.js +526 -0
- package/bin/runners/lib/doctor/fix-script.js +336 -0
- package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
- package/bin/runners/lib/doctor/modules/index.js +62 -3
- package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
- package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
- package/bin/runners/lib/doctor/safe-repair.js +384 -0
- package/bin/runners/lib/engines/attack-detector.js +1192 -0
- package/bin/runners/lib/entitlements-v2.js +2 -2
- package/bin/runners/lib/error-messages.js +1 -1
- package/bin/runners/lib/missions/briefing.js +427 -0
- package/bin/runners/lib/missions/checkpoint.js +753 -0
- package/bin/runners/lib/missions/hardening.js +851 -0
- package/bin/runners/lib/missions/plan.js +421 -32
- package/bin/runners/lib/missions/safety-gates.js +645 -0
- package/bin/runners/lib/missions/schema.js +478 -0
- package/bin/runners/lib/packs/bundle.js +675 -0
- package/bin/runners/lib/packs/evidence-pack.js +671 -0
- package/bin/runners/lib/packs/pack-factory.js +837 -0
- package/bin/runners/lib/packs/permissions-pack.js +686 -0
- package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
- package/bin/runners/lib/report-output.js +6 -6
- package/bin/runners/lib/safelist/index.js +96 -0
- package/bin/runners/lib/safelist/integration.js +334 -0
- package/bin/runners/lib/safelist/matcher.js +696 -0
- package/bin/runners/lib/safelist/schema.js +948 -0
- package/bin/runners/lib/safelist/store.js +438 -0
- package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
- package/bin/runners/lib/ship-gate.js +832 -0
- package/bin/runners/lib/ship-manifest.js +1153 -0
- package/bin/runners/lib/ship-output.js +1 -1
- package/bin/runners/lib/unified-cli-output.js +710 -383
- package/bin/runners/lib/upsell.js +3 -3
- package/bin/runners/lib/why-tree.js +650 -0
- package/bin/runners/runAllowlist.js +33 -4
- package/bin/runners/runApprove.js +240 -1122
- package/bin/runners/runAudit.js +692 -0
- package/bin/runners/runAuth.js +325 -29
- package/bin/runners/runCheckpoint.js +442 -494
- package/bin/runners/runCleanup.js +343 -0
- package/bin/runners/runDoctor.js +269 -19
- package/bin/runners/runFix.js +411 -32
- package/bin/runners/runForge.js +411 -0
- package/bin/runners/runIntent.js +906 -0
- package/bin/runners/runKickoff.js +878 -0
- package/bin/runners/runLaunch.js +2000 -0
- package/bin/runners/runLink.js +785 -0
- package/bin/runners/runMcp.js +1741 -837
- package/bin/runners/runPacks.js +2089 -0
- package/bin/runners/runPolish.js +41 -0
- package/bin/runners/runSafelist.js +1190 -0
- package/bin/runners/runScan.js +21 -9
- package/bin/runners/runShield.js +1282 -0
- package/bin/runners/runShip.js +395 -16
- package/bin/vibecheck.js +34 -6
- package/mcp-server/README.md +117 -158
- package/mcp-server/handlers/tool-handler.ts +3 -3
- package/mcp-server/index.js +16 -0
- package/mcp-server/intent-firewall-interceptor.js +529 -0
- package/mcp-server/manifest.json +473 -0
- package/mcp-server/package.json +1 -1
- package/mcp-server/registry/tool-registry.js +315 -523
- package/mcp-server/registry/tools.json +442 -428
- package/mcp-server/tier-auth.js +164 -16
- package/mcp-server/tools-v3.js +70 -16
- package/package.json +1 -1
- package/bin/runners/runProof.zip +0 -0
package/bin/runners/runShip.js
CHANGED
|
@@ -22,6 +22,46 @@ const {
|
|
|
22
22
|
} = require("./lib/cli-output");
|
|
23
23
|
const { EXIT, verdictToExitCode, exitCodeToVerdict } = require("./lib/exit-codes");
|
|
24
24
|
|
|
25
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
26
|
+
// NEW: Ship Gate System (v2) - Deterministic, Evidence-backed
|
|
27
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
28
|
+
let _shipGate = null;
|
|
29
|
+
let _shipManifest = null;
|
|
30
|
+
let _whyTree = null;
|
|
31
|
+
|
|
32
|
+
function getShipGate() {
|
|
33
|
+
if (!_shipGate) {
|
|
34
|
+
try {
|
|
35
|
+
_shipGate = require("./lib/ship-gate");
|
|
36
|
+
} catch {
|
|
37
|
+
_shipGate = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return _shipGate;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getShipManifest() {
|
|
44
|
+
if (!_shipManifest) {
|
|
45
|
+
try {
|
|
46
|
+
_shipManifest = require("./lib/ship-manifest");
|
|
47
|
+
} catch {
|
|
48
|
+
_shipManifest = false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return _shipManifest;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getWhyTree() {
|
|
55
|
+
if (!_whyTree) {
|
|
56
|
+
try {
|
|
57
|
+
_whyTree = require("./lib/why-tree");
|
|
58
|
+
} catch {
|
|
59
|
+
_whyTree = false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return _whyTree;
|
|
63
|
+
}
|
|
64
|
+
|
|
25
65
|
// Route Truth - Fake endpoint detection (V2 engine-aware)
|
|
26
66
|
const { buildTruthpackSmart, writeTruthpack, detectFastifyEntry } = require("./lib/truth");
|
|
27
67
|
const {
|
|
@@ -73,20 +113,23 @@ const {
|
|
|
73
113
|
} = require("./lib/ship-output-enterprise");
|
|
74
114
|
|
|
75
115
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
76
|
-
//
|
|
116
|
+
// UNIFIED CLI OUTPUT
|
|
77
117
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
78
118
|
|
|
79
|
-
|
|
119
|
+
let _cliOutput = null;
|
|
120
|
+
function getCliOutput() {
|
|
121
|
+
if (!_cliOutput) _cliOutput = require("./lib/unified-cli-output");
|
|
122
|
+
return _cliOutput;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Fallback banner if unified not available
|
|
126
|
+
const BANNER_FALLBACK = `
|
|
80
127
|
${ansi.rgb(0, 255, 200)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${ansi.reset}
|
|
81
128
|
${ansi.rgb(0, 230, 220)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${ansi.reset}
|
|
82
129
|
${ansi.rgb(0, 200, 255)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${ansi.reset}
|
|
83
130
|
${ansi.rgb(50, 150, 255)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${ansi.reset}
|
|
84
131
|
${ansi.rgb(100, 100, 255)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${ansi.reset}
|
|
85
132
|
${ansi.rgb(150, 50, 255)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${ansi.reset}
|
|
86
|
-
|
|
87
|
-
${ansi.dim} ┌─────────────────────────────────────────────────────────────────────┐${ansi.reset}
|
|
88
|
-
${ansi.dim} │${ansi.reset} ${ansi.rgb(0, 255, 200)}🚀${ansi.reset} ${ansi.bold}SHIP${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(200, 200, 200)}The One Command${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(150, 150, 150)}Zero Config${ansi.reset} ${ansi.dim}•${ansi.reset} ${ansi.rgb(100, 100, 100)}Plain English${ansi.reset} ${ansi.dim}│${ansi.reset}
|
|
89
|
-
${ansi.dim} └─────────────────────────────────────────────────────────────────────┘${ansi.reset}
|
|
90
133
|
`;
|
|
91
134
|
|
|
92
135
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -210,8 +253,8 @@ function stopSpinner(message, success = true) {
|
|
|
210
253
|
spinnerStartTime = null;
|
|
211
254
|
}
|
|
212
255
|
|
|
213
|
-
// Compact banner for fix mode
|
|
214
|
-
const
|
|
256
|
+
// Compact banner for fix mode (fallback)
|
|
257
|
+
const SHIP_BANNER_FALLBACK = `
|
|
215
258
|
${ansi.rgb(0, 255, 200)} ███████╗██╗ ██╗██╗██████╗ ${ansi.reset}
|
|
216
259
|
${ansi.rgb(0, 230, 220)} ██╔════╝██║ ██║██║██╔══██╗${ansi.reset}
|
|
217
260
|
${ansi.rgb(0, 200, 255)} ███████╗███████║██║██████╔╝${ansi.reset}
|
|
@@ -220,8 +263,23 @@ ${ansi.rgb(100, 100, 255)} ███████║██║ ██║██
|
|
|
220
263
|
${ansi.rgb(150, 50, 255)} ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ${ansi.reset}
|
|
221
264
|
`;
|
|
222
265
|
|
|
223
|
-
function printBanner(compact = false) {
|
|
224
|
-
|
|
266
|
+
function printBanner(compact = false, projectRoot = process.cwd()) {
|
|
267
|
+
if (compact) {
|
|
268
|
+
console.log(SHIP_BANNER_FALLBACK);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const cli = getCliOutput();
|
|
274
|
+
console.log(cli.renderFullHeader("ship", {
|
|
275
|
+
version: "4.0.0",
|
|
276
|
+
tier: "FREE",
|
|
277
|
+
target: projectRoot,
|
|
278
|
+
sessionId: cli.generateSessionId(),
|
|
279
|
+
}));
|
|
280
|
+
} catch {
|
|
281
|
+
console.log(BANNER_FALLBACK);
|
|
282
|
+
}
|
|
225
283
|
}
|
|
226
284
|
|
|
227
285
|
function printDivider(char = '─', width = 69, color = c.dim) {
|
|
@@ -627,15 +685,26 @@ function printHelp(showBanner = true) {
|
|
|
627
685
|
|
|
628
686
|
${ansi.bold}The One Command${ansi.reset} — Get a ship verdict: ${colors.success}SHIP${ansi.reset} | ${colors.warning}WARN${ansi.reset} | ${colors.error}BLOCK${ansi.reset}
|
|
629
687
|
|
|
630
|
-
${ansi.bold}Options:${ansi.reset}
|
|
688
|
+
${ansi.bold}Verdict Options:${ansi.reset}
|
|
689
|
+
${colors.accent}--strict${ansi.reset} Treat warnings as blockers
|
|
690
|
+
${colors.accent}--why${ansi.reset} Show detailed "why tree" (top blockers + evidence)
|
|
691
|
+
${colors.accent}--cached${ansi.reset} Use cached verdict if repo unchanged
|
|
692
|
+
|
|
693
|
+
${ansi.bold}Output Options:${ansi.reset}
|
|
694
|
+
${colors.accent}--json${ansi.reset} Output full manifest as JSON
|
|
695
|
+
${colors.accent}--ci${ansi.reset} Machine output (KEY=VALUE format for CI/CD)
|
|
696
|
+
${colors.accent}--receipt${ansi.reset} Single-line receipt (parseable)
|
|
697
|
+
${colors.accent}--manifest${ansi.reset} Output full signed manifest
|
|
698
|
+
${colors.accent}--badge, -b${ansi.reset} Generate embeddable badge for README
|
|
699
|
+
|
|
700
|
+
${ansi.bold}Fix Options:${ansi.reset}
|
|
631
701
|
${colors.accent}--fix, -f${ansi.reset} Try safe mechanical fixes ${ansi.dim}(shows plan first)${ansi.reset}
|
|
632
702
|
${colors.accent}--assist${ansi.reset} Generate AI mission prompts for complex issues
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
${colors.accent}--ci${ansi.reset} Machine output for CI/CD pipelines
|
|
636
|
-
${colors.accent}--json${ansi.reset} Output results as JSON
|
|
703
|
+
|
|
704
|
+
${ansi.bold}General Options:${ansi.reset}
|
|
637
705
|
${colors.accent}--path, -p${ansi.reset} Project path ${ansi.dim}(default: current directory)${ansi.reset}
|
|
638
706
|
${colors.accent}--verbose, -v${ansi.reset} Show detailed progress
|
|
707
|
+
${colors.accent}--legacy${ansi.reset} Use legacy (non-gate) evaluation
|
|
639
708
|
${colors.accent}--help, -h${ansi.reset} Show this help
|
|
640
709
|
|
|
641
710
|
${ansi.bold}Exit Codes:${ansi.reset}
|
|
@@ -643,10 +712,23 @@ function printHelp(showBanner = true) {
|
|
|
643
712
|
${colors.warning}1${ansi.reset} WARN — Warnings found, review recommended
|
|
644
713
|
${colors.error}2${ansi.reset} BLOCK — Blockers found, must fix before shipping
|
|
645
714
|
|
|
715
|
+
${ansi.bold}CI Integration:${ansi.reset}
|
|
716
|
+
${ansi.dim}# GitHub Actions - set output variables${ansi.reset}
|
|
717
|
+
vibecheck ship --ci >> $$GITHUB_OUTPUT
|
|
718
|
+
|
|
719
|
+
${ansi.dim}# Get receipt for artifact storage${ansi.reset}
|
|
720
|
+
vibecheck ship --receipt > .vibecheck/receipt.txt
|
|
721
|
+
|
|
722
|
+
${ansi.dim}# Cache-aware evaluation (fast re-runs)${ansi.reset}
|
|
723
|
+
vibecheck ship --cached
|
|
724
|
+
|
|
646
725
|
${ansi.bold}Examples:${ansi.reset}
|
|
647
726
|
${ansi.dim}# Quick ship check${ansi.reset}
|
|
648
727
|
vibecheck ship
|
|
649
728
|
|
|
729
|
+
${ansi.dim}# Show why a verdict was reached${ansi.reset}
|
|
730
|
+
vibecheck ship --why
|
|
731
|
+
|
|
650
732
|
${ansi.dim}# Auto-fix what can be fixed${ansi.reset}
|
|
651
733
|
vibecheck ship --fix
|
|
652
734
|
|
|
@@ -655,6 +737,11 @@ function printHelp(showBanner = true) {
|
|
|
655
737
|
|
|
656
738
|
${ansi.dim}# Strict CI mode (warnings = failure)${ansi.reset}
|
|
657
739
|
vibecheck ship --strict --ci
|
|
740
|
+
|
|
741
|
+
${ansi.bold}Artifacts Generated:${ansi.reset}
|
|
742
|
+
${ansi.dim}.vibecheck/ship/manifest.json${ansi.reset} Full signed manifest
|
|
743
|
+
${ansi.dim}.vibecheck/ship/receipt.txt${ansi.reset} CI-friendly receipt
|
|
744
|
+
${ansi.dim}.vibecheck/ship/github-output.txt${ansi.reset} GitHub Actions output
|
|
658
745
|
`);
|
|
659
746
|
}
|
|
660
747
|
|
|
@@ -784,6 +871,12 @@ function parseArgs(args) {
|
|
|
784
871
|
help: globalFlags.help || false,
|
|
785
872
|
noBanner: globalFlags.noBanner || false,
|
|
786
873
|
quiet: globalFlags.quiet || false,
|
|
874
|
+
// NEW: Gate mode options
|
|
875
|
+
useGate: true, // Use new gate system by default
|
|
876
|
+
cached: false, // Use cached verdict if valid
|
|
877
|
+
manifest: false, // Output full manifest
|
|
878
|
+
receipt: false, // Output CI receipt
|
|
879
|
+
whyTree: false, // Show detailed why tree
|
|
787
880
|
};
|
|
788
881
|
|
|
789
882
|
// Parse command-specific args
|
|
@@ -799,6 +892,12 @@ function parseArgs(args) {
|
|
|
799
892
|
}
|
|
800
893
|
else if (a.startsWith("--path=")) opts.path = a.split("=")[1];
|
|
801
894
|
else if (a === "--path" || a === "-p") opts.path = args[++i];
|
|
895
|
+
// NEW: Gate options
|
|
896
|
+
else if (a === "--legacy") opts.useGate = false;
|
|
897
|
+
else if (a === "--cached") opts.cached = true;
|
|
898
|
+
else if (a === "--manifest") opts.manifest = true;
|
|
899
|
+
else if (a === "--receipt") opts.receipt = true;
|
|
900
|
+
else if (a === "--why") opts.whyTree = true;
|
|
802
901
|
}
|
|
803
902
|
|
|
804
903
|
return opts;
|
|
@@ -822,6 +921,24 @@ async function runShip(args, context = {}) {
|
|
|
822
921
|
return 0;
|
|
823
922
|
}
|
|
824
923
|
|
|
924
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
925
|
+
// NEW: Use Ship Gate (v2) system for deterministic, evidence-backed verdicts
|
|
926
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
927
|
+
if (opts.useGate && !opts.fix && !opts.assist) {
|
|
928
|
+
try {
|
|
929
|
+
const gateResult = await runShipGate(opts, context);
|
|
930
|
+
if (gateResult !== null) {
|
|
931
|
+
return gateResult;
|
|
932
|
+
}
|
|
933
|
+
// If null returned, fall through to legacy mode
|
|
934
|
+
} catch (err) {
|
|
935
|
+
if (opts.verbose) {
|
|
936
|
+
console.error(` Gate evaluation failed: ${err.message}, using legacy mode`);
|
|
937
|
+
}
|
|
938
|
+
// Fall through to legacy mode
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
825
942
|
// Entitlement check
|
|
826
943
|
try {
|
|
827
944
|
await enforceLimit('scans');
|
|
@@ -1204,6 +1321,141 @@ async function runAutoFix(projectPath, results, outputDir, findings) {
|
|
|
1204
1321
|
return fixResults;
|
|
1205
1322
|
}
|
|
1206
1323
|
|
|
1324
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1325
|
+
// SHIP GATE (v2) - Deterministic, Evidence-backed Verdict
|
|
1326
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1327
|
+
|
|
1328
|
+
/**
|
|
1329
|
+
* Run ship using the new gate system.
|
|
1330
|
+
*
|
|
1331
|
+
* The gate system provides:
|
|
1332
|
+
* - Deterministic verdicts (same repo state → same verdict)
|
|
1333
|
+
* - Evidence-backed decisions (why tree)
|
|
1334
|
+
* - CI-ready artifacts (manifest, receipt, badge)
|
|
1335
|
+
*
|
|
1336
|
+
* @param {Object} opts - Options from parseArgs
|
|
1337
|
+
* @param {Object} context - Execution context
|
|
1338
|
+
* @returns {number} Exit code
|
|
1339
|
+
*/
|
|
1340
|
+
async function runShipGate(opts, context = {}) {
|
|
1341
|
+
const shipGate = getShipGate();
|
|
1342
|
+
const shipManifest = getShipManifest();
|
|
1343
|
+
const whyTreeModule = getWhyTree();
|
|
1344
|
+
|
|
1345
|
+
if (!shipGate || !shipManifest) {
|
|
1346
|
+
// Fall back to legacy mode if gate modules not available
|
|
1347
|
+
console.error(" Ship gate modules not available, using legacy mode");
|
|
1348
|
+
return null; // Signal to use legacy
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
const projectPath = path.resolve(opts.path);
|
|
1352
|
+
const startTime = Date.now();
|
|
1353
|
+
|
|
1354
|
+
// Check cache first if requested
|
|
1355
|
+
if (opts.cached) {
|
|
1356
|
+
const cacheResult = shipGate.checkVerdictCache(projectPath);
|
|
1357
|
+
if (cacheResult.valid) {
|
|
1358
|
+
const manifest = cacheResult.manifest;
|
|
1359
|
+
|
|
1360
|
+
if (opts.json || opts.manifest) {
|
|
1361
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
1362
|
+
} else if (opts.receipt) {
|
|
1363
|
+
console.log(shipManifest.generateCIReceipt(manifest));
|
|
1364
|
+
} else if (!opts.quiet) {
|
|
1365
|
+
console.log(` Using cached verdict: ${manifest.verdict.status} (${cacheResult.age}ms old)`);
|
|
1366
|
+
console.log(` Fingerprint: ${manifest.repo.fingerprint}`);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
return manifest.verdict.exitCode;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
if (!opts.quiet && !opts.json) {
|
|
1373
|
+
console.log(` Cache invalid: ${cacheResult.reason}`);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Run full gate evaluation
|
|
1378
|
+
const gate = new shipGate.ShipGate(projectPath, {
|
|
1379
|
+
strict: opts.strict,
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
const result = await gate.evaluate({
|
|
1383
|
+
skipReality: !opts.withRuntime,
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
// Output based on mode
|
|
1387
|
+
if (opts.json || opts.manifest) {
|
|
1388
|
+
console.log(JSON.stringify(result.manifest, null, 2));
|
|
1389
|
+
return result.exitCode;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
if (opts.receipt) {
|
|
1393
|
+
console.log(shipManifest.generateCIReceipt(result.manifest));
|
|
1394
|
+
return result.exitCode;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
if (opts.ci) {
|
|
1398
|
+
// CI mode: minimal key=value output
|
|
1399
|
+
const envVars = shipManifest.generateCIEnvVars(result.manifest);
|
|
1400
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
1401
|
+
console.log(`${key}=${value}`);
|
|
1402
|
+
}
|
|
1403
|
+
return result.exitCode;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// Human-readable output
|
|
1407
|
+
if (!opts.quiet) {
|
|
1408
|
+
// Print verdict card
|
|
1409
|
+
printVerdictCard(
|
|
1410
|
+
result.verdict,
|
|
1411
|
+
result.score,
|
|
1412
|
+
result.evidence.audit.blockers,
|
|
1413
|
+
result.evidence.audit.warnings,
|
|
1414
|
+
result.durationMs
|
|
1415
|
+
);
|
|
1416
|
+
|
|
1417
|
+
// Print why tree if requested or if blocked
|
|
1418
|
+
if (opts.whyTree || result.verdict !== "SHIP") {
|
|
1419
|
+
if (whyTreeModule) {
|
|
1420
|
+
const whyTree = whyTreeModule.buildWhyTree(result.findings);
|
|
1421
|
+
console.log(whyTreeModule.renderWhyTreeTerminal(whyTree));
|
|
1422
|
+
} else {
|
|
1423
|
+
// Fallback: simple why tree from manifest
|
|
1424
|
+
const why = result.manifest.evidence.whyTree;
|
|
1425
|
+
console.log(`\n ${ansi.bold}WHY: ${result.verdict}${ansi.reset}`);
|
|
1426
|
+
console.log(` ${ansi.dim}${why.summary}${ansi.reset}\n`);
|
|
1427
|
+
|
|
1428
|
+
for (const issue of why.topIssues || []) {
|
|
1429
|
+
console.log(` ${colors.blockRed}•${ansi.reset} ${issue.title}`);
|
|
1430
|
+
if (issue.evidence?.file) {
|
|
1431
|
+
console.log(` ${ansi.dim}→ ${issue.evidence.file}:${issue.evidence.lines || ""}${ansi.reset}`);
|
|
1432
|
+
}
|
|
1433
|
+
if (issue.fixHint) {
|
|
1434
|
+
console.log(` ${colors.shipGreen}Fix: ${issue.fixHint}${ansi.reset}`);
|
|
1435
|
+
}
|
|
1436
|
+
console.log();
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// Print artifact paths
|
|
1442
|
+
console.log(` ${ansi.dim}Artifacts:${ansi.reset}`);
|
|
1443
|
+
console.log(` ${ansi.dim}Manifest: ${result.artifactPaths.manifest}${ansi.reset}`);
|
|
1444
|
+
console.log(` ${ansi.dim}Receipt: ${result.artifactPaths.receipt}${ansi.reset}`);
|
|
1445
|
+
console.log();
|
|
1446
|
+
|
|
1447
|
+
// Badge info if requested
|
|
1448
|
+
if (opts.badge) {
|
|
1449
|
+
const badgeInfo = shipManifest.generateBadgeInfo(result.manifest);
|
|
1450
|
+
console.log(` ${ansi.bold}Badge:${ansi.reset}`);
|
|
1451
|
+
console.log(` ${badgeInfo.markdown}`);
|
|
1452
|
+
console.log();
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
return result.exitCode;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1207
1459
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1208
1460
|
// SHIP CORE (for programmatic use)
|
|
1209
1461
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -1275,7 +1527,134 @@ async function shipCore({ repoRoot, fastifyEntry, jsonOut, noWrite } = {}) {
|
|
|
1275
1527
|
// EXPORTS
|
|
1276
1528
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1277
1529
|
|
|
1530
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1531
|
+
// SEAL COMMAND - Badge and Attestation Generator
|
|
1532
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1533
|
+
|
|
1534
|
+
/**
|
|
1535
|
+
* Generate seal (badge) and attestation from the latest ship manifest.
|
|
1536
|
+
*
|
|
1537
|
+
* @param {string[]} args - Command arguments
|
|
1538
|
+
* @param {Object} context - Execution context
|
|
1539
|
+
* @returns {number} Exit code
|
|
1540
|
+
*/
|
|
1541
|
+
async function runSeal(args = [], context = {}) {
|
|
1542
|
+
const { parseGlobalFlags } = require("./lib/global-flags");
|
|
1543
|
+
const { EXIT } = require("./lib/exit-codes");
|
|
1544
|
+
|
|
1545
|
+
const { flags: globalFlags } = parseGlobalFlags(args);
|
|
1546
|
+
const quiet = globalFlags.quiet || args.includes("--quiet") || args.includes("-q");
|
|
1547
|
+
const json = globalFlags.json || args.includes("--json");
|
|
1548
|
+
const projectRoot = context.repoRoot || globalFlags.path || process.cwd();
|
|
1549
|
+
|
|
1550
|
+
const shipManifest = getShipManifest();
|
|
1551
|
+
|
|
1552
|
+
if (!shipManifest) {
|
|
1553
|
+
if (!quiet) {
|
|
1554
|
+
console.error(" Seal module not available");
|
|
1555
|
+
}
|
|
1556
|
+
return EXIT.INTERNAL_ERROR;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
// Load latest manifest
|
|
1560
|
+
const manifest = shipManifest.loadShipManifest(projectRoot);
|
|
1561
|
+
|
|
1562
|
+
if (!manifest) {
|
|
1563
|
+
if (json) {
|
|
1564
|
+
console.log(JSON.stringify({
|
|
1565
|
+
success: false,
|
|
1566
|
+
error: "No ship manifest found. Run 'vibecheck ship' first."
|
|
1567
|
+
}));
|
|
1568
|
+
} else if (!quiet) {
|
|
1569
|
+
console.error(" No ship manifest found. Run 'vibecheck ship' first.");
|
|
1570
|
+
}
|
|
1571
|
+
return EXIT.NOT_FOUND;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// Verify signature
|
|
1575
|
+
const verification = shipManifest.verifyManifestSignature(manifest);
|
|
1576
|
+
|
|
1577
|
+
// Generate badge info
|
|
1578
|
+
const badge = shipManifest.generateBadgeInfo(manifest);
|
|
1579
|
+
|
|
1580
|
+
// Build seal output
|
|
1581
|
+
const seal = {
|
|
1582
|
+
verdict: manifest.verdict.status,
|
|
1583
|
+
score: manifest.verdict.score,
|
|
1584
|
+
canShip: manifest.verdict.canShip,
|
|
1585
|
+
|
|
1586
|
+
// Repo info
|
|
1587
|
+
repo: {
|
|
1588
|
+
name: manifest.repo.name,
|
|
1589
|
+
commit: manifest.repo.commit,
|
|
1590
|
+
branch: manifest.repo.branch,
|
|
1591
|
+
fingerprint: manifest.repo.fingerprint,
|
|
1592
|
+
},
|
|
1593
|
+
|
|
1594
|
+
// Badge URLs
|
|
1595
|
+
badge: {
|
|
1596
|
+
shields: badge.url.shields,
|
|
1597
|
+
badgen: badge.url.badgen,
|
|
1598
|
+
markdown: badge.markdown,
|
|
1599
|
+
},
|
|
1600
|
+
|
|
1601
|
+
// Attestation
|
|
1602
|
+
attestation: {
|
|
1603
|
+
signatureValid: verification.valid,
|
|
1604
|
+
digest: manifest.signature.digest,
|
|
1605
|
+
algorithm: manifest.signature.algorithm,
|
|
1606
|
+
generatedAt: manifest.meta.generatedAt,
|
|
1607
|
+
},
|
|
1608
|
+
|
|
1609
|
+
// Evidence summary
|
|
1610
|
+
evidence: {
|
|
1611
|
+
blockers: manifest.evidence.summary.blockers,
|
|
1612
|
+
warnings: manifest.evidence.summary.warnings,
|
|
1613
|
+
totalFindings: manifest.evidence.summary.total,
|
|
1614
|
+
},
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
if (json) {
|
|
1618
|
+
console.log(JSON.stringify(seal, null, 2));
|
|
1619
|
+
} else if (!quiet) {
|
|
1620
|
+
console.log(`
|
|
1621
|
+
${ansi.bold}${ansi.cyan}╔═══════════════════════════════════════════════════════════════════╗
|
|
1622
|
+
║ ║
|
|
1623
|
+
║ SHIP SEAL ║
|
|
1624
|
+
║ ║
|
|
1625
|
+
╚═══════════════════════════════════════════════════════════════════╝${ansi.reset}
|
|
1626
|
+
|
|
1627
|
+
${ansi.bold}Verdict:${ansi.reset} ${seal.verdict === "SHIP" ? colors.shipGreen : seal.verdict === "WARN" ? colors.warnAmber : colors.blockRed}${seal.verdict}${ansi.reset}
|
|
1628
|
+
${ansi.bold}Score:${ansi.reset} ${seal.score}/100
|
|
1629
|
+
|
|
1630
|
+
${ansi.bold}Repository${ansi.reset}
|
|
1631
|
+
${ansi.dim}Name:${ansi.reset} ${seal.repo.name}
|
|
1632
|
+
${ansi.dim}Commit:${ansi.reset} ${seal.repo.commit}
|
|
1633
|
+
${ansi.dim}Branch:${ansi.reset} ${seal.repo.branch}
|
|
1634
|
+
${ansi.dim}Fingerprint:${ansi.reset} ${seal.repo.fingerprint}
|
|
1635
|
+
|
|
1636
|
+
${ansi.bold}Attestation${ansi.reset}
|
|
1637
|
+
${ansi.dim}Signature:${ansi.reset} ${verification.valid ? colors.shipGreen + "✓ Valid" : colors.blockRed + "✗ Invalid"}${ansi.reset}
|
|
1638
|
+
${ansi.dim}Digest:${ansi.reset} ${seal.attestation.digest}
|
|
1639
|
+
${ansi.dim}Generated:${ansi.reset} ${seal.attestation.generatedAt}
|
|
1640
|
+
|
|
1641
|
+
${ansi.bold}Badge (copy to README.md):${ansi.reset}
|
|
1642
|
+
${colors.shipGreen}${seal.badge.markdown}${ansi.reset}
|
|
1643
|
+
|
|
1644
|
+
${ansi.dim}Evidence: ${seal.evidence.blockers} blockers, ${seal.evidence.warnings} warnings${ansi.reset}
|
|
1645
|
+
`);
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
return manifest.verdict.exitCode;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1652
|
+
// EXPORTS
|
|
1653
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1654
|
+
|
|
1278
1655
|
module.exports = {
|
|
1279
1656
|
runShip: withErrorHandling(runShip, "Ship check failed"),
|
|
1280
|
-
|
|
1657
|
+
runSeal: withErrorHandling(runSeal, "Seal generation failed"),
|
|
1658
|
+
runShipGate,
|
|
1659
|
+
shipCore,
|
|
1281
1660
|
};
|
package/bin/vibecheck.js
CHANGED
|
@@ -946,7 +946,7 @@ ${c.dim}${sym.boxHorizontal.repeat(64)}${c.reset}
|
|
|
946
946
|
${c.green}${sym.star} PRICING TIERS${c.reset}
|
|
947
947
|
|
|
948
948
|
${c.green}FREE${c.reset} ${c.dim}$0${c.reset} scan, ship, report, context, doctor, polish - full visibility
|
|
949
|
-
${c.magenta}PRO${c.reset} ${c.dim}$
|
|
949
|
+
${c.magenta}PRO${c.reset} ${c.dim}$49/mo${c.reset} + autofix, prove, reality, mcp, guard, ai-test, PR comments
|
|
950
950
|
|
|
951
951
|
${c.green}${sym.rocket} QUICK START (2 minutes to first proof)${c.reset}
|
|
952
952
|
|
|
@@ -1247,14 +1247,42 @@ async function main() {
|
|
|
1247
1247
|
|
|
1248
1248
|
// Load registry for command resolution
|
|
1249
1249
|
const registry = getRegistry();
|
|
1250
|
-
const { COMMANDS, ALIAS_MAP, ALL_COMMANDS } = registry;
|
|
1250
|
+
const { COMMANDS, ALIAS_MAP, ALL_COMMANDS, isDeprecated, getDeprecationTarget } = registry;
|
|
1251
1251
|
|
|
1252
1252
|
let cmd = cleanArgs[0];
|
|
1253
|
+
const originalCmd = cmd; // Save original for deprecation warning
|
|
1254
|
+
|
|
1253
1255
|
// Prefer exact match, then alias
|
|
1256
|
+
let prependSubcommand = null;
|
|
1254
1257
|
if (!COMMANDS[cmd] && ALIAS_MAP[cmd]) {
|
|
1255
|
-
|
|
1258
|
+
const target = ALIAS_MAP[cmd];
|
|
1259
|
+
// For auth aliases (login, logout, whoami, me, signin, signout),
|
|
1260
|
+
// prepend the original command as a subcommand to runAuth
|
|
1261
|
+
const authAliases = ['login', 'logout', 'whoami', 'me', 'signin', 'signout'];
|
|
1262
|
+
if (target === 'auth' && authAliases.includes(cmd)) {
|
|
1263
|
+
prependSubcommand = cmd === 'signin' ? 'login' : cmd === 'signout' ? 'logout' : cmd;
|
|
1264
|
+
}
|
|
1265
|
+
cmd = target;
|
|
1266
|
+
}
|
|
1267
|
+
let cmdArgs = prependSubcommand ? [prependSubcommand, ...cleanArgs.slice(1)] : cleanArgs.slice(1);
|
|
1268
|
+
|
|
1269
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1270
|
+
// DEPRECATION WARNING
|
|
1271
|
+
// Show warning if user used a deprecated command name
|
|
1272
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1273
|
+
if (cmd && COMMANDS[cmd]) {
|
|
1274
|
+
const cmdDef = COMMANDS[cmd];
|
|
1275
|
+
// Check if original command is deprecated (either via alias or direct deprecated command)
|
|
1276
|
+
if (cmdDef.deprecated && cmdDef.deprecatedBy) {
|
|
1277
|
+
// Direct deprecated command
|
|
1278
|
+
const { warnDeprecationOnce } = require("./_deprecations");
|
|
1279
|
+
warnDeprecationOnce(originalCmd, cmdDef.deprecatedBy, getVersion());
|
|
1280
|
+
} else if (originalCmd !== cmd) {
|
|
1281
|
+
// Used an alias - check if it's in the deprecation map
|
|
1282
|
+
const { warnIfDeprecated } = require("./_deprecations");
|
|
1283
|
+
warnIfDeprecated(originalCmd, getVersion());
|
|
1284
|
+
}
|
|
1256
1285
|
}
|
|
1257
|
-
let cmdArgs = cleanArgs.slice(1);
|
|
1258
1286
|
|
|
1259
1287
|
// Handle command-specific help (vibecheck <cmd> --help)
|
|
1260
1288
|
if (globalFlags.help && cmd && COMMANDS[cmd]) {
|
|
@@ -1319,8 +1347,8 @@ async function main() {
|
|
|
1319
1347
|
isDevProAllowed;
|
|
1320
1348
|
|
|
1321
1349
|
// Auth check (unless skipAuth or offline mode)
|
|
1322
|
-
//
|
|
1323
|
-
const authExemptCommands = ['login', 'logout', 'whoami', 'help', '--help', '-h', 'version', '--version'];
|
|
1350
|
+
// Allow auth commands and help without prior auth
|
|
1351
|
+
const authExemptCommands = ['auth', 'login', 'logout', 'whoami', 'me', 'signin', 'signout', 'help', '--help', '-h', 'version', '--version'];
|
|
1324
1352
|
const needsAuth = !authExemptCommands.includes(cmd) && !cmdDef.skipAuth && !isOffline;
|
|
1325
1353
|
|
|
1326
1354
|
if (needsAuth) {
|