@vibecheckai/cli 3.1.8 → 3.2.1

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 (84) hide show
  1. package/bin/registry.js +106 -116
  2. package/bin/runners/context/generators/mcp.js +18 -0
  3. package/bin/runners/context/index.js +72 -4
  4. package/bin/runners/context/proof-context.js +293 -1
  5. package/bin/runners/context/security-scanner.js +311 -73
  6. package/bin/runners/lib/agent-firewall/change-packet/builder.js +214 -0
  7. package/bin/runners/lib/agent-firewall/change-packet/schema.json +228 -0
  8. package/bin/runners/lib/agent-firewall/change-packet/store.js +200 -0
  9. package/bin/runners/lib/agent-firewall/claims/claim-types.js +21 -0
  10. package/bin/runners/lib/agent-firewall/claims/extractor.js +214 -0
  11. package/bin/runners/lib/agent-firewall/claims/patterns.js +24 -0
  12. package/bin/runners/lib/agent-firewall/evidence/auth-evidence.js +88 -0
  13. package/bin/runners/lib/agent-firewall/evidence/contract-evidence.js +75 -0
  14. package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +118 -0
  15. package/bin/runners/lib/agent-firewall/evidence/resolver.js +102 -0
  16. package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +142 -0
  17. package/bin/runners/lib/agent-firewall/evidence/side-effect-evidence.js +145 -0
  18. package/bin/runners/lib/agent-firewall/fs-hook/daemon.js +19 -0
  19. package/bin/runners/lib/agent-firewall/fs-hook/installer.js +87 -0
  20. package/bin/runners/lib/agent-firewall/fs-hook/watcher.js +184 -0
  21. package/bin/runners/lib/agent-firewall/git-hook/pre-commit.js +163 -0
  22. package/bin/runners/lib/agent-firewall/ide-extension/cursor.js +107 -0
  23. package/bin/runners/lib/agent-firewall/ide-extension/vscode.js +68 -0
  24. package/bin/runners/lib/agent-firewall/ide-extension/windsurf.js +66 -0
  25. package/bin/runners/lib/agent-firewall/interceptor/base.js +304 -0
  26. package/bin/runners/lib/agent-firewall/interceptor/cursor.js +35 -0
  27. package/bin/runners/lib/agent-firewall/interceptor/vscode.js +35 -0
  28. package/bin/runners/lib/agent-firewall/interceptor/windsurf.js +34 -0
  29. package/bin/runners/lib/agent-firewall/policy/default-policy.json +84 -0
  30. package/bin/runners/lib/agent-firewall/policy/engine.js +72 -0
  31. package/bin/runners/lib/agent-firewall/policy/loader.js +143 -0
  32. package/bin/runners/lib/agent-firewall/policy/rules/auth-drift.js +50 -0
  33. package/bin/runners/lib/agent-firewall/policy/rules/contract-drift.js +50 -0
  34. package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +61 -0
  35. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +50 -0
  36. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +50 -0
  37. package/bin/runners/lib/agent-firewall/policy/rules/scope.js +93 -0
  38. package/bin/runners/lib/agent-firewall/policy/rules/unsafe-side-effect.js +57 -0
  39. package/bin/runners/lib/agent-firewall/policy/schema.json +183 -0
  40. package/bin/runners/lib/agent-firewall/policy/verdict.js +54 -0
  41. package/bin/runners/lib/agent-firewall/truthpack/index.js +67 -0
  42. package/bin/runners/lib/agent-firewall/truthpack/loader.js +116 -0
  43. package/bin/runners/lib/agent-firewall/unblock/planner.js +337 -0
  44. package/bin/runners/lib/analysis-core.js +198 -180
  45. package/bin/runners/lib/analyzers.js +1394 -224
  46. package/bin/runners/lib/detectors-v2.js +560 -641
  47. package/bin/runners/lib/entitlements-v2.js +48 -1
  48. package/bin/runners/lib/evidence-pack.js +678 -0
  49. package/bin/runners/lib/fingerprint.js +377 -0
  50. package/bin/runners/lib/html-proof-report.js +913 -0
  51. package/bin/runners/lib/missions/plan.js +231 -41
  52. package/bin/runners/lib/missions/templates.js +125 -0
  53. package/bin/runners/lib/route-truth.js +1167 -322
  54. package/bin/runners/lib/scan-output.js +558 -235
  55. package/bin/runners/lib/ship-output.js +901 -641
  56. package/bin/runners/lib/truth.js +1004 -321
  57. package/bin/runners/runAgent.js +161 -0
  58. package/bin/runners/runCheckpoint.js +44 -3
  59. package/bin/runners/runContext.d.ts +4 -0
  60. package/bin/runners/runDoctor.js +10 -2
  61. package/bin/runners/runFirewall.js +134 -0
  62. package/bin/runners/runFirewallHook.js +56 -0
  63. package/bin/runners/runFix.js +51 -341
  64. package/bin/runners/runInit.js +11 -0
  65. package/bin/runners/runPolish.d.ts +4 -0
  66. package/bin/runners/runPolish.js +608 -29
  67. package/bin/runners/runProve.js +210 -25
  68. package/bin/runners/runReality.js +846 -101
  69. package/bin/runners/runScan.js +351 -14
  70. package/bin/runners/runShip.js +19 -3
  71. package/bin/runners/runTruth.js +89 -0
  72. package/bin/runners/runWatch.js +14 -1
  73. package/bin/vibecheck.js +32 -2
  74. package/mcp-server/agent-firewall-interceptor.js +164 -0
  75. package/mcp-server/consolidated-tools.js +408 -42
  76. package/mcp-server/index.js +498 -327
  77. package/mcp-server/proof-tools.js +571 -0
  78. package/mcp-server/tier-auth.js +22 -19
  79. package/mcp-server/tools-v3.js +744 -0
  80. package/mcp-server/truth-context.js +131 -90
  81. package/mcp-server/truth-firewall-tools.js +1494 -941
  82. package/package.json +3 -1
  83. package/bin/runners/runInstall.js +0 -281
  84. package/bin/runners/runLabs.js +0 -341
@@ -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}
@@ -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
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type declarations for runPolish.js
3
+ */
4
+ export function runPolish(args: string[]): Promise<number>;