@vibecheckai/cli 3.2.6 → 3.3.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 (84) hide show
  1. package/bin/registry.js +192 -5
  2. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  3. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  4. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  5. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  6. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  7. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  8. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  11. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  12. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  13. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  14. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  15. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  16. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  17. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  18. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  19. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  20. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  21. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  22. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  23. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  24. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  25. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  26. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  27. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  28. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  29. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  30. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  31. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  32. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  35. package/bin/runners/lib/analyzers.js +81 -18
  36. package/bin/runners/lib/authority-badge.js +425 -0
  37. package/bin/runners/lib/cli-output.js +7 -1
  38. package/bin/runners/lib/error-handler.js +16 -9
  39. package/bin/runners/lib/exit-codes.js +275 -0
  40. package/bin/runners/lib/global-flags.js +37 -0
  41. package/bin/runners/lib/help-formatter.js +413 -0
  42. package/bin/runners/lib/logger.js +38 -0
  43. package/bin/runners/lib/unified-cli-output.js +604 -0
  44. package/bin/runners/lib/upsell.js +148 -0
  45. package/bin/runners/runApprove.js +1200 -0
  46. package/bin/runners/runAuth.js +324 -95
  47. package/bin/runners/runCheckpoint.js +39 -21
  48. package/bin/runners/runClassify.js +859 -0
  49. package/bin/runners/runContext.js +136 -24
  50. package/bin/runners/runDoctor.js +108 -68
  51. package/bin/runners/runFix.js +6 -5
  52. package/bin/runners/runGuard.js +212 -118
  53. package/bin/runners/runInit.js +3 -2
  54. package/bin/runners/runMcp.js +130 -52
  55. package/bin/runners/runPolish.js +43 -20
  56. package/bin/runners/runProve.js +1 -2
  57. package/bin/runners/runReport.js +3 -2
  58. package/bin/runners/runScan.js +63 -44
  59. package/bin/runners/runShip.js +3 -4
  60. package/bin/runners/runValidate.js +19 -2
  61. package/bin/runners/runWatch.js +104 -53
  62. package/bin/vibecheck.js +106 -19
  63. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  64. package/mcp-server/agent-firewall-interceptor.js +367 -31
  65. package/mcp-server/authority-tools.js +569 -0
  66. package/mcp-server/conductor/conflict-resolver.js +588 -0
  67. package/mcp-server/conductor/execution-planner.js +544 -0
  68. package/mcp-server/conductor/index.js +377 -0
  69. package/mcp-server/conductor/lock-manager.js +615 -0
  70. package/mcp-server/conductor/request-queue.js +550 -0
  71. package/mcp-server/conductor/session-manager.js +500 -0
  72. package/mcp-server/conductor/tools.js +510 -0
  73. package/mcp-server/index.js +1149 -243
  74. package/mcp-server/lib/{api-client.js → api-client.cjs} +40 -4
  75. package/mcp-server/lib/logger.cjs +30 -0
  76. package/mcp-server/logger.js +173 -0
  77. package/mcp-server/package.json +2 -2
  78. package/mcp-server/premium-tools.js +2 -2
  79. package/mcp-server/tier-auth.js +245 -35
  80. package/mcp-server/truth-firewall-tools.js +145 -15
  81. package/mcp-server/vibecheck-tools.js +2 -2
  82. package/package.json +2 -3
  83. package/mcp-server/index.old.js +0 -4137
  84. package/mcp-server/package-lock.json +0 -165
@@ -1,22 +1,39 @@
1
1
  /**
2
2
  * runContext.js - AI Rules Generator
3
3
  *
4
- * Generates .cursorrules, .windsurf/rules, and other AI context files.
4
+ * ═══════════════════════════════════════════════════════════════════════════════
5
+ * World-Class AI Context Generation
6
+ * ═══════════════════════════════════════════════════════════════════════════════
5
7
  */
6
8
 
7
9
  const path = require("path");
10
+ const fs = require("fs");
8
11
  const { runContext: contextRunner } = require("./context");
9
- const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
12
+ const { parseGlobalFlags, shouldShowBanner, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
13
+ const { EXIT } = require("./lib/exit-codes");
14
+ const {
15
+ ansi,
16
+ sym,
17
+ renderMinimalHeader,
18
+ renderSectionHeader,
19
+ renderSuccess,
20
+ renderError,
21
+ renderBullet,
22
+ renderFooter,
23
+ Spinner,
24
+ getTierFromKey,
25
+ } = require("./lib/unified-cli-output");
10
26
 
11
27
  async function runContext(args) {
12
- // Parse global flags first
13
28
  const { flags: globalFlags, cleanArgs } = parseGlobalFlags(args);
14
-
29
+ const quiet = shouldSuppressOutput(globalFlags);
30
+ const json = isJsonMode(globalFlags);
15
31
  const root = globalFlags.path || process.cwd();
32
+ const startTime = Date.now();
16
33
 
17
34
  // Parse command-specific args
18
35
  const opts = {
19
- ...globalFlags, // Merge global flags (includes help, json, noBanner, etc.)
36
+ ...globalFlags,
20
37
  output: null,
21
38
  format: "all"
22
39
  };
@@ -29,34 +46,129 @@ async function runContext(args) {
29
46
  }
30
47
 
31
48
  if (opts.help) {
32
- const banner = shouldShowBanner(opts) ? `
33
- ${require('./lib/global-flags').BANNER || ''}
34
- ` : '';
35
- console.log(`${banner}
36
- vibecheck context - Generate AI rules files
37
-
38
- USAGE
39
- vibecheck context Generate all AI rules files
40
- vibecheck context --format X Generate specific format (cursor, windsurf, all)
41
-
42
- OPTIONS
43
- -o, --output <dir> Output directory (default: project root)
44
- -f, --format <fmt> Format: cursor, windsurf, all (default: all)
45
- -h, --help Show this help
49
+ console.log(`
50
+ ${ansi.bold}USAGE${ansi.reset}
51
+ ${ansi.cyan}vibecheck context${ansi.reset} [options]
52
+
53
+ ${ansi.dim}Aliases: rules, ai-rules, mdc${ansi.reset}
54
+
55
+ Generate project-aware AI coding rules for your IDE. These rules help
56
+ AI assistants understand your codebase conventions, preventing common
57
+ mistakes and enforcing patterns.
58
+
59
+ ${ansi.bold}FORMATS${ansi.reset}
60
+ ${ansi.cyan}cursor${ansi.reset} .cursorrules file for Cursor IDE
61
+ ${ansi.cyan}mdc${ansi.reset} MDC format (.cursor/rules/*.mdc)
62
+ ${ansi.cyan}windsurf${ansi.reset} Windsurf rules directory
63
+ ${ansi.cyan}copilot${ansi.reset} GitHub Copilot instructions
64
+ ${ansi.cyan}all${ansi.reset} Generate all formats (default)
65
+
66
+ ${ansi.bold}OPTIONS${ansi.reset}
67
+ ${ansi.cyan}-f, --format <fmt>${ansi.reset} Format to generate (default: all)
68
+ ${ansi.cyan}-o, --output <dir>${ansi.reset} Output directory (default: project root)
69
+ ${ansi.cyan}--json${ansi.reset} Output result as JSON
70
+ ${ansi.cyan}--quiet, -q${ansi.reset} Suppress non-essential output
71
+ ${ansi.cyan}--help, -h${ansi.reset} Show this help
72
+
73
+ ${ansi.bold}EXAMPLES${ansi.reset}
74
+ ${ansi.dim}# Generate all rule files${ansi.reset}
75
+ vibecheck context
76
+
77
+ ${ansi.dim}# Generate only Cursor rules${ansi.reset}
78
+ vibecheck context --format cursor
79
+
80
+ ${ansi.dim}# Custom output directory${ansi.reset}
81
+ vibecheck context --output ./ai-rules
82
+
83
+ ${ansi.bold}OUTPUT FILES${ansi.reset}
84
+ .cursorrules Cursor IDE rules
85
+ .cursor/rules/*.mdc MDC format rules
86
+ .windsurf/rules/*.md Windsurf rules
87
+ .github/copilot-instructions.md
88
+
89
+ ${ansi.dim}────────────────────────────────────────────────────────────────────${ansi.reset}
90
+ ${ansi.dim}Documentation: https://docs.vibecheckai.dev/cli/context${ansi.reset}
46
91
  `);
47
- return 0;
92
+ return EXIT.SUCCESS;
93
+ }
94
+
95
+ // Validate project path exists
96
+ if (!fs.existsSync(root)) {
97
+ if (json) {
98
+ console.log(JSON.stringify({ success: false, error: `Project path does not exist: ${root}` }));
99
+ } else {
100
+ renderError(`Project path does not exist: ${root}`);
101
+ console.log(` ${ansi.dim}Verify the path and try again.${ansi.reset}`);
102
+ }
103
+ return EXIT.NOT_FOUND;
104
+ }
105
+
106
+ // Validate format option
107
+ const validFormats = ["all", "cursor", "mdc", "windsurf", "copilot"];
108
+ if (!validFormats.includes(opts.format)) {
109
+ if (json) {
110
+ console.log(JSON.stringify({ success: false, error: `Unknown format: ${opts.format}`, validFormats }));
111
+ } else {
112
+ renderError(`Unknown format: ${opts.format}`);
113
+ console.log(` ${ansi.dim}Valid formats: ${validFormats.join(", ")}${ansi.reset}`);
114
+ }
115
+ return EXIT.USER_ERROR;
48
116
  }
49
117
 
50
118
  try {
51
- await contextRunner({
119
+ if (!quiet && !json) {
120
+ renderMinimalHeader("context", "starter");
121
+ renderSectionHeader("Generating AI Rules", sym.gear);
122
+ }
123
+
124
+ const spinner = !quiet && !json ? new Spinner(`Analyzing project and generating ${opts.format} rules`).start() : null;
125
+
126
+ const result = await contextRunner({
52
127
  repoRoot: root,
53
128
  output: opts.output,
54
129
  format: opts.format
55
130
  });
56
- return 0;
131
+
132
+ const duration = Date.now() - startTime;
133
+
134
+ if (json) {
135
+ console.log(JSON.stringify({
136
+ success: true,
137
+ format: opts.format,
138
+ files: result?.files || [],
139
+ path: root,
140
+ duration,
141
+ }));
142
+ } else if (!quiet) {
143
+ spinner?.succeed("AI rules generated successfully");
144
+
145
+ // Show generated files
146
+ if (result?.files && result.files.length > 0) {
147
+ console.log();
148
+ console.log(` ${ansi.bold}Generated files:${ansi.reset}`);
149
+ result.files.forEach(f => {
150
+ renderBullet(`${ansi.cyan}${f}${ansi.reset}`, 4);
151
+ });
152
+ }
153
+
154
+ renderFooter({
155
+ nextSteps: [
156
+ { cmd: "vibecheck scan", desc: "analyze code quality" },
157
+ { cmd: "vibecheck guard", desc: "validate AI outputs" },
158
+ ],
159
+ docsUrl: "https://docs.vibecheckai.dev/cli/context",
160
+ });
161
+ }
162
+
163
+ return EXIT.SUCCESS;
57
164
  } catch (error) {
58
- console.error("Error generating context:", error.message);
59
- return 1;
165
+ if (json) {
166
+ console.log(JSON.stringify({ success: false, error: error.message }));
167
+ } else {
168
+ renderError(`Failed to generate context: ${error.message}`);
169
+ console.log(` ${ansi.dim}Run "vibecheck doctor" to check your setup.${ansi.reset}`);
170
+ }
171
+ return EXIT.INTERNAL_ERROR;
60
172
  }
61
173
  }
62
174
 
@@ -18,7 +18,10 @@ const {
18
18
  verdictToExitCode,
19
19
  saveArtifact
20
20
  } = require("./lib/cli-output");
21
- const { parseGlobalFlags, shouldShowBanner } = require("./lib/global-flags");
21
+ const { parseGlobalFlags, shouldShowBanner, shouldSuppressOutput, isJsonMode } = require("./lib/global-flags");
22
+ const { EXIT } = require("./lib/exit-codes");
23
+ const { formatWorkflowUpsell } = require("./lib/upsell");
24
+ const { getApiKey } = require("./lib/auth");
22
25
 
23
26
  // ═══════════════════════════════════════════════════════════════════════════════
24
27
  // ADVANCED TERMINAL - ANSI CODES & UTILITIES
@@ -298,86 +301,110 @@ async function runDoctor(args, context = {}) {
298
301
  const startTime = context.startTime || new Date().toISOString();
299
302
 
300
303
  const opts = parseArgs(args);
304
+ const quiet = shouldSuppressOutput(opts);
305
+ const json = isJsonMode(opts);
301
306
  const executionStart = Date.now();
302
307
 
303
308
  if (opts.help) {
304
309
  printHelp(shouldShowBanner(opts));
305
- return 0;
310
+ return EXIT.SUCCESS;
306
311
  }
307
312
 
308
313
  const projectPath = path.resolve(opts.path || process.cwd());
309
- const projectName = path.basename(projectPath);
310
314
 
311
- // Print banner conditionally
312
- if (shouldShowBanner(opts)) {
313
- printBanner();
314
- console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
315
- console.log(` ${c.dim}Path:${c.reset} ${projectPath}`);
316
- console.log();
315
+ // Validate project path exists
316
+ if (!fs.existsSync(projectPath)) {
317
+ if (json) {
318
+ console.log(JSON.stringify({ success: false, error: `Project path does not exist: ${projectPath}` }));
319
+ } else {
320
+ console.error(` ❌ Project path does not exist: ${projectPath}`);
321
+ }
322
+ return EXIT.NOT_FOUND;
317
323
  }
324
+ const projectName = path.basename(projectPath);
318
325
 
319
- // Use new DoctorService if available
320
- if (DoctorService.name === 'DoctorService') {
321
- const doctor = new DoctorService(projectPath, {
322
- json: opts.json,
323
- fix: opts.fix,
324
- fixDryRun: opts.dryRun,
325
- quiet: opts.quiet,
326
- verbose: opts.verbose,
327
- categories: opts.categories,
328
- skipNetwork: opts.skipNetwork,
329
- saveReport: opts.saveReport,
330
- failOnWarn: opts.failOnWarn,
331
- runId,
332
- });
333
-
334
- const results = await doctor.run();
326
+ try {
327
+ // Print banner conditionally
328
+ if (shouldShowBanner(opts) && !quiet) {
329
+ printBanner();
330
+ console.log(` ${c.dim}Project:${c.reset} ${c.bold}${projectName}${c.reset}`);
331
+ console.log(` ${c.dim}Path:${c.reset} ${projectPath}`);
332
+ console.log();
333
+ }
335
334
 
336
- // Apply CLI output conventions
337
- if (opts.json) {
338
- const output = createJsonOutput({
335
+ // Use new DoctorService if available
336
+ if (DoctorService.name === 'DoctorService') {
337
+ const doctor = new DoctorService(projectPath, {
338
+ json: opts.json,
339
+ fix: opts.fix,
340
+ fixDryRun: opts.dryRun,
341
+ quiet: opts.quiet || quiet,
342
+ verbose: opts.verbose,
343
+ categories: opts.categories,
344
+ skipNetwork: opts.skipNetwork,
345
+ saveReport: opts.saveReport,
346
+ failOnWarn: opts.failOnWarn,
339
347
  runId,
340
- command: "doctor",
341
- startTime,
342
- exitCode: results.exitCode || 0,
343
- verdict: exitCodeToVerdict(results.exitCode || 0),
344
- result: {
345
- health: results.health || "unknown",
346
- checks: results.checks || [],
347
- fixes: results.fixes || [],
348
- summary: {
349
- total: results.totalChecks || 0,
350
- passed: results.passedChecks || 0,
351
- warnings: results.warnings || 0,
352
- errors: results.errors || 0,
353
- }
354
- },
355
- tier: "free",
356
- version: require("../../package.json").version,
357
- artifacts: results.reportPath ? [{
358
- type: "report",
359
- path: results.reportPath,
360
- description: "Doctor report"
361
- }] : []
362
348
  });
363
349
 
364
- writeJsonOutput(output, opts.output);
350
+ const results = await doctor.run();
351
+
352
+ // Apply CLI output conventions
353
+ if (opts.json || json) {
354
+ const output = createJsonOutput({
355
+ runId,
356
+ command: "doctor",
357
+ startTime,
358
+ exitCode: results.exitCode || EXIT.SUCCESS,
359
+ verdict: exitCodeToVerdict(results.exitCode || EXIT.SUCCESS),
360
+ result: {
361
+ health: results.health || "unknown",
362
+ checks: results.checks || [],
363
+ fixes: results.fixes || [],
364
+ summary: {
365
+ total: results.totalChecks || 0,
366
+ passed: results.passedChecks || 0,
367
+ warnings: results.warnings || 0,
368
+ errors: results.errors || 0,
369
+ }
370
+ },
371
+ tier: "free",
372
+ version: require("../../package.json").version,
373
+ artifacts: results.reportPath ? [{
374
+ type: "report",
375
+ path: results.reportPath,
376
+ description: "Doctor report"
377
+ }] : []
378
+ });
379
+
380
+ writeJsonOutput(output, opts.output);
381
+ }
382
+
383
+ // Save artifacts
384
+ if (results.checks) {
385
+ saveArtifact(runId, "checks", results.checks);
386
+ }
387
+ if (results.fixes) {
388
+ saveArtifact(runId, "fixes", results.fixes);
389
+ }
390
+
391
+ // Map results to proper exit codes
392
+ if (results.errors > 0) return EXIT.BLOCKING;
393
+ if (results.warnings > 0) return EXIT.WARNINGS;
394
+ return EXIT.SUCCESS;
365
395
  }
366
396
 
367
- // Save artifacts
368
- if (results.checks) {
369
- saveArtifact(runId, "checks", results.checks);
370
- }
371
- if (results.fixes) {
372
- saveArtifact(runId, "fixes", results.fixes);
397
+ // Legacy fallback
398
+ const doctor = new DoctorService(projectPath, opts);
399
+ return await doctor.diagnose();
400
+ } catch (error) {
401
+ if (json) {
402
+ console.log(JSON.stringify({ success: false, error: error.message }));
403
+ } else {
404
+ console.error(` ❌ Doctor check failed: ${error.message}`);
373
405
  }
374
-
375
- return results.exitCode || 0;
406
+ return EXIT.INTERNAL_ERROR;
376
407
  }
377
-
378
- // Legacy fallback
379
- const doctor = new DoctorService(projectPath, opts);
380
- return await doctor.diagnose();
381
408
  }
382
409
 
383
410
  function parseArgs(args) {
@@ -573,20 +600,33 @@ function runDoctorLegacy() {
573
600
  }
574
601
 
575
602
  console.log("");
603
+
604
+ // Get tier for upsell
605
+ const { key } = getApiKey();
606
+ const currentTier = key ? "starter" : "free";
607
+
576
608
  if (hasIssues) {
577
609
  console.log(" ❌ Issues found. Fix them and run doctor again.");
578
610
  console.log();
579
611
  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}`);
612
+ if (currentTier === "free") {
613
+ console.log(` ${c.dim}Requires STARTER plan • vibecheckai.dev${c.reset}`);
614
+ }
581
615
  console.log();
582
- return 2;
616
+ return EXIT.BLOCKING;
583
617
  } else {
584
618
  console.log(" ✅ Environment healthy!");
585
619
  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}`);
620
+
621
+ // Workflow upsell
622
+ const workflow = formatWorkflowUpsell("doctor", currentTier);
623
+ if (workflow) {
624
+ console.log(` ${workflow}`);
625
+ } else {
626
+ 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}`);
627
+ }
588
628
  console.log();
589
- return 0;
629
+ return EXIT.SUCCESS;
590
630
  }
591
631
  }
592
632
 
@@ -33,6 +33,7 @@ const { backupFiles, restoreBackup } = require('./lib/backup');
33
33
  const { validatePatchResponse, parseDiffTouchedFiles } = require('./lib/validate-patch');
34
34
  const { buildSharePack } = require('./lib/share-pack');
35
35
  const { parseGlobalFlags, shouldShowBanner } = require('./lib/global-flags');
36
+ const { EXIT, verdictToExitCode } = require('./lib/exit-codes');
36
37
 
37
38
  // Entitlements enforcement
38
39
  const entitlements = require('./lib/entitlements-v2');
@@ -696,7 +697,7 @@ async function runFix(args) {
696
697
  } catch (e) {
697
698
  stopSpinner('LLM failed', false);
698
699
  console.log(` ${colors.blockRed}${ICONS.cross}${c.reset} ${e.message}`);
699
- return 1;
700
+ return EXIT.INTERNAL_ERROR;
700
701
  }
701
702
 
702
703
  const respPath = path.join(outDir, `step_${String(step).padStart(2,"0")}_${mission.id}_response.json`);
@@ -722,7 +723,7 @@ async function runFix(args) {
722
723
  console.log(` ${colors.warnAmber}${ICONS.warning}${c.reset} ${w}`);
723
724
  }
724
725
  }
725
- return 1;
726
+ return EXIT.BLOCKING;
726
727
  }
727
728
 
728
729
  if (v.warnings.length) {
@@ -753,7 +754,7 @@ async function runFix(args) {
753
754
  console.log(` ${colors.blockRed}${ICONS.cross}${c.reset} Patch apply failed: ${res.error}`);
754
755
  restoreBackup(root, backupRoot);
755
756
  console.log(` ${colors.rollback}${ICONS.rollback}${c.reset} Restored from backup`);
756
- return 1;
757
+ return EXIT.INTERNAL_ERROR;
757
758
  }
758
759
  console.log(` ${colors.patch}${ICONS.patch}${c.reset} Applied: ${c.dim}${ed.path}${c.reset}`);
759
760
  }
@@ -789,7 +790,7 @@ async function runFix(args) {
789
790
  console.log();
790
791
  console.log(` ${colors.blockRed}${ICONS.stop}${c.reset} Stopping: stagnation limit reached (${stagnant}/${stagnationLimit})`);
791
792
  console.log();
792
- return 1;
793
+ return EXIT.BLOCKING;
793
794
  }
794
795
  continue;
795
796
  }
@@ -820,7 +821,7 @@ async function runFix(args) {
820
821
  }
821
822
  }
822
823
 
823
- return 1;
824
+ return EXIT.WARNINGS; // Max steps reached, incomplete
824
825
  }
825
826
 
826
827
  // ═══════════════════════════════════════════════════════════════════════════════