@vibecheckai/cli 3.1.6 → 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.
Files changed (56) hide show
  1. package/README.md +27 -32
  2. package/bin/registry.js +208 -343
  3. package/bin/runners/context/generators/mcp.js +18 -0
  4. package/bin/runners/context/index.js +72 -4
  5. package/bin/runners/context/proof-context.js +293 -1
  6. package/bin/runners/context/security-scanner.js +311 -73
  7. package/bin/runners/lib/analyzers.js +607 -20
  8. package/bin/runners/lib/detectors-v2.js +172 -15
  9. package/bin/runners/lib/entitlements-v2.js +48 -1
  10. package/bin/runners/lib/evidence-pack.js +678 -0
  11. package/bin/runners/lib/html-proof-report.js +913 -0
  12. package/bin/runners/lib/missions/plan.js +231 -41
  13. package/bin/runners/lib/missions/templates.js +125 -0
  14. package/bin/runners/lib/scan-output.js +492 -253
  15. package/bin/runners/lib/ship-output.js +901 -641
  16. package/bin/runners/runCheckpoint.js +44 -3
  17. package/bin/runners/runContext.d.ts +4 -0
  18. package/bin/runners/runContext.js +2 -3
  19. package/bin/runners/runDoctor.js +11 -4
  20. package/bin/runners/runFix.js +51 -341
  21. package/bin/runners/runInit.js +37 -20
  22. package/bin/runners/runPolish.d.ts +4 -0
  23. package/bin/runners/runPolish.js +608 -29
  24. package/bin/runners/runProve.js +210 -25
  25. package/bin/runners/runReality.js +861 -107
  26. package/bin/runners/runScan.js +238 -4
  27. package/bin/runners/runShip.js +19 -3
  28. package/bin/runners/runWatch.js +25 -5
  29. package/bin/vibecheck.js +35 -47
  30. package/mcp-server/consolidated-tools.js +408 -42
  31. package/mcp-server/index.js +152 -15
  32. package/mcp-server/package.json +1 -1
  33. package/mcp-server/proof-tools.js +571 -0
  34. package/mcp-server/tier-auth.js +22 -19
  35. package/mcp-server/tools-v3.js +744 -0
  36. package/mcp-server/truth-firewall-tools.js +190 -4
  37. package/package.json +3 -1
  38. package/bin/runners/runBadge.js +0 -916
  39. package/bin/runners/runContracts.js +0 -105
  40. package/bin/runners/runCtx.js +0 -680
  41. package/bin/runners/runCtxDiff.js +0 -301
  42. package/bin/runners/runCtxGuard.js +0 -176
  43. package/bin/runners/runCtxSync.js +0 -116
  44. package/bin/runners/runExport.js +0 -93
  45. package/bin/runners/runGraph.js +0 -454
  46. package/bin/runners/runInstall.js +0 -273
  47. package/bin/runners/runLabs.js +0 -341
  48. package/bin/runners/runLaunch.js +0 -181
  49. package/bin/runners/runPR.js +0 -255
  50. package/bin/runners/runPermissions.js +0 -310
  51. package/bin/runners/runPreflight.js +0 -580
  52. package/bin/runners/runReplay.js +0 -499
  53. package/bin/runners/runSecurity.js +0 -92
  54. package/bin/runners/runShare.js +0 -212
  55. package/bin/runners/runStatus.js +0 -102
  56. package/bin/runners/runVerify.js +0 -272
@@ -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
- return checkpoint.summary.regressions > 0 ? 1 : 0;
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: 1 if regressions, 0 otherwise
500
- return comparison.regressions.length > 0 ? 1 : 0;
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 };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type declarations for runContext.js
3
+ */
4
+ export function runContext(args: string[]): Promise<number>;
@@ -16,10 +16,9 @@ async function runContext(args) {
16
16
 
17
17
  // Parse command-specific args
18
18
  const opts = {
19
- ...globalFlags, // Merge global flags
19
+ ...globalFlags, // Merge global flags (includes help, json, noBanner, etc.)
20
20
  output: null,
21
- format: "all",
22
- help: false
21
+ format: "all"
23
22
  };
24
23
 
25
24
  for (let i = 0; i < cleanArgs.length; i++) {
@@ -385,9 +385,8 @@ function parseArgs(args) {
385
385
  const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
386
386
 
387
387
  const opts = {
388
- ...globalFlags, // Merge global flags (json, verbose, noBanner, quiet, ci, etc.)
388
+ ...globalFlags, // Merge global flags (json, verbose, noBanner, quiet, ci, help, etc.)
389
389
  path: globalFlags.path || process.cwd(),
390
- help: false,
391
390
  fix: false,
392
391
  dryRun: false,
393
392
  output: null,
@@ -575,10 +574,18 @@ function runDoctorLegacy() {
575
574
 
576
575
  console.log("");
577
576
  if (hasIssues) {
578
- console.log(" ❌ Issues found. Fix them and run doctor again.\n");
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();
579
582
  return 2;
580
583
  } else {
581
- console.log(" ✅ Environment healthy!\n");
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();
582
589
  return 0;
583
590
  }
584
591
  }
@@ -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
- printAutopilotHeader(maxSteps, stagnationLimit);
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
- * Show Fix Plan (default behavior - no changes applied)
806
- */
807
- async function showFixPlan(projectPath, opts) {
808
- console.log(`\n${c.cyan}${c.bold}📋 FIX PLAN${c.reset}\n`);
809
- console.log(`${c.dim}This shows what CAN be fixed. No changes will be made.${c.reset}`);
810
- console.log(`${c.dim}Use --apply to apply changes (requires clean git state).${c.reset}\n`);
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
- help: false,
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.medium}${ICONS.auth}${c.reset} ADD_SERVER_AUTH ${c.dim}Add auth to sensitive endpoints${c.reset}
1215
- ${colors.medium}${ICONS.route}${c.reset} FIX_MISSING_ROUTE ${c.dim}Wire client refs to server routes${c.reset}
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}