@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
|
@@ -213,6 +213,8 @@ function parseArgs(args) {
|
|
|
213
213
|
current: null,
|
|
214
214
|
hallucination: false,
|
|
215
215
|
save: false,
|
|
216
|
+
gate: false, // Exit non-zero on regressions (for CI)
|
|
217
|
+
preCommit: false, // Pre-commit mode - fast check with minimal output
|
|
216
218
|
path: process.cwd(),
|
|
217
219
|
};
|
|
218
220
|
|
|
@@ -224,6 +226,8 @@ function parseArgs(args) {
|
|
|
224
226
|
else if (arg === '--current' || arg === '-c') opts.current = args[++i];
|
|
225
227
|
else if (arg === '--hallucination' || arg === '--hall') opts.hallucination = true;
|
|
226
228
|
else if (arg === '--save' || arg === '-s') opts.save = true;
|
|
229
|
+
else if (arg === '--gate') opts.gate = true;
|
|
230
|
+
else if (arg === '--pre-commit') opts.preCommit = true;
|
|
227
231
|
else if (arg === '--path' || arg === '-p') opts.path = args[++i];
|
|
228
232
|
else if (!arg.startsWith('-')) opts.path = arg;
|
|
229
233
|
}
|
|
@@ -245,15 +249,22 @@ ${c.bold}OPTIONS${c.reset}
|
|
|
245
249
|
-c, --current <file> Path to current results file (default: latest)
|
|
246
250
|
--hallucination Include hallucination scoring ${c.magenta}[PRO]${c.reset}
|
|
247
251
|
-s, --save Save checkpoint to .vibecheck/checkpoints/
|
|
252
|
+
--gate Exit non-zero on regressions (for CI)
|
|
253
|
+
--pre-commit Pre-commit mode - fast check with minimal output
|
|
248
254
|
--json Output as JSON
|
|
249
255
|
-p, --path <dir> Project path (default: current directory)
|
|
250
256
|
-h, --help Show this help
|
|
251
257
|
|
|
258
|
+
${c.bold}CI INTEGRATION${c.reset}
|
|
259
|
+
vibecheck checkpoint --gate # Fail CI on regressions
|
|
260
|
+
vibecheck checkpoint --pre-commit # Fast check for git hooks
|
|
261
|
+
|
|
252
262
|
${c.bold}EXAMPLES${c.reset}
|
|
253
263
|
vibecheck checkpoint # Compare latest vs baseline
|
|
254
264
|
vibecheck checkpoint --baseline scan-v1.json # Use specific baseline
|
|
255
265
|
vibecheck checkpoint --hallucination # Include AI hallucination check
|
|
256
266
|
vibecheck checkpoint --save # Save checkpoint report
|
|
267
|
+
vibecheck checkpoint --gate --json # CI: exit 1 on regressions
|
|
257
268
|
|
|
258
269
|
${c.bold}OUTPUT${c.reset}
|
|
259
270
|
• Fixed issues - Issues resolved since baseline
|
|
@@ -261,6 +272,10 @@ ${c.bold}OUTPUT${c.reset}
|
|
|
261
272
|
• Regressions - New issues introduced
|
|
262
273
|
• Hallucination score - AI-generated code issues ${c.magenta}[PRO]${c.reset}
|
|
263
274
|
|
|
275
|
+
${c.bold}EXIT CODES${c.reset}
|
|
276
|
+
0 No regressions (or --gate not specified)
|
|
277
|
+
1 Regressions detected (with --gate flag)
|
|
278
|
+
|
|
264
279
|
${c.bold}TIER${c.reset}
|
|
265
280
|
${c.cyan}FREE${c.reset} Basic comparison (fixed, remaining, regressions)
|
|
266
281
|
${c.magenta}PRO${c.reset} + Hallucination scoring
|
|
@@ -382,10 +397,29 @@ async function runCheckpoint(args) {
|
|
|
382
397
|
fs.writeFileSync(checkpointFile, JSON.stringify(checkpoint, null, 2));
|
|
383
398
|
}
|
|
384
399
|
|
|
400
|
+
// Pre-commit mode - minimal output for git hooks
|
|
401
|
+
if (opts.preCommit) {
|
|
402
|
+
const hasRegressions = checkpoint.summary.regressions > 0;
|
|
403
|
+
if (hasRegressions) {
|
|
404
|
+
console.log(`${c.red}${icons.cross}${c.reset} Checkpoint: ${checkpoint.summary.regressions} regression(s) detected`);
|
|
405
|
+
for (const r of comparison.regressions.slice(0, 3)) {
|
|
406
|
+
console.log(` ${c.dim}${r.title || r.id || r.type}${c.reset}`);
|
|
407
|
+
}
|
|
408
|
+
if (comparison.regressions.length > 3) {
|
|
409
|
+
console.log(` ${c.dim}... and ${comparison.regressions.length - 3} more${c.reset}`);
|
|
410
|
+
}
|
|
411
|
+
return 1;
|
|
412
|
+
} else {
|
|
413
|
+
console.log(`${c.green}${icons.check}${c.reset} Checkpoint passed (${checkpoint.summary.fixed} fixed, ${checkpoint.summary.remaining} remaining)`);
|
|
414
|
+
return 0;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
385
418
|
// JSON output
|
|
386
419
|
if (opts.json) {
|
|
387
420
|
console.log(JSON.stringify(checkpoint, null, 2));
|
|
388
|
-
|
|
421
|
+
const exitCode = opts.gate && checkpoint.summary.regressions > 0 ? 1 : 0;
|
|
422
|
+
return exitCode;
|
|
389
423
|
}
|
|
390
424
|
|
|
391
425
|
// Human-readable output
|
|
@@ -496,8 +530,15 @@ ${c.bold}╔══════════════════════
|
|
|
496
530
|
}
|
|
497
531
|
console.log();
|
|
498
532
|
|
|
499
|
-
// Return code:
|
|
500
|
-
|
|
533
|
+
// Return code:
|
|
534
|
+
// - With --gate: 1 if regressions, 0 otherwise
|
|
535
|
+
// - Without --gate: always 0 (informational only)
|
|
536
|
+
if (opts.gate && comparison.regressions.length > 0) {
|
|
537
|
+
console.log(`${c.yellow}${icons.warning} Gate mode: Exiting with code 1 due to regressions${c.reset}\n`);
|
|
538
|
+
return 1;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return 0;
|
|
501
542
|
}
|
|
502
543
|
|
|
503
544
|
module.exports = { runCheckpoint };
|
package/bin/runners/runDoctor.js
CHANGED
|
@@ -574,10 +574,18 @@ function runDoctorLegacy() {
|
|
|
574
574
|
|
|
575
575
|
console.log("");
|
|
576
576
|
if (hasIssues) {
|
|
577
|
-
console.log(" ❌ Issues found. Fix them and run doctor again
|
|
577
|
+
console.log(" ❌ Issues found. Fix them and run doctor again.");
|
|
578
|
+
console.log();
|
|
579
|
+
console.log(` ${c.dim}Need help?${c.reset} ${colors.accent}vibecheck fix${c.reset} ${c.dim}can auto-repair many issues${c.reset}`);
|
|
580
|
+
console.log(` ${c.dim}Requires STARTER plan • vibecheck.dev/pricing${c.reset}`);
|
|
581
|
+
console.log();
|
|
578
582
|
return 2;
|
|
579
583
|
} else {
|
|
580
|
-
console.log(" ✅ Environment healthy
|
|
584
|
+
console.log(" ✅ Environment healthy!");
|
|
585
|
+
console.log();
|
|
586
|
+
console.log(` ${c.dim}Ready to scan?${c.reset} ${colors.accent}vibecheck scan${c.reset} ${c.dim}finds AI mistakes before they ship${c.reset}`);
|
|
587
|
+
console.log(` ${c.dim}Want verified proof?${c.reset} ${colors.accent}vibecheck prove${c.reset} ${c.dim}(PRO) records video evidence${c.reset}`);
|
|
588
|
+
console.log();
|
|
581
589
|
return 0;
|
|
582
590
|
}
|
|
583
591
|
}
|
package/bin/runners/runFix.js
CHANGED
|
@@ -330,6 +330,17 @@ function getMissionIcon(missionType) {
|
|
|
330
330
|
'FIX_FAKE_SUCCESS': ICONS.ghost,
|
|
331
331
|
'FIX_ENV_CONTRACT': ICONS.env,
|
|
332
332
|
'FIX_DEAD_UI': ICONS.dead,
|
|
333
|
+
// New enhanced mission types
|
|
334
|
+
'FIX_EMPTY_CATCH': ICONS.bug,
|
|
335
|
+
'FIX_TEST_KEYS': ICONS.key,
|
|
336
|
+
'FIX_MOCK_DOMAINS': ICONS.link,
|
|
337
|
+
'FIX_PLACEHOLDER_DATA': ICONS.doc,
|
|
338
|
+
'FIX_HARDCODED_SECRETS': ICONS.lock,
|
|
339
|
+
'FIX_SIMULATED_BILLING': ICONS.money,
|
|
340
|
+
'FIX_SILENT_FALLBACK': ICONS.warning,
|
|
341
|
+
'SYNC_CONTRACTS': ICONS.graph,
|
|
342
|
+
'FIX_ROUTE_DRIFT': ICONS.route,
|
|
343
|
+
'FIX_AUTH_DRIFT': ICONS.shield,
|
|
333
344
|
};
|
|
334
345
|
return icons[missionType] || ICONS.mission;
|
|
335
346
|
}
|
|
@@ -344,6 +355,17 @@ function getMissionColor(missionType) {
|
|
|
344
355
|
'FIX_FAKE_SUCCESS': colors.medium,
|
|
345
356
|
'FIX_ENV_CONTRACT': colors.low,
|
|
346
357
|
'FIX_DEAD_UI': colors.low,
|
|
358
|
+
// New enhanced mission types
|
|
359
|
+
'FIX_EMPTY_CATCH': colors.high,
|
|
360
|
+
'FIX_TEST_KEYS': colors.critical,
|
|
361
|
+
'FIX_MOCK_DOMAINS': colors.critical,
|
|
362
|
+
'FIX_PLACEHOLDER_DATA': colors.medium,
|
|
363
|
+
'FIX_HARDCODED_SECRETS': colors.critical,
|
|
364
|
+
'FIX_SIMULATED_BILLING': colors.critical,
|
|
365
|
+
'FIX_SILENT_FALLBACK': colors.high,
|
|
366
|
+
'SYNC_CONTRACTS': colors.medium,
|
|
367
|
+
'FIX_ROUTE_DRIFT': colors.medium,
|
|
368
|
+
'FIX_AUTH_DRIFT': colors.critical,
|
|
347
369
|
};
|
|
348
370
|
return colorMap[missionType] || colors.accent;
|
|
349
371
|
}
|
|
@@ -579,7 +601,7 @@ async function runFix(args) {
|
|
|
579
601
|
|
|
580
602
|
// Show autopilot header if in autopilot mode
|
|
581
603
|
if (opts.autopilot) {
|
|
582
|
-
|
|
604
|
+
printLoopHeader(maxSteps, stagnationLimit);
|
|
583
605
|
}
|
|
584
606
|
|
|
585
607
|
for (let step = 1; step <= maxSteps; step++) {
|
|
@@ -801,349 +823,21 @@ async function runFix(args) {
|
|
|
801
823
|
return 1;
|
|
802
824
|
}
|
|
803
825
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
const scanResult = await loadLatestScan(projectPath);
|
|
813
|
-
if (!scanResult) {
|
|
814
|
-
console.error(`${c.red}Error:${c.reset} No scan results found. Run 'vibecheck scan' first.\n`);
|
|
815
|
-
return EXIT_CODES.MISCONFIG;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
// Filter to fixable findings
|
|
819
|
-
const fixable = scanResult.findings.filter(f => {
|
|
820
|
-
const ruleId = f.ruleId || '';
|
|
821
|
-
return f.autofixAvailable && ALLOWED_FIX_TYPES.includes(ruleId);
|
|
822
|
-
});
|
|
823
|
-
|
|
824
|
-
if (fixable.length === 0) {
|
|
825
|
-
console.log(`${c.green}✓${c.reset} No mechanical fixes available.\n`);
|
|
826
|
-
console.log(`${c.dim}All remaining issues require manual intervention.${c.reset}\n`);
|
|
827
|
-
return EXIT_CODES.PASS;
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
console.log(`${c.bold}Fixable Issues (${fixable.length}):${c.reset}\n`);
|
|
831
|
-
|
|
832
|
-
for (const finding of fixable) {
|
|
833
|
-
const patch = generatePatchPreview(projectPath, finding);
|
|
834
|
-
|
|
835
|
-
console.log(`${c.blue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}`);
|
|
836
|
-
console.log(`${c.bold}${finding.id?.full || finding.id}${c.reset} - ${finding.ruleId}`);
|
|
837
|
-
console.log(`${c.dim}File:${c.reset} ${finding.file}:${finding.line}`);
|
|
838
|
-
console.log(`${c.dim}Why:${c.reset} ${finding.message || finding.description}`);
|
|
839
|
-
console.log(`${c.dim}Evidence:${c.reset} ${finding.evidence?.[0]?.snippet || 'See file'}`);
|
|
840
|
-
console.log('');
|
|
841
|
-
console.log(`${c.bold}Proposed Change:${c.reset}`);
|
|
842
|
-
console.log(patch.preview);
|
|
843
|
-
console.log('');
|
|
844
|
-
console.log(`${c.dim}Risk:${c.reset} ${patch.risk}`);
|
|
845
|
-
console.log(`${c.dim}Verification:${c.reset} Re-run scan to confirm no regression`);
|
|
846
|
-
console.log('');
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
console.log(`${c.blue}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${c.reset}`);
|
|
850
|
-
console.log(`\n${c.bold}To apply these fixes:${c.reset}`);
|
|
851
|
-
console.log(` ${c.cyan}vibecheck fix --apply${c.reset}\n`);
|
|
852
|
-
console.log(`${c.dim}This will:${c.reset}`);
|
|
853
|
-
console.log(` 1. Check for clean git state`);
|
|
854
|
-
console.log(` 2. Create branch: vibecheck/fix-<timestamp>`);
|
|
855
|
-
console.log(` 3. Apply patches with structured commits`);
|
|
856
|
-
console.log(` 4. Re-run scan to verify no regression\n`);
|
|
857
|
-
|
|
858
|
-
return EXIT_CODES.PASS;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
/**
|
|
862
|
-
* Apply fixes with proof-gating
|
|
863
|
-
*/
|
|
864
|
-
async function applyFixes(projectPath, opts) {
|
|
865
|
-
console.log(`\n${c.cyan}${c.bold}🔧 APPLYING FIXES${c.reset}\n`);
|
|
866
|
-
|
|
867
|
-
// Step 1: Check git state
|
|
868
|
-
if (!isGitClean(projectPath)) {
|
|
869
|
-
console.error(`${c.red}Error:${c.reset} Git working directory is not clean.`);
|
|
870
|
-
console.error(`${c.dim}Commit or stash your changes before applying fixes.${c.reset}\n`);
|
|
871
|
-
return EXIT_CODES.MISCONFIG;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
const scanResult = await loadLatestScan(projectPath);
|
|
875
|
-
if (!scanResult) {
|
|
876
|
-
console.error(`${c.red}Error:${c.reset} No scan results found. Run 'vibecheck scan' first.\n`);
|
|
877
|
-
return EXIT_CODES.MISCONFIG;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
const fixable = scanResult.findings.filter(f => {
|
|
881
|
-
const ruleId = f.ruleId || '';
|
|
882
|
-
return f.autofixAvailable && ALLOWED_FIX_TYPES.includes(ruleId);
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
if (fixable.length === 0) {
|
|
886
|
-
console.log(`${c.green}✓${c.reset} No fixes to apply.\n`);
|
|
887
|
-
return EXIT_CODES.PASS;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
// Step 2: Create branch
|
|
891
|
-
const branchName = `vibecheck/fix-${Date.now()}`;
|
|
892
|
-
try {
|
|
893
|
-
execSync(`git checkout -b ${branchName}`, { cwd: projectPath, stdio: 'pipe' });
|
|
894
|
-
console.log(`${c.green}✓${c.reset} Created branch: ${branchName}\n`);
|
|
895
|
-
} catch (err) {
|
|
896
|
-
console.error(`${c.red}Error:${c.reset} Failed to create branch: ${err.message}\n`);
|
|
897
|
-
return EXIT_CODES.INTERNAL;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// Step 3: Apply fixes
|
|
901
|
-
let applied = 0;
|
|
902
|
-
let failed = 0;
|
|
903
|
-
|
|
904
|
-
for (const finding of fixable) {
|
|
905
|
-
try {
|
|
906
|
-
const result = await applyAutofix(projectPath, finding, opts);
|
|
907
|
-
if (result) {
|
|
908
|
-
applied++;
|
|
909
|
-
|
|
910
|
-
// Commit with structured message
|
|
911
|
-
const commitMsg = `fix(${finding.ruleId}): ${finding.id?.full || finding.id}\n\nFile: ${finding.file}:${finding.line}\nEvidence: ${finding.evidence?.[0]?.snippet || 'See scan results'}\n\nApplied by vibecheck fix --apply`;
|
|
912
|
-
|
|
913
|
-
try {
|
|
914
|
-
execSync(`git add "${finding.file}"`, { cwd: projectPath, stdio: 'pipe' });
|
|
915
|
-
execSync(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`, { cwd: projectPath, stdio: 'pipe' });
|
|
916
|
-
} catch {
|
|
917
|
-
// File might not have changed, continue
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
console.log(`${c.green}✓${c.reset} Fixed: ${finding.id?.full || finding.id}`);
|
|
921
|
-
} else {
|
|
922
|
-
failed++;
|
|
923
|
-
console.log(`${c.yellow}⚠${c.reset} Skipped: ${finding.id?.full || finding.id}`);
|
|
924
|
-
}
|
|
925
|
-
} catch (error) {
|
|
926
|
-
failed++;
|
|
927
|
-
console.log(`${c.red}✗${c.reset} Error: ${finding.id?.full || finding.id} - ${error.message}`);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
console.log('');
|
|
932
|
-
console.log(`${c.bold}Summary:${c.reset}`);
|
|
933
|
-
console.log(` Applied: ${c.green}${applied}${c.reset}`);
|
|
934
|
-
console.log(` Skipped: ${c.yellow}${failed}${c.reset}`);
|
|
935
|
-
console.log('');
|
|
936
|
-
|
|
937
|
-
// Step 4: Re-run scan to verify
|
|
938
|
-
console.log(`${c.dim}Verifying fixes...${c.reset}\n`);
|
|
939
|
-
|
|
940
|
-
try {
|
|
941
|
-
const { runScan } = require('./runScan');
|
|
942
|
-
const verifyResult = await runScan(['--json', '--path', projectPath]);
|
|
943
|
-
|
|
944
|
-
if (verifyResult === 0 || verifyResult === EXIT_CODES.PASS) {
|
|
945
|
-
console.log(`${c.green}✓${c.reset} Verification passed - no regressions\n`);
|
|
946
|
-
} else {
|
|
947
|
-
console.log(`${c.yellow}⚠${c.reset} Verification found issues - review before merging\n`);
|
|
948
|
-
}
|
|
949
|
-
} catch {
|
|
950
|
-
console.log(`${c.dim}Run 'vibecheck scan' to verify manually.${c.reset}\n`);
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
console.log(`${c.bold}Rollback:${c.reset}`);
|
|
954
|
-
console.log(` ${c.cyan}git checkout main && git branch -D ${branchName}${c.reset}\n`);
|
|
955
|
-
|
|
956
|
-
return applied > 0 ? EXIT_CODES.PASS : EXIT_CODES.FAIL;
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
/**
|
|
960
|
-
* Check if git working directory is clean
|
|
961
|
-
*/
|
|
962
|
-
function isGitClean(projectPath) {
|
|
963
|
-
try {
|
|
964
|
-
const status = execSync('git status --porcelain', { cwd: projectPath, encoding: 'utf8' });
|
|
965
|
-
return status.trim() === '';
|
|
966
|
-
} catch {
|
|
967
|
-
return false;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
/**
|
|
972
|
-
* Generate patch preview for a finding
|
|
973
|
-
*/
|
|
974
|
-
function generatePatchPreview(projectPath, finding) {
|
|
975
|
-
const filePath = path.join(projectPath, finding.file);
|
|
976
|
-
|
|
977
|
-
if (!fs.existsSync(filePath)) {
|
|
978
|
-
return { preview: ` ${c.red}File not found${c.reset}`, risk: 'N/A' };
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
982
|
-
const lines = content.split('\n');
|
|
983
|
-
const lineIndex = finding.line - 1;
|
|
984
|
-
|
|
985
|
-
if (lineIndex < 0 || lineIndex >= lines.length) {
|
|
986
|
-
return { preview: ` ${c.red}Invalid line number${c.reset}`, risk: 'N/A' };
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
const before = lines[lineIndex];
|
|
990
|
-
let after = before;
|
|
991
|
-
let risk = 'Low';
|
|
992
|
-
|
|
993
|
-
// Generate preview based on rule type
|
|
994
|
-
switch (finding.ruleId) {
|
|
995
|
-
case 'empty-catch':
|
|
996
|
-
after = before.replace(/catch\s*(?:\(([^)]*)\))?\s*\{\s*\}/, (m, v) => {
|
|
997
|
-
const varName = v || 'err';
|
|
998
|
-
return `catch (${varName}) { console.error('Error:', ${varName}); throw ${varName}; }`;
|
|
999
|
-
});
|
|
1000
|
-
risk = 'Low - adds proper error handling';
|
|
1001
|
-
break;
|
|
1002
|
-
case 'dangerous-default':
|
|
1003
|
-
after = before.replace(/\s*(\|\||\?\?)\s*['"][^'"]*['"]/, '');
|
|
1004
|
-
risk = 'Medium - removes fallback (may require env var)';
|
|
1005
|
-
break;
|
|
1006
|
-
case 'placeholder-value':
|
|
1007
|
-
after = before.replace(/(CHANGEME|REPLACE_ME|YOUR_\w+|INSERT_\w+)/g, "/* TODO: Replace $1 */ ''");
|
|
1008
|
-
risk = 'Low - flags for manual replacement';
|
|
1009
|
-
break;
|
|
1010
|
-
default:
|
|
1011
|
-
return { preview: ` ${c.dim}No preview available${c.reset}`, risk: 'Unknown' };
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
return {
|
|
1015
|
-
preview: ` ${c.red}- ${before.trim()}${c.reset}\n ${c.green}+ ${after.trim()}${c.reset}`,
|
|
1016
|
-
risk,
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
async function applyAutofix(projectPath, finding, options) {
|
|
1021
|
-
const filePath = path.join(projectPath, finding.file);
|
|
1022
|
-
|
|
1023
|
-
if (!fs.existsSync(filePath)) {
|
|
1024
|
-
throw new Error(`File not found: ${finding.file}`);
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
1028
|
-
const lines = content.split('\n');
|
|
1029
|
-
const lineIndex = finding.line - 1;
|
|
1030
|
-
|
|
1031
|
-
if (lineIndex < 0 || lineIndex >= lines.length) {
|
|
1032
|
-
throw new Error(`Invalid line number: ${finding.line}`);
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
let newContent = content;
|
|
1036
|
-
const ruleId = finding.ruleId;
|
|
1037
|
-
|
|
1038
|
-
// Apply fixes based on rule type
|
|
1039
|
-
switch (ruleId) {
|
|
1040
|
-
case 'empty-catch':
|
|
1041
|
-
newContent = fixEmptyCatch(lines, lineIndex, filePath);
|
|
1042
|
-
break;
|
|
1043
|
-
case 'dangerous-default':
|
|
1044
|
-
newContent = fixDangerousDefault(lines, lineIndex, filePath);
|
|
1045
|
-
break;
|
|
1046
|
-
case 'placeholder-value':
|
|
1047
|
-
newContent = fixPlaceholderValue(lines, lineIndex, filePath);
|
|
1048
|
-
break;
|
|
1049
|
-
default:
|
|
1050
|
-
return false;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
if (newContent !== content) {
|
|
1054
|
-
if (!options.dryRun) {
|
|
1055
|
-
fs.writeFileSync(filePath, newContent, 'utf8');
|
|
1056
|
-
}
|
|
1057
|
-
return true;
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
return false;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
function fixEmptyCatch(lines, lineIndex, filePath) {
|
|
1064
|
-
const line = lines[lineIndex];
|
|
1065
|
-
const newLines = [...lines];
|
|
1066
|
-
|
|
1067
|
-
// Find the catch block
|
|
1068
|
-
if (line.includes('catch') && line.includes('{')) {
|
|
1069
|
-
// Simple case: catch {} on one line
|
|
1070
|
-
if (line.match(/catch\s*(?:\([^)]*\))?\s*\{\s*\}/)) {
|
|
1071
|
-
const indent = line.match(/^(\s*)/)?.[1] || '';
|
|
1072
|
-
newLines[lineIndex] = line.replace(
|
|
1073
|
-
/catch\s*(?:\(([^)]*)\))?\s*\{\s*\}/,
|
|
1074
|
-
(match, errVar) => {
|
|
1075
|
-
const varName = errVar || 'err';
|
|
1076
|
-
return `catch (${varName}) {\n${indent} console.error('Error:', ${varName});\n${indent} throw ${varName};\n${indent}}`;
|
|
1077
|
-
}
|
|
1078
|
-
);
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
return newLines.join('\n');
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
function fixDangerousDefault(lines, lineIndex, filePath) {
|
|
1086
|
-
const line = lines[lineIndex];
|
|
1087
|
-
const newLines = [...lines];
|
|
1088
|
-
|
|
1089
|
-
// Remove dangerous default
|
|
1090
|
-
if (line.includes('process.env.') && (line.includes('||') || line.includes('??'))) {
|
|
1091
|
-
newLines[lineIndex] = line.replace(
|
|
1092
|
-
/\s*(\|\||\?\?)\s*['"][^'"]*['"]/,
|
|
1093
|
-
''
|
|
1094
|
-
);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
return newLines.join('\n');
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
function fixPlaceholderValue(lines, lineIndex, filePath) {
|
|
1101
|
-
const line = lines[lineIndex];
|
|
1102
|
-
const newLines = [...lines];
|
|
1103
|
-
|
|
1104
|
-
// Replace placeholder with TODO comment
|
|
1105
|
-
newLines[lineIndex] = line.replace(
|
|
1106
|
-
/(CHANGEME|REPLACE_ME|YOUR_[A-Z0-9_]+|INSERT_[A-Z0-9_]+)/g,
|
|
1107
|
-
(match) => `/* TODO: Replace ${match} */ ''`
|
|
1108
|
-
);
|
|
1109
|
-
|
|
1110
|
-
return newLines.join('\n');
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
async function loadLatestScan(projectPath) {
|
|
1114
|
-
const scanDir = path.join(projectPath, '.vibecheck', 'reality-sniff', 'scans');
|
|
1115
|
-
|
|
1116
|
-
if (!fs.existsSync(scanDir)) {
|
|
1117
|
-
return null;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
try {
|
|
1121
|
-
const files = fs.readdirSync(scanDir)
|
|
1122
|
-
.filter(f => f.endsWith('.json'))
|
|
1123
|
-
.map(f => ({
|
|
1124
|
-
name: f,
|
|
1125
|
-
path: path.join(scanDir, f),
|
|
1126
|
-
mtime: fs.statSync(path.join(scanDir, f)).mtime,
|
|
1127
|
-
}))
|
|
1128
|
-
.sort((a, b) => b.mtime - a.mtime);
|
|
1129
|
-
|
|
1130
|
-
if (files.length === 0) {
|
|
1131
|
-
return null;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
const content = fs.readFileSync(files[0].path, 'utf8');
|
|
1135
|
-
return JSON.parse(content);
|
|
1136
|
-
} catch {
|
|
1137
|
-
return null;
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
826
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
827
|
+
// NOTE: Legacy rule-based fix functions (showFixPlan, applyFixes, etc.) were removed.
|
|
828
|
+
// The fix command now uses the mission-based system exclusively, which provides:
|
|
829
|
+
// - LLM-generated patches with Reality Firewall prompt
|
|
830
|
+
// - Automatic backup/restore on failure
|
|
831
|
+
// - Progress detection and stagnation handling
|
|
832
|
+
// - Integration with ship verification
|
|
833
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
1140
834
|
|
|
1141
835
|
function parseArgs(args) {
|
|
1142
836
|
// Parse global flags first
|
|
1143
837
|
const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
|
|
1144
838
|
|
|
1145
839
|
const opts = {
|
|
1146
|
-
...globalFlags, // Merge global flags (json, verbose, noBanner, quiet, ci, etc.)
|
|
840
|
+
...globalFlags, // Merge global flags (json, verbose, noBanner, quiet, ci, help, etc.)
|
|
1147
841
|
path: globalFlags.path || process.cwd(),
|
|
1148
842
|
apply: false,
|
|
1149
843
|
promptOnly: false,
|
|
@@ -1153,7 +847,7 @@ function parseArgs(args) {
|
|
|
1153
847
|
maxSteps: 10,
|
|
1154
848
|
stagnationLimit: 2,
|
|
1155
849
|
fastifyEntry: null,
|
|
1156
|
-
|
|
850
|
+
// Note: help comes from globalFlags, don't override it here
|
|
1157
851
|
};
|
|
1158
852
|
|
|
1159
853
|
// Parse command-specific args from cleanArgs
|
|
@@ -1208,13 +902,29 @@ function printHelp(showBanner = true) {
|
|
|
1208
902
|
${colors.shipGreen}${ICONS.stop}${c.reset} Progress detector stops on stagnation
|
|
1209
903
|
|
|
1210
904
|
${c.bold}Mission Types:${c.reset}
|
|
905
|
+
${c.dim}Security & Auth:${c.reset}
|
|
1211
906
|
${colors.critical}${ICONS.lock}${c.reset} REMOVE_OWNER_MODE ${c.dim}Delete backdoor env bypass${c.reset}
|
|
907
|
+
${colors.critical}${ICONS.lock}${c.reset} FIX_HARDCODED_SECRETS ${c.dim}Move secrets to env vars${c.reset}
|
|
908
|
+
${colors.critical}${ICONS.key}${c.reset} FIX_TEST_KEYS ${c.dim}Replace test API keys${c.reset}
|
|
909
|
+
${colors.critical}${ICONS.shield}${c.reset} FIX_AUTH_DRIFT ${c.dim}Restore removed auth patterns${c.reset}
|
|
910
|
+
${colors.medium}${ICONS.auth}${c.reset} ADD_SERVER_AUTH ${c.dim}Add auth to sensitive endpoints${c.reset}
|
|
911
|
+
|
|
912
|
+
${c.dim}Billing & Payments:${c.reset}
|
|
1212
913
|
${colors.high}${ICONS.money}${c.reset} FIX_STRIPE_WEBHOOKS ${c.dim}Add signature verification + idempotency${c.reset}
|
|
1213
914
|
${colors.high}${ICONS.shield}${c.reset} ENFORCE_PAID_SURFACE ${c.dim}Add server-side entitlement checks${c.reset}
|
|
1214
|
-
${colors.
|
|
1215
|
-
|
|
915
|
+
${colors.critical}${ICONS.money}${c.reset} FIX_SIMULATED_BILLING ${c.dim}Replace fake billing with real${c.reset}
|
|
916
|
+
|
|
917
|
+
${c.dim}Reality & Data:${c.reset}
|
|
918
|
+
${colors.critical}${ICONS.link}${c.reset} FIX_MOCK_DOMAINS ${c.dim}Replace localhost/mock URLs${c.reset}
|
|
1216
919
|
${colors.medium}${ICONS.ghost}${c.reset} FIX_FAKE_SUCCESS ${c.dim}Gate success UI on network result${c.reset}
|
|
920
|
+
${colors.medium}${ICONS.doc}${c.reset} FIX_PLACEHOLDER_DATA ${c.dim}Replace lorem ipsum with real data${c.reset}
|
|
921
|
+
${colors.high}${ICONS.warning}${c.reset} FIX_SILENT_FALLBACK ${c.dim}Make failures visible${c.reset}
|
|
922
|
+
|
|
923
|
+
${c.dim}Code Quality:${c.reset}
|
|
924
|
+
${colors.high}${ICONS.bug}${c.reset} FIX_EMPTY_CATCH ${c.dim}Add error handling to catch blocks${c.reset}
|
|
925
|
+
${colors.medium}${ICONS.route}${c.reset} FIX_MISSING_ROUTE ${c.dim}Wire client refs to server routes${c.reset}
|
|
1217
926
|
${colors.low}${ICONS.env}${c.reset} FIX_ENV_CONTRACT ${c.dim}Add missing env vars to templates${c.reset}
|
|
927
|
+
${colors.low}${ICONS.dead}${c.reset} FIX_DEAD_UI ${c.dim}Make UI actions functional${c.reset}
|
|
1218
928
|
|
|
1219
929
|
${c.bold}LLM Configuration:${c.reset}
|
|
1220
930
|
${c.dim}Set these environment variables:${c.reset}
|
package/bin/runners/runInit.js
CHANGED
|
@@ -1054,6 +1054,17 @@ function printNextSteps(options = {}) {
|
|
|
1054
1054
|
console.log();
|
|
1055
1055
|
console.log(` ${c.dim}Full docs:${c.reset} ${colors.info}https://docs.vibecheckai.dev${c.reset}`);
|
|
1056
1056
|
console.log();
|
|
1057
|
+
|
|
1058
|
+
// Upsell box
|
|
1059
|
+
console.log(` ${c.dim}╭────────────────────────────────────────────────────────────╮${c.reset}`);
|
|
1060
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
|
|
1061
|
+
console.log(` ${c.dim}│${c.reset} ${colors.accent}⚡ STARTER${c.reset} ${c.dim}•${c.reset} AI-powered fixes, GitHub CI, MCP tools ${c.dim}│${c.reset}`);
|
|
1062
|
+
console.log(` ${c.dim}│${c.reset} ${colors.accent}🏆 PRO${c.reset} ${c.dim}•${c.reset} Runtime proof, verified badges, AI testing ${c.dim}│${c.reset}`);
|
|
1063
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
|
|
1064
|
+
console.log(` ${c.dim}│${c.reset} ${colors.info}vibecheck login${c.reset} ${c.dim}to upgrade • vibecheck.dev/pricing${c.reset} ${c.dim}│${c.reset}`);
|
|
1065
|
+
console.log(` ${c.dim}│${c.reset} ${c.dim}│${c.reset}`);
|
|
1066
|
+
console.log(` ${c.dim}╰────────────────────────────────────────────────────────────╯${c.reset}`);
|
|
1067
|
+
console.log();
|
|
1057
1068
|
}
|
|
1058
1069
|
|
|
1059
1070
|
// ═══════════════════════════════════════════════════════════════════════════════
|