@vibecheckai/cli 3.0.10 → 3.1.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/bin/.generated +25 -0
- package/bin/registry.js +105 -0
- package/bin/runners/lib/cli-output.js +368 -0
- package/bin/runners/lib/entitlements-v2.js +26 -30
- package/bin/runners/lib/receipts.js +179 -0
- package/bin/runners/lib/upsell.js +510 -0
- package/bin/runners/lib/usage.js +153 -0
- package/bin/runners/runBadge.js +31 -4
- package/bin/runners/runDoctor.js +72 -3
- package/bin/runners/runFix.js +13 -0
- package/bin/runners/runGraph.js +14 -0
- package/bin/runners/runMcp.js +865 -42
- package/bin/runners/runPermissions.js +14 -0
- package/bin/runners/runPreflight.js +553 -0
- package/bin/runners/runProve.js +100 -41
- package/bin/runners/runShip.js +98 -19
- package/bin/runners/runVerify.js +272 -0
- package/bin/vibecheck.js +108 -94
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage Logger - Track metered units per command
|
|
3
|
+
*
|
|
4
|
+
* Records usage for monetization and billing.
|
|
5
|
+
* All paid commands must log their usage.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const os = require("os");
|
|
13
|
+
|
|
14
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
15
|
+
// USAGE LOGGING
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
17
|
+
|
|
18
|
+
function getUsageLogPath() {
|
|
19
|
+
const home = os.homedir();
|
|
20
|
+
const vibecheckDir = path.join(home, ".vibecheck");
|
|
21
|
+
const usageDir = path.join(vibecheckDir, "usage");
|
|
22
|
+
|
|
23
|
+
// Ensure directories exist
|
|
24
|
+
if (!fs.existsSync(vibecheckDir)) fs.mkdirSync(vibecheckDir, { recursive: true });
|
|
25
|
+
if (!fs.existsSync(usageDir)) fs.mkdirSync(usageDir, { recursive: true });
|
|
26
|
+
|
|
27
|
+
return path.join(usageDir, "usage.jsonl");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Log usage for a command run
|
|
32
|
+
* @param {Object} usage - Usage data
|
|
33
|
+
* @param {string} usage.runId - Unique run identifier
|
|
34
|
+
* @param {string} usage.command - Command name
|
|
35
|
+
* @param {string} usage.tier - User tier (free/pro/complete)
|
|
36
|
+
* @param {Object} usage.units - Units consumed
|
|
37
|
+
* @param {number} usage.units.pages - Pages crawled (reality)
|
|
38
|
+
* @param {number} usage.units.clicks - UI interactions (reality)
|
|
39
|
+
* @param {number} usage.units.endpoints - API endpoints tested (permissions)
|
|
40
|
+
* @param {number} usage.units.roles - Roles tested (permissions)
|
|
41
|
+
* @param {number} usage.units.files - Files modified (fix)
|
|
42
|
+
* @param {number} usage.units.hunks - Hunks applied (fix)
|
|
43
|
+
* @param {number} usage.units.exports - Reports generated
|
|
44
|
+
* @param {string} usage.timestamp - ISO timestamp
|
|
45
|
+
* @param {string} usage.projectPath - Project directory
|
|
46
|
+
*/
|
|
47
|
+
function logUsage(usage) {
|
|
48
|
+
const logPath = getUsageLogPath();
|
|
49
|
+
const entry = {
|
|
50
|
+
...usage,
|
|
51
|
+
timestamp: usage.timestamp || new Date().toISOString(),
|
|
52
|
+
hostname: os.hostname(),
|
|
53
|
+
platform: process.platform,
|
|
54
|
+
nodeVersion: process.version,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Append to log file (JSONL format - one JSON per line)
|
|
58
|
+
const line = JSON.stringify(entry) + "\n";
|
|
59
|
+
fs.appendFileSync(logPath, line);
|
|
60
|
+
|
|
61
|
+
// Also store in run-specific directory for receipts
|
|
62
|
+
if (usage.runId) {
|
|
63
|
+
const runDir = path.join(process.cwd(), ".vibecheck", "runs", usage.runId);
|
|
64
|
+
if (fs.existsSync(runDir)) {
|
|
65
|
+
fs.writeFileSync(path.join(runDir, "usage.json"), JSON.stringify(entry, null, 2));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return entry;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get usage statistics for a time period
|
|
74
|
+
* @param {Object} options - Query options
|
|
75
|
+
* @param {string} options.since - ISO date to start from
|
|
76
|
+
* @param {string} options.until - ISO date to end at
|
|
77
|
+
* @param {string} options.command - Filter by command
|
|
78
|
+
* @param {string} options.tier - Filter by tier
|
|
79
|
+
* @returns {Array} Array of usage entries
|
|
80
|
+
*/
|
|
81
|
+
function getUsage(options = {}) {
|
|
82
|
+
const logPath = getUsageLogPath();
|
|
83
|
+
|
|
84
|
+
if (!fs.existsSync(logPath)) return [];
|
|
85
|
+
|
|
86
|
+
const lines = fs.readFileSync(logPath, "utf8").split("\n").filter(Boolean);
|
|
87
|
+
const entries = lines.map(line => {
|
|
88
|
+
try { return JSON.parse(line); } catch { return null; }
|
|
89
|
+
}).filter(Boolean);
|
|
90
|
+
|
|
91
|
+
// Apply filters
|
|
92
|
+
let filtered = entries;
|
|
93
|
+
|
|
94
|
+
if (options.since) {
|
|
95
|
+
filtered = filtered.filter(e => e.timestamp >= options.since);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (options.until) {
|
|
99
|
+
filtered = filtered.filter(e => e.timestamp <= options.until);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (options.command) {
|
|
103
|
+
filtered = filtered.filter(e => e.command === options.command);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (options.tier) {
|
|
107
|
+
filtered = filtered.filter(e => e.tier === options.tier);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return filtered;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get aggregate usage by unit type
|
|
115
|
+
* @param {Object} options - Query options (same as getUsage)
|
|
116
|
+
* @returns {Object} Aggregated totals
|
|
117
|
+
*/
|
|
118
|
+
function getUsageTotals(options = {}) {
|
|
119
|
+
const entries = getUsage(options);
|
|
120
|
+
const totals = {
|
|
121
|
+
runs: entries.length,
|
|
122
|
+
pages: 0,
|
|
123
|
+
clicks: 0,
|
|
124
|
+
endpoints: 0,
|
|
125
|
+
roles: 0,
|
|
126
|
+
files: 0,
|
|
127
|
+
hunks: 0,
|
|
128
|
+
exports: 0,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (entry.units) {
|
|
133
|
+
totals.pages += entry.units.pages || 0;
|
|
134
|
+
totals.clicks += entry.units.clicks || 0;
|
|
135
|
+
totals.endpoints += entry.units.endpoints || 0;
|
|
136
|
+
totals.roles += entry.units.roles || 0;
|
|
137
|
+
totals.files += entry.units.files || 0;
|
|
138
|
+
totals.hunks += entry.units.hunks || 0;
|
|
139
|
+
totals.exports += entry.units.exports || 0;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return totals;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
147
|
+
// EXPORTS
|
|
148
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
149
|
+
module.exports = {
|
|
150
|
+
logUsage,
|
|
151
|
+
getUsage,
|
|
152
|
+
getUsageTotals,
|
|
153
|
+
};
|
package/bin/runners/runBadge.js
CHANGED
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
const fs = require("fs");
|
|
13
13
|
const path = require("path");
|
|
14
14
|
|
|
15
|
+
const upsell = require("./lib/upsell");
|
|
16
|
+
const entitlements = require("./lib/entitlements-v2");
|
|
17
|
+
|
|
15
18
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
16
19
|
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
17
20
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -726,7 +729,7 @@ function printHelp() {
|
|
|
726
729
|
// MAIN BADGE FUNCTION
|
|
727
730
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
728
731
|
|
|
729
|
-
async function runBadge(args) {
|
|
732
|
+
async function runBadge(args, context = {}) {
|
|
730
733
|
const opts = parseArgs(args);
|
|
731
734
|
|
|
732
735
|
if (opts.help) {
|
|
@@ -740,10 +743,15 @@ async function runBadge(args) {
|
|
|
740
743
|
return 0;
|
|
741
744
|
}
|
|
742
745
|
|
|
746
|
+
// Get current tier for policy enforcement
|
|
747
|
+
const authInfo = context.authInfo || {};
|
|
748
|
+
const tier = authInfo.access?.tier || await entitlements.getTier({ apiKey: authInfo.key });
|
|
749
|
+
|
|
743
750
|
// Load last scan result
|
|
744
751
|
let verdict = opts.verdict || 'SHIP';
|
|
745
752
|
let score = opts.score || 100;
|
|
746
753
|
let stats = { blockers: 0, warnings: 0 };
|
|
754
|
+
let findings = [];
|
|
747
755
|
|
|
748
756
|
const lastScanPath = path.join(process.cwd(), '.vibecheck', 'last_scan.json');
|
|
749
757
|
const lastShipPath = path.join(process.cwd(), '.vibecheck', 'last_ship.json');
|
|
@@ -755,11 +763,12 @@ async function runBadge(args) {
|
|
|
755
763
|
if (fs.existsSync(reportPath)) {
|
|
756
764
|
try {
|
|
757
765
|
const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
758
|
-
verdict = report.verdict || 'SHIP';
|
|
766
|
+
verdict = report.verdict || report.meta?.verdict || 'SHIP';
|
|
767
|
+
findings = report.findings || [];
|
|
759
768
|
score = report.score ?? report.vibeScore ?? 100;
|
|
760
769
|
stats = {
|
|
761
|
-
blockers: report.counts?.BLOCK ||
|
|
762
|
-
warnings: report.counts?.WARN ||
|
|
770
|
+
blockers: report.counts?.BLOCK || findings.filter(f => f.severity === 'BLOCK').length || 0,
|
|
771
|
+
warnings: report.counts?.WARN || findings.filter(f => f.severity === 'WARN').length || 0,
|
|
763
772
|
};
|
|
764
773
|
} catch (e) {
|
|
765
774
|
// Use defaults
|
|
@@ -767,6 +776,24 @@ async function runBadge(args) {
|
|
|
767
776
|
}
|
|
768
777
|
}
|
|
769
778
|
|
|
779
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
780
|
+
// BADGE POLICY ENFORCEMENT - SHIP ONLY
|
|
781
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
782
|
+
// Badge is only generated when:
|
|
783
|
+
// 1. User has STARTER+ tier (enforced at CLI level)
|
|
784
|
+
// 2. Last verdict = SHIP
|
|
785
|
+
//
|
|
786
|
+
// If verdict != SHIP, show withheld message with top issues
|
|
787
|
+
if (verdict !== 'SHIP' && !opts.verdict) {
|
|
788
|
+
console.log(BANNER_FULL);
|
|
789
|
+
|
|
790
|
+
// Show badge withheld message with top issues
|
|
791
|
+
console.log(upsell.formatBadgeWithheld(verdict, findings, tier));
|
|
792
|
+
|
|
793
|
+
// Do NOT write badge file
|
|
794
|
+
return verdict === 'BLOCK' ? 2 : 1;
|
|
795
|
+
}
|
|
796
|
+
|
|
770
797
|
const projectName = path.basename(process.cwd());
|
|
771
798
|
|
|
772
799
|
// Print banner
|
package/bin/runners/runDoctor.js
CHANGED
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
const fs = require("fs");
|
|
12
12
|
const path = require("path");
|
|
13
|
+
const {
|
|
14
|
+
generateRunId,
|
|
15
|
+
createJsonOutput,
|
|
16
|
+
writeJsonOutput,
|
|
17
|
+
exitCodeToVerdict,
|
|
18
|
+
verdictToExitCode,
|
|
19
|
+
saveArtifact
|
|
20
|
+
} = require("./lib/cli-output");
|
|
13
21
|
|
|
14
22
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
15
23
|
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
@@ -283,9 +291,13 @@ try {
|
|
|
283
291
|
// MAIN DOCTOR FUNCTION
|
|
284
292
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
285
293
|
|
|
286
|
-
async function runDoctor(args) {
|
|
294
|
+
async function runDoctor(args, context = {}) {
|
|
295
|
+
// Extract runId from context or generate new one
|
|
296
|
+
const runId = context.runId || generateRunId();
|
|
297
|
+
const startTime = context.startTime || new Date().toISOString();
|
|
298
|
+
|
|
287
299
|
const opts = parseArgs(args);
|
|
288
|
-
const
|
|
300
|
+
const executionStart = Date.now();
|
|
289
301
|
|
|
290
302
|
if (opts.help) {
|
|
291
303
|
printHelp();
|
|
@@ -315,8 +327,51 @@ async function runDoctor(args) {
|
|
|
315
327
|
skipNetwork: opts.skipNetwork,
|
|
316
328
|
saveReport: opts.saveReport,
|
|
317
329
|
failOnWarn: opts.failOnWarn,
|
|
330
|
+
runId,
|
|
318
331
|
});
|
|
319
|
-
|
|
332
|
+
|
|
333
|
+
const results = await doctor.run();
|
|
334
|
+
|
|
335
|
+
// Apply CLI output conventions
|
|
336
|
+
if (opts.json) {
|
|
337
|
+
const output = createJsonOutput({
|
|
338
|
+
runId,
|
|
339
|
+
command: "doctor",
|
|
340
|
+
startTime,
|
|
341
|
+
exitCode: results.exitCode || 0,
|
|
342
|
+
verdict: exitCodeToVerdict(results.exitCode || 0),
|
|
343
|
+
result: {
|
|
344
|
+
health: results.health || "unknown",
|
|
345
|
+
checks: results.checks || [],
|
|
346
|
+
fixes: results.fixes || [],
|
|
347
|
+
summary: {
|
|
348
|
+
total: results.totalChecks || 0,
|
|
349
|
+
passed: results.passedChecks || 0,
|
|
350
|
+
warnings: results.warnings || 0,
|
|
351
|
+
errors: results.errors || 0,
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
tier: "free",
|
|
355
|
+
version: require("../../package.json").version,
|
|
356
|
+
artifacts: results.reportPath ? [{
|
|
357
|
+
type: "report",
|
|
358
|
+
path: results.reportPath,
|
|
359
|
+
description: "Doctor report"
|
|
360
|
+
}] : []
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
writeJsonOutput(output, opts.output);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Save artifacts
|
|
367
|
+
if (results.checks) {
|
|
368
|
+
saveArtifact(runId, "checks", results.checks);
|
|
369
|
+
}
|
|
370
|
+
if (results.fixes) {
|
|
371
|
+
saveArtifact(runId, "fixes", results.fixes);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return results.exitCode || 0;
|
|
320
375
|
}
|
|
321
376
|
|
|
322
377
|
// Legacy fallback
|
|
@@ -333,6 +388,9 @@ function parseArgs(args) {
|
|
|
333
388
|
dryRun: false,
|
|
334
389
|
quiet: false,
|
|
335
390
|
verbose: false,
|
|
391
|
+
strict: false,
|
|
392
|
+
ci: false,
|
|
393
|
+
output: null,
|
|
336
394
|
categories: null,
|
|
337
395
|
skipNetwork: false,
|
|
338
396
|
saveReport: true,
|
|
@@ -381,6 +439,17 @@ function parseArgs(args) {
|
|
|
381
439
|
case '--fail-on-warn':
|
|
382
440
|
opts.failOnWarn = true;
|
|
383
441
|
break;
|
|
442
|
+
case '--strict':
|
|
443
|
+
opts.strict = true;
|
|
444
|
+
break;
|
|
445
|
+
case '--ci':
|
|
446
|
+
opts.ci = true;
|
|
447
|
+
opts.quiet = true;
|
|
448
|
+
break;
|
|
449
|
+
case '--output':
|
|
450
|
+
case '-o':
|
|
451
|
+
opts.output = args[++i];
|
|
452
|
+
break;
|
|
384
453
|
default:
|
|
385
454
|
if (!arg.startsWith('-')) {
|
|
386
455
|
opts.path = path.resolve(arg);
|
package/bin/runners/runFix.js
CHANGED
|
@@ -35,6 +35,7 @@ const { buildSharePack } = require('./lib/share-pack');
|
|
|
35
35
|
|
|
36
36
|
// Entitlements enforcement
|
|
37
37
|
const entitlements = require('./lib/entitlements-v2');
|
|
38
|
+
const upsell = require('./lib/upsell');
|
|
38
39
|
|
|
39
40
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
40
41
|
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
@@ -549,6 +550,18 @@ async function runFix(args) {
|
|
|
549
550
|
console.log(` ${colors.mission}${ICONS.robot}${c.reset} ${c.bold}vibecheck fix --autopilot --apply${c.reset}`);
|
|
550
551
|
console.log(` ${c.dim}Loop until SHIP or stuck${c.reset}`);
|
|
551
552
|
console.log();
|
|
553
|
+
|
|
554
|
+
// Earned upsell: Show if --apply is locked for their tier
|
|
555
|
+
const applyAccess = await entitlements.checkCommand("fix.apply_patches", { projectPath: root });
|
|
556
|
+
if (!applyAccess.allowed) {
|
|
557
|
+
console.log(upsell.formatEarnedUpsell({
|
|
558
|
+
cmd: "fix",
|
|
559
|
+
withheldArtifact: "apply",
|
|
560
|
+
upgradeTier: applyAccess.requiredTier || "complete",
|
|
561
|
+
why: "Apply patches automatically",
|
|
562
|
+
}));
|
|
563
|
+
}
|
|
564
|
+
|
|
552
565
|
return 0;
|
|
553
566
|
}
|
|
554
567
|
|
package/bin/runners/runGraph.js
CHANGED
|
@@ -31,6 +31,9 @@ const {
|
|
|
31
31
|
mergeRuntimeResults
|
|
32
32
|
} = require("./lib/graph");
|
|
33
33
|
|
|
34
|
+
// Entitlements enforcement
|
|
35
|
+
const entitlements = require("./lib/entitlements-v2");
|
|
36
|
+
|
|
34
37
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
35
38
|
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
36
39
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -174,6 +177,17 @@ async function runGraph(args) {
|
|
|
174
177
|
return 0;
|
|
175
178
|
}
|
|
176
179
|
|
|
180
|
+
// TIER ENFORCEMENT: COMPLETE only
|
|
181
|
+
const access = await entitlements.enforce("graph", {
|
|
182
|
+
projectPath: opts.path || process.cwd(),
|
|
183
|
+
silent: false,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (!access.allowed) {
|
|
187
|
+
console.log(`\n${c.yellow}Tip:${c.reset} The graph command requires COMPLETE tier for advanced proof graph visualization.`);
|
|
188
|
+
return entitlements.EXIT_FEATURE_NOT_ALLOWED;
|
|
189
|
+
}
|
|
190
|
+
|
|
177
191
|
const root = path.resolve(opts.path || process.cwd());
|
|
178
192
|
const projectName = path.basename(root);
|
|
179
193
|
const outDir = path.join(root, ".vibecheck", "graph");
|