@vibecheckai/cli 3.1.8 → 3.2.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/registry.js +106 -116
- package/bin/runners/context/generators/mcp.js +18 -0
- package/bin/runners/context/index.js +72 -4
- package/bin/runners/context/proof-context.js +293 -1
- package/bin/runners/context/security-scanner.js +311 -73
- package/bin/runners/lib/analyzers.js +607 -20
- package/bin/runners/lib/detectors-v2.js +172 -15
- package/bin/runners/lib/entitlements-v2.js +48 -1
- package/bin/runners/lib/evidence-pack.js +678 -0
- package/bin/runners/lib/html-proof-report.js +913 -0
- package/bin/runners/lib/missions/plan.js +231 -41
- package/bin/runners/lib/missions/templates.js +125 -0
- package/bin/runners/lib/scan-output.js +492 -253
- package/bin/runners/lib/ship-output.js +901 -641
- package/bin/runners/runCheckpoint.js +44 -3
- package/bin/runners/runContext.d.ts +4 -0
- package/bin/runners/runDoctor.js +10 -2
- package/bin/runners/runFix.js +51 -341
- package/bin/runners/runInit.js +11 -0
- package/bin/runners/runPolish.d.ts +4 -0
- package/bin/runners/runPolish.js +608 -29
- package/bin/runners/runProve.js +210 -25
- package/bin/runners/runReality.js +846 -101
- package/bin/runners/runScan.js +238 -4
- package/bin/runners/runShip.js +19 -3
- package/bin/runners/runWatch.js +14 -1
- package/bin/vibecheck.js +32 -2
- package/mcp-server/consolidated-tools.js +408 -42
- package/mcp-server/index.js +152 -15
- package/mcp-server/proof-tools.js +571 -0
- package/mcp-server/tier-auth.js +22 -19
- package/mcp-server/tools-v3.js +744 -0
- package/mcp-server/truth-firewall-tools.js +190 -4
- package/package.json +3 -1
- package/bin/runners/runInstall.js +0 -281
- package/bin/runners/runLabs.js +0 -341
|
@@ -398,7 +398,7 @@ export async function handleTruthFirewallTool(toolName, args, projectPath = proc
|
|
|
398
398
|
return await addAssumption(projectPath, args);
|
|
399
399
|
|
|
400
400
|
case "vibecheck.validate_plan":
|
|
401
|
-
return await
|
|
401
|
+
return await getPlanValidationResult(projectPath, args);
|
|
402
402
|
|
|
403
403
|
case "vibecheck.check_drift":
|
|
404
404
|
return await checkDriftTool(projectPath, args);
|
|
@@ -421,8 +421,147 @@ const state = {
|
|
|
421
421
|
assumptions: [],
|
|
422
422
|
verifiedClaims: new Map(),
|
|
423
423
|
maxAssumptions: 2,
|
|
424
|
+
lastValidationByProject: new Map(),
|
|
424
425
|
};
|
|
425
426
|
|
|
427
|
+
const MAX_EVIDENCE_SNIPPET = 200;
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Policy configuration - aligned with proof-context.js TRUTH_CONTRACT
|
|
431
|
+
*/
|
|
432
|
+
const POLICY_CONFIG = {
|
|
433
|
+
strict: {
|
|
434
|
+
minConfidence: 0.8,
|
|
435
|
+
allowUnknown: false,
|
|
436
|
+
requireValidation: true,
|
|
437
|
+
blockOnDrift: true,
|
|
438
|
+
validationTTL: 5 * 60 * 1000, // 5 minutes
|
|
439
|
+
},
|
|
440
|
+
balanced: {
|
|
441
|
+
minConfidence: 0.6,
|
|
442
|
+
allowUnknown: false,
|
|
443
|
+
requireValidation: true,
|
|
444
|
+
blockOnDrift: false,
|
|
445
|
+
validationTTL: 10 * 60 * 1000, // 10 minutes
|
|
446
|
+
},
|
|
447
|
+
permissive: {
|
|
448
|
+
minConfidence: 0.4,
|
|
449
|
+
allowUnknown: true,
|
|
450
|
+
requireValidation: false,
|
|
451
|
+
blockOnDrift: false,
|
|
452
|
+
validationTTL: 30 * 60 * 1000, // 30 minutes
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Get policy configuration
|
|
458
|
+
*/
|
|
459
|
+
export function getPolicyConfig(policy = 'strict') {
|
|
460
|
+
return POLICY_CONFIG[policy] || POLICY_CONFIG.strict;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function confidenceToScore(confidence) {
|
|
464
|
+
if (typeof confidence === "number") return confidence;
|
|
465
|
+
switch (confidence) {
|
|
466
|
+
case "high":
|
|
467
|
+
return 0.9;
|
|
468
|
+
case "medium":
|
|
469
|
+
return 0.7;
|
|
470
|
+
case "low":
|
|
471
|
+
return 0.5;
|
|
472
|
+
default:
|
|
473
|
+
return 0.6;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async function readSnippet(projectPath, file, line) {
|
|
478
|
+
if (!file) return "";
|
|
479
|
+
try {
|
|
480
|
+
const content = await fs.readFile(path.join(projectPath, file), "utf8");
|
|
481
|
+
const lines = content.split("\n");
|
|
482
|
+
const idx = Math.max(0, Math.min(lines.length - 1, line - 1));
|
|
483
|
+
return (lines[idx] || "").slice(0, MAX_EVIDENCE_SNIPPET);
|
|
484
|
+
} catch {
|
|
485
|
+
return "";
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
async function normalizeEvidence(projectPath, evidence, fallback, confidence) {
|
|
490
|
+
const raw = Array.isArray(evidence) ? evidence : evidence ? [evidence] : [];
|
|
491
|
+
const normalized = [];
|
|
492
|
+
|
|
493
|
+
for (const item of raw) {
|
|
494
|
+
const file = item?.file || fallback?.file || "";
|
|
495
|
+
const line = Number(item?.line || item?.lines || fallback?.line || 1);
|
|
496
|
+
const snippet =
|
|
497
|
+
item?.snippet ||
|
|
498
|
+
item?.evidence ||
|
|
499
|
+
(await readSnippet(projectPath, file, line));
|
|
500
|
+
|
|
501
|
+
normalized.push({
|
|
502
|
+
file,
|
|
503
|
+
line,
|
|
504
|
+
snippet,
|
|
505
|
+
confidence: item?.confidence ?? confidenceToScore(confidence),
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (normalized.length === 0 && fallback?.file) {
|
|
510
|
+
normalized.push({
|
|
511
|
+
file: fallback.file,
|
|
512
|
+
line: fallback.line || 1,
|
|
513
|
+
snippet: await readSnippet(projectPath, fallback.file, fallback.line || 1),
|
|
514
|
+
confidence: confidenceToScore(confidence),
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return normalized;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Check if there's a recent claim validation for the project.
|
|
523
|
+
* The TTL depends on the policy mode.
|
|
524
|
+
*/
|
|
525
|
+
export function hasRecentClaimValidation(projectPath, policy = 'strict') {
|
|
526
|
+
const last = state.lastValidationByProject.get(projectPath);
|
|
527
|
+
if (typeof last !== "number") return false;
|
|
528
|
+
|
|
529
|
+
const config = getPolicyConfig(policy);
|
|
530
|
+
const maxAgeMs = config.validationTTL;
|
|
531
|
+
return Date.now() - last <= maxAgeMs;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Validate a claim result against policy thresholds.
|
|
536
|
+
* Returns an enforcement decision.
|
|
537
|
+
*/
|
|
538
|
+
export function enforceClaimResult(result, policy = 'strict') {
|
|
539
|
+
const config = getPolicyConfig(policy);
|
|
540
|
+
const confidence = confidenceToScore(result.confidence || result.result === 'true' ? 0.9 : 0.3);
|
|
541
|
+
|
|
542
|
+
// Unknown results
|
|
543
|
+
if (result.result === 'unknown') {
|
|
544
|
+
if (!config.allowUnknown) {
|
|
545
|
+
return {
|
|
546
|
+
allowed: false,
|
|
547
|
+
reason: `Unknown claims are not allowed in ${policy} mode`,
|
|
548
|
+
suggestion: 'Use search_evidence to find proof or get_truthpack to refresh context',
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Low confidence
|
|
554
|
+
if (confidence < config.minConfidence) {
|
|
555
|
+
return {
|
|
556
|
+
allowed: false,
|
|
557
|
+
reason: `Confidence ${(confidence * 100).toFixed(0)}% below ${policy} threshold ${(config.minConfidence * 100).toFixed(0)}%`,
|
|
558
|
+
suggestion: 'Find additional evidence or use permissive policy',
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return { allowed: true };
|
|
563
|
+
}
|
|
564
|
+
|
|
426
565
|
async function getTruthPack(projectPath, args) {
|
|
427
566
|
const scope = args.scope || 'all';
|
|
428
567
|
const refresh = args.refresh || false;
|
|
@@ -439,6 +578,7 @@ async function getTruthPack(projectPath, args) {
|
|
|
439
578
|
commitHash: getCommitHash(projectPath),
|
|
440
579
|
sections: {},
|
|
441
580
|
confidence: 0,
|
|
581
|
+
_attribution: CONTEXT_ATTRIBUTION,
|
|
442
582
|
};
|
|
443
583
|
|
|
444
584
|
if (scope === 'all' || scope === 'routes') {
|
|
@@ -510,22 +650,45 @@ async function validateClaim(projectPath, args) {
|
|
|
510
650
|
result.nextSteps = [`Verification error: ${error.message}`];
|
|
511
651
|
}
|
|
512
652
|
|
|
513
|
-
//
|
|
653
|
+
// ENFORCED: Unknown claims must return explicit "unknown" error
|
|
654
|
+
// that blocks dependent actions in strict/balanced modes
|
|
514
655
|
if (result.result === 'unknown') {
|
|
515
656
|
result.nextSteps.push(
|
|
516
657
|
'call vibecheck.search_evidence to find related code',
|
|
517
658
|
'call vibecheck.get_truthpack to get full context',
|
|
518
659
|
);
|
|
519
660
|
result.warning = '⚠️ UNKNOWN claims BLOCK dependent actions. Verify before proceeding.';
|
|
661
|
+
result.enforcement = {
|
|
662
|
+
allowed: false,
|
|
663
|
+
reason: 'Claim result is unknown - cannot proceed without evidence',
|
|
664
|
+
blockedActions: ['fix', 'autopilot_apply', 'propose_patch'],
|
|
665
|
+
};
|
|
666
|
+
} else if (result.result === 'true') {
|
|
667
|
+
result.enforcement = {
|
|
668
|
+
allowed: true,
|
|
669
|
+
confidence: confidenceToScore(result.confidence),
|
|
670
|
+
};
|
|
671
|
+
} else {
|
|
672
|
+
// result is 'false'
|
|
673
|
+
result.enforcement = {
|
|
674
|
+
allowed: false,
|
|
675
|
+
reason: 'Claim is disproven - do not proceed with dependent actions',
|
|
676
|
+
};
|
|
520
677
|
}
|
|
521
678
|
|
|
522
679
|
// Cache result
|
|
523
|
-
state.verifiedClaims.set(claimId, { result, timestamp: Date.now() });
|
|
680
|
+
state.verifiedClaims.set(claimId, { result, timestamp: Date.now(), projectPath });
|
|
681
|
+
state.lastValidationByProject.set(projectPath, Date.now());
|
|
524
682
|
|
|
525
683
|
return {
|
|
526
684
|
claimId,
|
|
527
685
|
...result,
|
|
686
|
+
evidence: await normalizeEvidence(projectPath, result.evidence, {
|
|
687
|
+
file: subject?.path || subject?.name,
|
|
688
|
+
line: 1,
|
|
689
|
+
}, result.confidence),
|
|
528
690
|
timestamp: new Date().toISOString(),
|
|
691
|
+
_attribution: CONTEXT_ATTRIBUTION,
|
|
529
692
|
};
|
|
530
693
|
}
|
|
531
694
|
|
|
@@ -565,6 +728,7 @@ async function compileContext(projectPath, args) {
|
|
|
565
728
|
invariants,
|
|
566
729
|
tokenCount,
|
|
567
730
|
warnings: generateContextWarnings(domains, policy, relevantRoutes.length),
|
|
731
|
+
_attribution: CONTEXT_ATTRIBUTION,
|
|
568
732
|
};
|
|
569
733
|
}
|
|
570
734
|
|
|
@@ -589,6 +753,7 @@ async function searchEvidence(projectPath, args) {
|
|
|
589
753
|
line: i + 1,
|
|
590
754
|
snippet: snippet.slice(0, 300),
|
|
591
755
|
hash: crypto.createHash('sha256').update(lines[i]).digest('hex').slice(0, 16),
|
|
756
|
+
confidence: 0.6,
|
|
592
757
|
});
|
|
593
758
|
|
|
594
759
|
if (results.length >= limit) break;
|
|
@@ -604,6 +769,7 @@ async function searchEvidence(projectPath, args) {
|
|
|
604
769
|
query,
|
|
605
770
|
count: results.length,
|
|
606
771
|
results,
|
|
772
|
+
_attribution: CONTEXT_ATTRIBUTION,
|
|
607
773
|
};
|
|
608
774
|
}
|
|
609
775
|
|
|
@@ -780,7 +946,7 @@ async function addAssumption(projectPath, args) {
|
|
|
780
946
|
// PLAN VALIDATION & DRIFT DETECTION (Spec 10.3)
|
|
781
947
|
// =============================================================================
|
|
782
948
|
|
|
783
|
-
async function
|
|
949
|
+
async function getPlanValidationResult(projectPath, args) {
|
|
784
950
|
const { plan, strict = false } = args;
|
|
785
951
|
|
|
786
952
|
// Load contracts
|
|
@@ -1224,6 +1390,11 @@ export function getProjectFingerprint(projectPath) {
|
|
|
1224
1390
|
};
|
|
1225
1391
|
}
|
|
1226
1392
|
|
|
1393
|
+
/**
|
|
1394
|
+
* Context attribution message shown when AI uses vibecheck data
|
|
1395
|
+
*/
|
|
1396
|
+
const CONTEXT_ATTRIBUTION = "🧠 Context enhanced by vibecheck";
|
|
1397
|
+
|
|
1227
1398
|
/**
|
|
1228
1399
|
* Wrap MCP response with standard metadata including fingerprint (Spec 10.2)
|
|
1229
1400
|
*/
|
|
@@ -1233,9 +1404,17 @@ export function wrapMcpResponse(data, projectPath) {
|
|
|
1233
1404
|
version: '2.0.0',
|
|
1234
1405
|
projectFingerprint: getProjectFingerprint(projectPath),
|
|
1235
1406
|
data,
|
|
1407
|
+
_attribution: CONTEXT_ATTRIBUTION,
|
|
1236
1408
|
};
|
|
1237
1409
|
}
|
|
1238
1410
|
|
|
1411
|
+
/**
|
|
1412
|
+
* Get the context attribution message
|
|
1413
|
+
*/
|
|
1414
|
+
export function getContextAttribution() {
|
|
1415
|
+
return CONTEXT_ATTRIBUTION;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1239
1418
|
async function extractRoutes(projectPath) {
|
|
1240
1419
|
const routes = [];
|
|
1241
1420
|
const files = await findSourceFiles(projectPath);
|
|
@@ -1497,4 +1676,11 @@ async function findSourceFiles(projectPath) {
|
|
|
1497
1676
|
export default {
|
|
1498
1677
|
TRUTH_FIREWALL_TOOLS,
|
|
1499
1678
|
handleTruthFirewallTool,
|
|
1679
|
+
hasRecentClaimValidation,
|
|
1680
|
+
enforceClaimResult,
|
|
1681
|
+
getPolicyConfig,
|
|
1682
|
+
getProjectFingerprint,
|
|
1683
|
+
wrapMcpResponse,
|
|
1684
|
+
getContextAttribution,
|
|
1685
|
+
CONTEXT_ATTRIBUTION,
|
|
1500
1686
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibecheckai/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Vibecheck CLI - Ship with confidence. One verdict: SHIP | WARN | BLOCK.",
|
|
5
5
|
"main": "bin/vibecheck.js",
|
|
6
6
|
"bin": {
|
|
@@ -28,6 +28,8 @@
|
|
|
28
28
|
"@babel/parser": "^7.23.0",
|
|
29
29
|
"@babel/traverse": "^7.23.0",
|
|
30
30
|
"@babel/types": "^7.23.0",
|
|
31
|
+
"@vibecheck/core": "workspace:*",
|
|
32
|
+
"@vibecheck/security": "workspace:*",
|
|
31
33
|
"chalk": "^5.3.0",
|
|
32
34
|
"commander": "^12.0.0",
|
|
33
35
|
"debug": "^4.3.4",
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* vibecheck install - Zero-friction Onboarding
|
|
3
|
-
*
|
|
4
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
-
* ENTERPRISE EDITION - World-Class Terminal Experience
|
|
6
|
-
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const fs = require("fs");
|
|
10
|
-
const path = require("path");
|
|
11
|
-
const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
|
|
12
|
-
const { buildTruthpack, writeTruthpack } = require("./lib/truth");
|
|
13
|
-
const { detectPackageManager, detectNext, detectFastify, detectFastifyEntry } = require("./lib/detect");
|
|
14
|
-
const { readPkg, writePkg, upsertScripts } = require("./lib/pkgjson");
|
|
15
|
-
const { writeEnvTemplateFromTruthpack } = require("./lib/env-template");
|
|
16
|
-
|
|
17
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
18
|
-
// ADVANCED TERMINAL - ANSI CODES & UTILITIES
|
|
19
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
20
|
-
|
|
21
|
-
const c = {
|
|
22
|
-
reset: '\x1b[0m',
|
|
23
|
-
bold: '\x1b[1m',
|
|
24
|
-
dim: '\x1b[2m',
|
|
25
|
-
italic: '\x1b[3m',
|
|
26
|
-
red: '\x1b[31m',
|
|
27
|
-
green: '\x1b[32m',
|
|
28
|
-
yellow: '\x1b[33m',
|
|
29
|
-
blue: '\x1b[34m',
|
|
30
|
-
magenta: '\x1b[35m',
|
|
31
|
-
cyan: '\x1b[36m',
|
|
32
|
-
white: '\x1b[37m',
|
|
33
|
-
gray: '\x1b[90m',
|
|
34
|
-
clearLine: '\x1b[2K',
|
|
35
|
-
hideCursor: '\x1b[?25l',
|
|
36
|
-
showCursor: '\x1b[?25h',
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
|
|
40
|
-
|
|
41
|
-
const colors = {
|
|
42
|
-
gradient1: rgb(255, 200, 100),
|
|
43
|
-
gradient2: rgb(255, 180, 80),
|
|
44
|
-
gradient3: rgb(255, 160, 60),
|
|
45
|
-
shipGreen: rgb(0, 255, 150),
|
|
46
|
-
warnAmber: rgb(255, 200, 0),
|
|
47
|
-
blockRed: rgb(255, 80, 80),
|
|
48
|
-
accent: rgb(255, 200, 100),
|
|
49
|
-
muted: rgb(120, 120, 140),
|
|
50
|
-
next: rgb(100, 200, 255),
|
|
51
|
-
fastify: rgb(255, 150, 200),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
55
|
-
// PREMIUM BANNER
|
|
56
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
57
|
-
|
|
58
|
-
const INSTALL_BANNER = `
|
|
59
|
-
${rgb(255, 200, 100)} ██╗███╗ ██╗███████╗████████╗ █████╗ ██╗ ██╗ ${c.reset}
|
|
60
|
-
${rgb(255, 180, 80)} ██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██║ ██║ ${c.reset}
|
|
61
|
-
${rgb(255, 160, 60)} ██║██╔██╗ ██║███████╗ ██║ ███████║██║ ██║ ${c.reset}
|
|
62
|
-
${rgb(255, 140, 40)} ██║██║╚██╗██║╚════██║ ██║ ██╔══██║██║ ██║ ${c.reset}
|
|
63
|
-
${rgb(255, 120, 20)} ██║██║ ╚████║███████║ ██║ ██║ ██║███████╗███████╗${c.reset}
|
|
64
|
-
${rgb(255, 100, 0)} ╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝${c.reset}
|
|
65
|
-
`;
|
|
66
|
-
|
|
67
|
-
const BANNER_FULL = `
|
|
68
|
-
${rgb(255, 200, 100)} ██╗ ██╗██╗██████╗ ███████╗ ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗${c.reset}
|
|
69
|
-
${rgb(255, 190, 90)} ██║ ██║██║██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝${c.reset}
|
|
70
|
-
${rgb(255, 180, 80)} ██║ ██║██║██████╔╝█████╗ ██║ ███████║█████╗ ██║ █████╔╝ ${c.reset}
|
|
71
|
-
${rgb(255, 160, 60)} ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ${c.reset}
|
|
72
|
-
${rgb(255, 140, 40)} ╚████╔╝ ██║██████╔╝███████╗╚██████╗██║ ██║███████╗╚██████╗██║ ██╗${c.reset}
|
|
73
|
-
${rgb(255, 120, 20)} ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝${c.reset}
|
|
74
|
-
|
|
75
|
-
${c.dim} ┌─────────────────────────────────────────────────────────────────────┐${c.reset}
|
|
76
|
-
${c.dim} │${c.reset} ${rgb(255, 200, 100)}📥${c.reset} ${c.bold}INSTALL${c.reset} ${c.dim}•${c.reset} ${rgb(200, 200, 200)}Zero-Friction${c.reset} ${c.dim}•${c.reset} ${rgb(150, 150, 150)}Auto-Detect${c.reset} ${c.dim}•${c.reset} ${rgb(100, 100, 100)}Onboarding${c.reset} ${c.dim}│${c.reset}
|
|
77
|
-
${c.dim} └─────────────────────────────────────────────────────────────────────┘${c.reset}
|
|
78
|
-
`;
|
|
79
|
-
|
|
80
|
-
const ICONS = {
|
|
81
|
-
install: '📥',
|
|
82
|
-
package: '📦',
|
|
83
|
-
file: '📄',
|
|
84
|
-
check: '✓',
|
|
85
|
-
cross: '✗',
|
|
86
|
-
warning: '⚠',
|
|
87
|
-
arrow: '→',
|
|
88
|
-
bullet: '•',
|
|
89
|
-
sparkle: '✨',
|
|
90
|
-
folder: '📁',
|
|
91
|
-
next: '▲',
|
|
92
|
-
fastify: '⚡',
|
|
93
|
-
npm: '📦',
|
|
94
|
-
gear: '⚙️',
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const SPINNER_DOTS = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
98
|
-
let spinnerIndex = 0;
|
|
99
|
-
let spinnerInterval = null;
|
|
100
|
-
let spinnerStartTime = null;
|
|
101
|
-
|
|
102
|
-
function formatDuration(ms) {
|
|
103
|
-
if (ms < 1000) return `${ms}ms`;
|
|
104
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function startSpinner(message) {
|
|
108
|
-
spinnerStartTime = Date.now();
|
|
109
|
-
process.stdout.write(c.hideCursor);
|
|
110
|
-
spinnerInterval = setInterval(() => {
|
|
111
|
-
const elapsed = formatDuration(Date.now() - spinnerStartTime);
|
|
112
|
-
process.stdout.write(`\r${c.clearLine} ${colors.accent}${SPINNER_DOTS[spinnerIndex]}${c.reset} ${message} ${c.dim}${elapsed}${c.reset}`);
|
|
113
|
-
spinnerIndex = (spinnerIndex + 1) % SPINNER_DOTS.length;
|
|
114
|
-
}, 80);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function stopSpinner(message, success = true) {
|
|
118
|
-
if (spinnerInterval) {
|
|
119
|
-
clearInterval(spinnerInterval);
|
|
120
|
-
spinnerInterval = null;
|
|
121
|
-
}
|
|
122
|
-
const elapsed = spinnerStartTime ? formatDuration(Date.now() - spinnerStartTime) : '';
|
|
123
|
-
const icon = success ? `${colors.shipGreen}${ICONS.check}${c.reset}` : `${colors.blockRed}${ICONS.cross}${c.reset}`;
|
|
124
|
-
process.stdout.write(`\r${c.clearLine} ${icon} ${message} ${c.dim}${elapsed}${c.reset}\n`);
|
|
125
|
-
process.stdout.write(c.showCursor);
|
|
126
|
-
spinnerStartTime = null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function printDivider(char = '─', width = 69, color = c.dim) {
|
|
130
|
-
console.log(`${color} ${char.repeat(width)}${c.reset}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function printSection(title, icon = '◆') {
|
|
134
|
-
console.log();
|
|
135
|
-
console.log(` ${colors.accent}${icon}${c.reset} ${c.bold}${title}${c.reset}`);
|
|
136
|
-
printDivider();
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function printHelp(opts = {}) {
|
|
140
|
-
if (shouldShowBanner(opts)) {
|
|
141
|
-
console.log(BANNER_FULL);
|
|
142
|
-
}
|
|
143
|
-
console.log(`
|
|
144
|
-
${c.bold}Usage:${c.reset} vibecheck install [options]
|
|
145
|
-
|
|
146
|
-
${c.bold}Zero-Friction Onboarding${c.reset} — Auto-detect and configure your project.
|
|
147
|
-
|
|
148
|
-
${c.bold}Options:${c.reset}
|
|
149
|
-
${colors.accent}--path, -p <dir>${c.reset} Project path ${c.dim}(default: current directory)${c.reset}
|
|
150
|
-
${colors.accent}--help, -h${c.reset} Show this help
|
|
151
|
-
|
|
152
|
-
${c.bold}What It Does:${c.reset}
|
|
153
|
-
${colors.shipGreen}1.${c.reset} Detects package manager ${c.dim}(npm, yarn, pnpm)${c.reset}
|
|
154
|
-
${colors.shipGreen}2.${c.reset} Detects frameworks ${c.dim}(Next.js, Fastify)${c.reset}
|
|
155
|
-
${colors.shipGreen}3.${c.reset} Builds initial truthpack
|
|
156
|
-
${colors.shipGreen}4.${c.reset} Creates .vibecheck/config.json
|
|
157
|
-
${colors.shipGreen}5.${c.reset} Generates env template from truthpack
|
|
158
|
-
${colors.shipGreen}6.${c.reset} Adds vibecheck scripts to package.json
|
|
159
|
-
|
|
160
|
-
${c.bold}Created Files:${c.reset}
|
|
161
|
-
${ICONS.file} ${colors.accent}.vibecheck/config.json${c.reset} ${c.dim}Local configuration${c.reset}
|
|
162
|
-
${ICONS.file} ${colors.accent}.vibecheck/truthpack.json${c.reset} ${c.dim}Ground truth for AI agents${c.reset}
|
|
163
|
-
${ICONS.file} ${colors.accent}.env.template${c.reset} ${c.dim}Env vars from truthpack${c.reset}
|
|
164
|
-
|
|
165
|
-
${c.bold}Examples:${c.reset}
|
|
166
|
-
${c.dim}# Install in current directory${c.reset}
|
|
167
|
-
vibecheck install
|
|
168
|
-
|
|
169
|
-
${c.dim}# Install in specific directory${c.reset}
|
|
170
|
-
vibecheck install --path ./my-app
|
|
171
|
-
`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function ensureDir(p) {
|
|
175
|
-
fs.mkdirSync(p, { recursive: true });
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function runInstall(argsOrContext = {}) {
|
|
179
|
-
// Handle array args from CLI
|
|
180
|
-
let opts = { noBanner: false, json: false, quiet: false, ci: false };
|
|
181
|
-
if (Array.isArray(argsOrContext)) {
|
|
182
|
-
const { flags } = parseGlobalFlags(argsOrContext);
|
|
183
|
-
opts = { ...opts, ...flags };
|
|
184
|
-
if (opts.help) {
|
|
185
|
-
printHelp(opts);
|
|
186
|
-
return 0;
|
|
187
|
-
}
|
|
188
|
-
argsOrContext = { repoRoot: opts.path || process.cwd() };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const { repoRoot } = argsOrContext;
|
|
192
|
-
const root = repoRoot || process.cwd();
|
|
193
|
-
const projectName = path.basename(root);
|
|
194
|
-
|
|
195
|
-
// Print banner
|
|
196
|
-
if (shouldShowBanner(opts)) {
|
|
197
|
-
console.log(BANNER_FULL);
|
|
198
|
-
}
|
|
199
|
-
console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
|
|
200
|
-
console.log(` ${c.dim}Path:${c.reset} ${root}`);
|
|
201
|
-
console.log();
|
|
202
|
-
|
|
203
|
-
const { path: pkgPath, json: pkg } = readPkg(root);
|
|
204
|
-
|
|
205
|
-
// Detect environment
|
|
206
|
-
startSpinner('Detecting environment...');
|
|
207
|
-
const pm = detectPackageManager(root);
|
|
208
|
-
const next = detectNext(root, pkg);
|
|
209
|
-
const fastify = detectFastify(root, pkg);
|
|
210
|
-
const fastifyEntry = fastify.enabled ? await detectFastifyEntry(root) : null;
|
|
211
|
-
stopSpinner('Environment detected', true);
|
|
212
|
-
|
|
213
|
-
// Build truthpack
|
|
214
|
-
startSpinner('Building truthpack...');
|
|
215
|
-
const truthpack = await buildTruthpack({ repoRoot: root, fastifyEntry: fastifyEntry || undefined });
|
|
216
|
-
writeTruthpack(root, truthpack);
|
|
217
|
-
stopSpinner('Truthpack built', true);
|
|
218
|
-
|
|
219
|
-
// Write config
|
|
220
|
-
startSpinner('Writing configuration...');
|
|
221
|
-
const cfgDir = path.join(root, ".vibecheck");
|
|
222
|
-
ensureDir(cfgDir);
|
|
223
|
-
|
|
224
|
-
const cfg = {
|
|
225
|
-
version: 1,
|
|
226
|
-
detected: {
|
|
227
|
-
packageManager: pm,
|
|
228
|
-
next: next.enabled,
|
|
229
|
-
fastify: fastify.enabled
|
|
230
|
-
},
|
|
231
|
-
fastifyEntry: fastifyEntry || null
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
fs.writeFileSync(path.join(cfgDir, "config.json"), JSON.stringify(cfg, null, 2), "utf8");
|
|
235
|
-
stopSpinner('Configuration written', true);
|
|
236
|
-
|
|
237
|
-
// Generate env template
|
|
238
|
-
startSpinner('Generating env template...');
|
|
239
|
-
const envRes = writeEnvTemplateFromTruthpack(root, truthpack);
|
|
240
|
-
stopSpinner(envRes.wrote ? `Env template generated (+${envRes.added.length} vars)` : 'Env template up to date', true);
|
|
241
|
-
|
|
242
|
-
// Add scripts
|
|
243
|
-
startSpinner('Updating package.json scripts...');
|
|
244
|
-
const scriptsToAdd = {
|
|
245
|
-
"vibecheck:ctx": "vibecheck ctx",
|
|
246
|
-
"vibecheck:ship": "vibecheck ship",
|
|
247
|
-
"vibecheck:fix": "vibecheck fix --apply",
|
|
248
|
-
"vibecheck:pr": "vibecheck pr"
|
|
249
|
-
};
|
|
250
|
-
const changedScripts = upsertScripts(pkg, scriptsToAdd);
|
|
251
|
-
if (changedScripts.length) writePkg(pkgPath, pkg);
|
|
252
|
-
stopSpinner(changedScripts.length ? `Added ${changedScripts.length} scripts` : 'Scripts up to date', true);
|
|
253
|
-
|
|
254
|
-
// Summary
|
|
255
|
-
printSection('DETECTED', ICONS.gear);
|
|
256
|
-
console.log();
|
|
257
|
-
console.log(` ${ICONS.npm} ${c.bold}Package Manager:${c.reset} ${colors.accent}${pm}${c.reset}`);
|
|
258
|
-
console.log(` ${colors.next}${ICONS.next}${c.reset} ${c.bold}Next.js:${c.reset} ${next.enabled ? `${colors.shipGreen}yes${c.reset}` : `${c.dim}no${c.reset}`}`);
|
|
259
|
-
console.log(` ${colors.fastify}${ICONS.fastify}${c.reset} ${c.bold}Fastify:${c.reset} ${fastify.enabled ? `${colors.shipGreen}yes${c.reset}` : `${c.dim}no${c.reset}`}`);
|
|
260
|
-
if (fastifyEntry) {
|
|
261
|
-
console.log(` ${c.dim} Entry:${c.reset} ${colors.accent}${fastifyEntry}${c.reset}`);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
printSection('CREATED FILES', ICONS.folder);
|
|
265
|
-
console.log();
|
|
266
|
-
console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}.vibecheck/config.json${c.reset}`);
|
|
267
|
-
console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}.vibecheck/truthpack.json${c.reset}`);
|
|
268
|
-
if (envRes.wrote) {
|
|
269
|
-
console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}${envRes.outRel}${c.reset} ${c.dim}(+${envRes.added.length} vars)${c.reset}`);
|
|
270
|
-
}
|
|
271
|
-
if (changedScripts.length) {
|
|
272
|
-
console.log(` ${colors.shipGreen}${ICONS.check}${c.reset} ${ICONS.file} ${colors.accent}package.json${c.reset} ${c.dim}(${changedScripts.join(', ')})${c.reset}`);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Installation complete
|
|
276
|
-
console.log();
|
|
277
|
-
console.log(` ${colors.shipGreen}${ICONS.sparkle}${c.reset} Installation complete!`);
|
|
278
|
-
console.log();
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
module.exports = { runInstall };
|