@vibecheckai/cli 3.7.0 → 3.8.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 (99) hide show
  1. package/README.md +135 -63
  2. package/bin/_deprecations.js +447 -19
  3. package/bin/_router.js +1 -1
  4. package/bin/registry.js +347 -280
  5. package/bin/runners/context/generators/cursor-enhanced.js +2439 -0
  6. package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
  7. package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
  8. package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
  9. package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
  10. package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
  11. package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
  12. package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
  13. package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
  14. package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
  15. package/bin/runners/lib/agent-firewall/index.js +200 -0
  16. package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
  17. package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
  18. package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +622 -0
  19. package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
  20. package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
  21. package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
  22. package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
  23. package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
  24. package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
  25. package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
  26. package/bin/runners/lib/agent-firewall/session/index.js +26 -0
  27. package/bin/runners/lib/artifact-envelope.js +540 -0
  28. package/bin/runners/lib/auth-shared.js +977 -0
  29. package/bin/runners/lib/checkpoint.js +941 -0
  30. package/bin/runners/lib/cleanup/engine.js +571 -0
  31. package/bin/runners/lib/cleanup/index.js +53 -0
  32. package/bin/runners/lib/cleanup/output.js +375 -0
  33. package/bin/runners/lib/cleanup/rules.js +1060 -0
  34. package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
  35. package/bin/runners/lib/doctor/failure-signatures.js +526 -0
  36. package/bin/runners/lib/doctor/fix-script.js +336 -0
  37. package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
  38. package/bin/runners/lib/doctor/modules/index.js +62 -3
  39. package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
  40. package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
  41. package/bin/runners/lib/doctor/safe-repair.js +384 -0
  42. package/bin/runners/lib/engines/attack-detector.js +1192 -0
  43. package/bin/runners/lib/entitlements-v2.js +2 -2
  44. package/bin/runners/lib/missions/briefing.js +427 -0
  45. package/bin/runners/lib/missions/checkpoint.js +753 -0
  46. package/bin/runners/lib/missions/hardening.js +851 -0
  47. package/bin/runners/lib/missions/plan.js +421 -32
  48. package/bin/runners/lib/missions/safety-gates.js +645 -0
  49. package/bin/runners/lib/missions/schema.js +478 -0
  50. package/bin/runners/lib/packs/bundle.js +675 -0
  51. package/bin/runners/lib/packs/evidence-pack.js +671 -0
  52. package/bin/runners/lib/packs/pack-factory.js +837 -0
  53. package/bin/runners/lib/packs/permissions-pack.js +686 -0
  54. package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
  55. package/bin/runners/lib/safelist/index.js +96 -0
  56. package/bin/runners/lib/safelist/integration.js +334 -0
  57. package/bin/runners/lib/safelist/matcher.js +696 -0
  58. package/bin/runners/lib/safelist/schema.js +948 -0
  59. package/bin/runners/lib/safelist/store.js +438 -0
  60. package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
  61. package/bin/runners/lib/ship-gate.js +832 -0
  62. package/bin/runners/lib/ship-manifest.js +1153 -0
  63. package/bin/runners/lib/ship-output.js +1 -1
  64. package/bin/runners/lib/unified-cli-output.js +710 -383
  65. package/bin/runners/lib/upsell.js +3 -3
  66. package/bin/runners/lib/why-tree.js +650 -0
  67. package/bin/runners/runAllowlist.js +33 -4
  68. package/bin/runners/runApprove.js +240 -1122
  69. package/bin/runners/runAudit.js +692 -0
  70. package/bin/runners/runAuth.js +325 -29
  71. package/bin/runners/runCheckpoint.js +442 -494
  72. package/bin/runners/runCleanup.js +343 -0
  73. package/bin/runners/runDoctor.js +269 -19
  74. package/bin/runners/runFix.js +411 -32
  75. package/bin/runners/runForge.js +411 -0
  76. package/bin/runners/runIntent.js +906 -0
  77. package/bin/runners/runKickoff.js +878 -0
  78. package/bin/runners/runLaunch.js +2000 -0
  79. package/bin/runners/runLink.js +785 -0
  80. package/bin/runners/runMcp.js +1741 -837
  81. package/bin/runners/runPacks.js +2089 -0
  82. package/bin/runners/runPolish.js +41 -0
  83. package/bin/runners/runSafelist.js +1190 -0
  84. package/bin/runners/runScan.js +21 -9
  85. package/bin/runners/runShield.js +1282 -0
  86. package/bin/runners/runShip.js +395 -16
  87. package/bin/vibecheck.js +34 -6
  88. package/mcp-server/README.md +117 -158
  89. package/mcp-server/handlers/tool-handler.ts +3 -3
  90. package/mcp-server/index.js +16 -0
  91. package/mcp-server/intent-firewall-interceptor.js +529 -0
  92. package/mcp-server/manifest.json +473 -0
  93. package/mcp-server/package.json +1 -1
  94. package/mcp-server/registry/tool-registry.js +315 -523
  95. package/mcp-server/registry/tools.json +442 -428
  96. package/mcp-server/tier-auth.js +68 -11
  97. package/mcp-server/tools-v3.js +70 -16
  98. package/package.json +1 -1
  99. package/bin/runners/runProof.zip +0 -0
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Simple 2-tier model:
5
5
  * - FREE ($0): Inspect & Observe
6
- * - PRO ($69/mo): Fix, Prove & Enforce
6
+ * - PRO ($49/mo): Fix, Prove & Enforce
7
7
  */
8
8
 
9
9
  "use strict";
@@ -199,7 +199,7 @@ ${c.bold}This feature requires Pro.${c.reset}
199
199
 
200
200
  ${c.yellow}${feature}${c.reset} is a Pro feature.
201
201
 
202
- Upgrade to Pro ($69/mo) to unlock Fix, Prove & Enforce capabilities.
202
+ Upgrade to Pro ($49/mo) to unlock Fix, Prove & Enforce capabilities.
203
203
 
204
204
  vibecheck upgrade
205
205
  https://vibecheckai.dev/pricing
@@ -0,0 +1,427 @@
1
+ // bin/runners/lib/missions/briefing.js
2
+ // ═══════════════════════════════════════════════════════════════════════════════
3
+ // MISSION BRIEFING - Beautiful plan-only mode output
4
+ // Shows what would happen without executing, building trust and transparency
5
+ // ═══════════════════════════════════════════════════════════════════════════════
6
+
7
+ const { RISK_LEVEL, BLAST_RADIUS } = require('./schema');
8
+ const { runPreFlightGates, formatGateResults } = require('./safety-gates');
9
+
10
+ // ═══════════════════════════════════════════════════════════════════════════════
11
+ // TERMINAL STYLING
12
+ // ═══════════════════════════════════════════════════════════════════════════════
13
+
14
+ const c = {
15
+ reset: '\x1b[0m',
16
+ bold: '\x1b[1m',
17
+ dim: '\x1b[2m',
18
+ italic: '\x1b[3m',
19
+ underline: '\x1b[4m',
20
+ // Colors
21
+ red: '\x1b[31m',
22
+ green: '\x1b[32m',
23
+ yellow: '\x1b[33m',
24
+ blue: '\x1b[34m',
25
+ magenta: '\x1b[35m',
26
+ cyan: '\x1b[36m',
27
+ white: '\x1b[37m',
28
+ gray: '\x1b[90m',
29
+ };
30
+
31
+ // RGB helper
32
+ const rgb = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
33
+
34
+ // Premium color palette
35
+ const colors = {
36
+ accent: rgb(255, 150, 50),
37
+ muted: rgb(120, 120, 140),
38
+ success: rgb(0, 255, 150),
39
+ warning: rgb(255, 200, 0),
40
+ danger: rgb(255, 80, 80),
41
+ info: rgb(100, 200, 255),
42
+
43
+ // Risk colors
44
+ low: rgb(100, 200, 100),
45
+ medium: rgb(255, 200, 100),
46
+ high: rgb(255, 150, 100),
47
+ critical: rgb(255, 80, 80),
48
+ };
49
+
50
+ const ICONS = {
51
+ mission: '🎯',
52
+ file: '📄',
53
+ check: '✓',
54
+ cross: '✗',
55
+ warning: '⚠',
56
+ info: 'ℹ',
57
+ arrow: '→',
58
+ bullet: '•',
59
+ shield: '🛡️',
60
+ checkpoint: '💾',
61
+ rollback: '↩️',
62
+ lock: '🔒',
63
+ unlock: '🔓',
64
+ rocket: '🚀',
65
+ brain: '🧠',
66
+ gear: '⚙️',
67
+ };
68
+
69
+ const BOX = {
70
+ topLeft: '╭',
71
+ topRight: '╮',
72
+ bottomLeft: '╰',
73
+ bottomRight: '╯',
74
+ horizontal: '─',
75
+ vertical: '│',
76
+ teeRight: '├',
77
+ teeLeft: '┤',
78
+ };
79
+
80
+ // ═══════════════════════════════════════════════════════════════════════════════
81
+ // FORMATTING UTILITIES
82
+ // ═══════════════════════════════════════════════════════════════════════════════
83
+
84
+ /**
85
+ * Get color for risk level
86
+ */
87
+ function getRiskColor(riskLevel) {
88
+ switch (riskLevel) {
89
+ case RISK_LEVEL.LOW: return colors.low;
90
+ case RISK_LEVEL.MEDIUM: return colors.medium;
91
+ case RISK_LEVEL.HIGH: return colors.high;
92
+ case RISK_LEVEL.CRITICAL: return colors.critical;
93
+ default: return colors.muted;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Get icon for mission type
99
+ */
100
+ function getMissionIcon(type) {
101
+ const icons = {
102
+ 'REMOVE_OWNER_MODE': '🔐',
103
+ 'FIX_STRIPE_WEBHOOKS': '💰',
104
+ 'ENFORCE_PAID_SURFACE': '🛡️',
105
+ 'ADD_SERVER_AUTH': '🔒',
106
+ 'FIX_MISSING_ROUTE': '🛤️',
107
+ 'FIX_FAKE_SUCCESS': '👻',
108
+ 'FIX_ENV_CONTRACT': '🌍',
109
+ 'FIX_DEAD_UI': '💀',
110
+ 'FIX_EMPTY_CATCH': '🐛',
111
+ 'FIX_TEST_KEYS': '🔑',
112
+ 'FIX_MOCK_DOMAINS': '🔗',
113
+ 'FIX_PLACEHOLDER_DATA': '📝',
114
+ 'FIX_HARDCODED_SECRETS': '🔐',
115
+ 'FIX_SIMULATED_BILLING': '💳',
116
+ 'FIX_SILENT_FALLBACK': '🔇',
117
+ 'SYNC_CONTRACTS': '📊',
118
+ 'FIX_ROUTE_DRIFT': '🛤️',
119
+ 'FIX_AUTH_DRIFT': '🛡️',
120
+ };
121
+ return icons[type] || ICONS.mission;
122
+ }
123
+
124
+ /**
125
+ * Truncate string to max length
126
+ */
127
+ function truncate(str, max) {
128
+ if (!str) return '';
129
+ if (str.length <= max) return str;
130
+ return str.slice(0, max - 3) + '...';
131
+ }
132
+
133
+ /**
134
+ * Create a horizontal line
135
+ */
136
+ function line(width = 60, char = '─') {
137
+ return c.dim + char.repeat(width) + c.reset;
138
+ }
139
+
140
+ // ═══════════════════════════════════════════════════════════════════════════════
141
+ // MISSION BRIEFING FORMATTERS
142
+ // ═══════════════════════════════════════════════════════════════════════════════
143
+
144
+ /**
145
+ * Format a single mission briefing
146
+ * @param {object} mission - Mission object
147
+ * @param {object} gateResults - Pre-flight gate results
148
+ * @param {number} index - Mission index (1-based)
149
+ * @param {number} total - Total missions
150
+ * @returns {string} Formatted briefing
151
+ */
152
+ function formatMissionBriefing(mission, gateResults, index, total) {
153
+ const lines = [];
154
+ const w = 60;
155
+ const icon = getMissionIcon(mission.type);
156
+ const riskColor = getRiskColor(mission.safety?.riskLevel);
157
+
158
+ // Header box
159
+ lines.push('');
160
+ lines.push(` ${c.dim}${BOX.topLeft}${BOX.horizontal.repeat(w)}${BOX.topRight}${c.reset}`);
161
+
162
+ // Mission type header
163
+ const header = `${icon} Mission ${index}/${total}: ${mission.type}`;
164
+ const headerPad = Math.max(0, w - header.length - 2);
165
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${colors.accent}${c.bold}${header}${c.reset}${' '.repeat(headerPad)} ${c.dim}${BOX.vertical}${c.reset}`);
166
+
167
+ // Separator
168
+ lines.push(` ${c.dim}${BOX.teeRight}${BOX.horizontal.repeat(w)}${BOX.teeLeft}${c.reset}`);
169
+
170
+ // Objective section
171
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.bold}Objective${c.reset}`);
172
+ const intent = truncate(mission.template?.intent || mission.objective?.intent || 'Fix issues', w - 4);
173
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${intent}`);
174
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset}`);
175
+
176
+ // Files section
177
+ const files = mission.scope?.allowedFiles || [];
178
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.bold}Files${c.reset} ${c.dim}(${files.length} total)${c.reset}`);
179
+ for (const file of files.slice(0, 4)) {
180
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${ICONS.file} ${truncate(file, w - 8)}`);
181
+ }
182
+ if (files.length > 4) {
183
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.dim}... and ${files.length - 4} more${c.reset}`);
184
+ }
185
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset}`);
186
+
187
+ // Risk section
188
+ const riskLevel = (mission.safety?.riskLevel || 'medium').toUpperCase();
189
+ const blastRadius = mission.scope?.blastRadius || 'medium';
190
+ const confidence = Math.round((mission.safety?.confidence || 0.5) * 100);
191
+
192
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.bold}Risk Assessment${c.reset}`);
193
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${riskColor}${ICONS.shield} Risk: ${riskLevel}${c.reset}`);
194
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${ICONS.bullet} Blast radius: ${blastRadius} (${files.length} files)`);
195
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${ICONS.bullet} Confidence: ${confidence}%`);
196
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${mission.safety?.reversible !== false ? colors.success + ICONS.check : colors.danger + ICONS.cross}${c.reset} Reversible: ${mission.safety?.reversible !== false ? 'Yes' : 'No'}`);
197
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset}`);
198
+
199
+ // Safety gates section
200
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.bold}Safety Gates${c.reset}`);
201
+ for (const result of gateResults.results) {
202
+ const gateIcon = result.pass
203
+ ? (result.severity === 'warning' ? colors.warning + ICONS.warning : colors.success + ICONS.check)
204
+ : colors.danger + ICONS.cross;
205
+ const gateName = result.gate.replace(/_/g, ' ');
206
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${gateIcon}${c.reset} ${gateName}`);
207
+ }
208
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset}`);
209
+
210
+ // Success criteria section
211
+ const criteria = mission.template?.success || mission.objective?.successCriteria || [];
212
+ if (criteria.length > 0) {
213
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.bold}Success Criteria${c.reset}`);
214
+ for (const criterion of criteria.slice(0, 3)) {
215
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${ICONS.bullet} ${truncate(criterion, w - 8)}`);
216
+ }
217
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset}`);
218
+ }
219
+
220
+ // Actions section
221
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.bold}Actions (on --apply)${c.reset}`);
222
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.dim}1.${c.reset} Create checkpoint ${ICONS.checkpoint}`);
223
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.dim}2.${c.reset} Generate LLM prompt with Reality Firewall ${ICONS.brain}`);
224
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.dim}3.${c.reset} Validate and apply patch ${ICONS.gear}`);
225
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.dim}4.${c.reset} Run ship to verify ${ICONS.rocket}`);
226
+ lines.push(` ${c.dim}${BOX.vertical}${c.reset} ${c.dim}5.${c.reset} Auto-rollback if gates fail ${ICONS.rollback}`);
227
+
228
+ // Footer box
229
+ lines.push(` ${c.dim}${BOX.bottomLeft}${BOX.horizontal.repeat(w)}${BOX.bottomRight}${c.reset}`);
230
+
231
+ // Execute hint
232
+ if (gateResults.ok) {
233
+ lines.push(` ${colors.success}${ICONS.check}${c.reset} Ready to execute: ${colors.accent}vibecheck fix --apply --mission ${mission.id}${c.reset}`);
234
+ } else {
235
+ lines.push(` ${colors.danger}${ICONS.cross}${c.reset} Cannot execute: ${gateResults.summary}`);
236
+ // Show remedies
237
+ for (const result of gateResults.results.filter(r => !r.pass && r.remedy)) {
238
+ lines.push(` ${c.dim}${ICONS.arrow} ${result.remedy}${c.reset}`);
239
+ }
240
+ }
241
+
242
+ return lines.join('\n');
243
+ }
244
+
245
+ /**
246
+ * Format all mission briefings
247
+ * @param {string} repoRoot - Repository root
248
+ * @param {object[]} missions - Array of missions
249
+ * @param {object} options - Formatting options
250
+ * @returns {string} Formatted output
251
+ */
252
+ function formatAllBriefings(repoRoot, missions, options = {}) {
253
+ const lines = [];
254
+
255
+ // Header
256
+ lines.push('');
257
+ lines.push(` ${colors.accent}${c.bold}MISSION BRIEFINGS${c.reset}`);
258
+ lines.push(` ${c.dim}Plan-only mode: No changes will be made${c.reset}`);
259
+ lines.push(' ' + line(60));
260
+
261
+ // Summary stats
262
+ const stats = {
263
+ total: missions.length,
264
+ findings: missions.reduce((sum, m) => sum + (m.objective?.findingCount || 1), 0),
265
+ files: new Set(missions.flatMap(m => m.scope?.allowedFiles || [])).size,
266
+ };
267
+
268
+ lines.push('');
269
+ lines.push(` ${ICONS.mission} ${c.bold}${stats.total}${c.reset} missions targeting ${c.bold}${stats.findings}${c.reset} findings in ${c.bold}${stats.files}${c.reset} files`);
270
+ lines.push('');
271
+
272
+ // Individual briefings
273
+ let readyCount = 0;
274
+ let blockedCount = 0;
275
+
276
+ for (let i = 0; i < missions.length; i++) {
277
+ const mission = missions[i];
278
+ const gateResults = runPreFlightGates(repoRoot, mission, options);
279
+
280
+ if (gateResults.ok) readyCount++;
281
+ else blockedCount++;
282
+
283
+ lines.push(formatMissionBriefing(mission, gateResults, i + 1, missions.length));
284
+ }
285
+
286
+ // Summary footer
287
+ lines.push('');
288
+ lines.push(' ' + line(60, '═'));
289
+ lines.push(` ${c.bold}SUMMARY${c.reset}`);
290
+ lines.push(` ${colors.success}${ICONS.check}${c.reset} Ready: ${readyCount}/${missions.length} missions`);
291
+ if (blockedCount > 0) {
292
+ lines.push(` ${colors.danger}${ICONS.cross}${c.reset} Blocked: ${blockedCount}/${missions.length} missions`);
293
+ }
294
+ lines.push('');
295
+
296
+ // Next steps
297
+ lines.push(` ${c.bold}Next Steps:${c.reset}`);
298
+ if (readyCount > 0) {
299
+ lines.push(` ${colors.accent}vibecheck fix --apply${c.reset} ${c.dim}Execute all ready missions${c.reset}`);
300
+ lines.push(` ${colors.accent}vibecheck fix --apply --mission M_xxx${c.reset} ${c.dim}Execute specific mission${c.reset}`);
301
+ }
302
+ if (blockedCount > 0) {
303
+ lines.push(` ${colors.accent}vibecheck fix --apply --force${c.reset} ${c.dim}Override safety gates${c.reset}`);
304
+ }
305
+ lines.push('');
306
+
307
+ return lines.join('\n');
308
+ }
309
+
310
+ /**
311
+ * Format a compact mission summary (for non-plan-only mode)
312
+ * @param {object[]} missions - Array of missions
313
+ * @returns {string} Formatted summary
314
+ */
315
+ function formatMissionSummary(missions) {
316
+ const lines = [];
317
+
318
+ lines.push('');
319
+ lines.push(` ${colors.accent}${ICONS.mission}${c.reset} ${c.bold}MISSION PLAN${c.reset} ${c.dim}(${missions.length} missions)${c.reset}`);
320
+ lines.push('');
321
+
322
+ for (let i = 0; i < missions.length; i++) {
323
+ const m = missions[i];
324
+ const icon = getMissionIcon(m.type);
325
+ const riskColor = getRiskColor(m.safety?.riskLevel);
326
+ const fileCount = m.scope?.allowedFiles?.length || 0;
327
+ const findingCount = m.objective?.findingCount || 1;
328
+
329
+ lines.push(` ${icon} ${c.bold}${i + 1}.${c.reset} ${m.type.padEnd(24)} ${c.dim}${findingCount} finding(s)${c.reset} ${riskColor}${m.safety?.riskLevel?.toUpperCase() || 'MEDIUM'}${c.reset}`);
330
+ }
331
+
332
+ lines.push('');
333
+
334
+ return lines.join('\n');
335
+ }
336
+
337
+ /**
338
+ * Format mission execution result
339
+ * @param {object} mission - Mission object
340
+ * @param {object} result - Execution result
341
+ * @returns {string} Formatted result
342
+ */
343
+ function formatMissionResult(mission, result) {
344
+ const lines = [];
345
+ const icon = getMissionIcon(mission.type);
346
+
347
+ if (result.success) {
348
+ lines.push(` ${colors.success}${ICONS.check}${c.reset} ${icon} ${mission.type} ${c.bold}completed${c.reset}`);
349
+ if (result.findings) {
350
+ lines.push(` ${c.dim}${ICONS.arrow} ${result.findings.before} → ${result.findings.after} findings${c.reset}`);
351
+ }
352
+ } else if (result.rolledBack) {
353
+ lines.push(` ${colors.warning}${ICONS.rollback}${c.reset} ${icon} ${mission.type} ${c.bold}rolled back${c.reset}`);
354
+ lines.push(` ${c.dim}${ICONS.arrow} ${result.reason || 'Post-flight gates failed'}${c.reset}`);
355
+ } else {
356
+ lines.push(` ${colors.danger}${ICONS.cross}${c.reset} ${icon} ${mission.type} ${c.bold}failed${c.reset}`);
357
+ lines.push(` ${c.dim}${ICONS.arrow} ${result.reason || 'Unknown error'}${c.reset}`);
358
+ }
359
+
360
+ return lines.join('\n');
361
+ }
362
+
363
+ /**
364
+ * Format final summary after all missions
365
+ * @param {object} summary - Execution summary
366
+ * @returns {string} Formatted summary
367
+ */
368
+ function formatFinalSummary(summary) {
369
+ const lines = [];
370
+
371
+ lines.push('');
372
+ lines.push(' ' + line(60, '═'));
373
+ lines.push(` ${c.bold}MISSION CONTROL SUMMARY${c.reset}`);
374
+ lines.push('');
375
+
376
+ // Stats
377
+ lines.push(` ${colors.success}${ICONS.check}${c.reset} Completed: ${summary.completed}/${summary.total}`);
378
+ if (summary.rolledBack > 0) {
379
+ lines.push(` ${colors.warning}${ICONS.rollback}${c.reset} Rolled back: ${summary.rolledBack}`);
380
+ }
381
+ if (summary.failed > 0) {
382
+ lines.push(` ${colors.danger}${ICONS.cross}${c.reset} Failed: ${summary.failed}`);
383
+ }
384
+ if (summary.skipped > 0) {
385
+ lines.push(` ${c.dim}${ICONS.bullet}${c.reset} Skipped: ${summary.skipped}`);
386
+ }
387
+
388
+ // Duration
389
+ if (summary.duration) {
390
+ const seconds = Math.round(summary.duration / 1000);
391
+ lines.push(` ${ICONS.bullet} Duration: ${seconds}s`);
392
+ }
393
+
394
+ // Verdict
395
+ lines.push('');
396
+ if (summary.verdict === 'SHIP') {
397
+ lines.push(` ${colors.success}${ICONS.rocket} SHIP${c.reset} ${c.dim}All issues resolved!${c.reset}`);
398
+ } else if (summary.verdict === 'WARN') {
399
+ lines.push(` ${colors.warning}${ICONS.warning} WARN${c.reset} ${c.dim}Some warnings remain${c.reset}`);
400
+ } else {
401
+ lines.push(` ${colors.danger}${ICONS.cross} BLOCK${c.reset} ${c.dim}Blocking issues remain${c.reset}`);
402
+ }
403
+
404
+ lines.push('');
405
+
406
+ return lines.join('\n');
407
+ }
408
+
409
+ module.exports = {
410
+ // Main formatters
411
+ formatMissionBriefing,
412
+ formatAllBriefings,
413
+ formatMissionSummary,
414
+ formatMissionResult,
415
+ formatFinalSummary,
416
+
417
+ // Utilities
418
+ getMissionIcon,
419
+ getRiskColor,
420
+ truncate,
421
+ line,
422
+
423
+ // Colors and icons for external use
424
+ colors,
425
+ ICONS,
426
+ c,
427
+ };