megabuff 0.6.0 → 0.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.
package/dist/index.js CHANGED
@@ -6,9 +6,24 @@ import OpenAI from "openai";
6
6
  import Anthropic from "@anthropic-ai/sdk";
7
7
  import { GoogleGenerativeAI } from "@google/generative-ai";
8
8
  import * as clipboardy from "clipboardy";
9
- import { getApiKeyInfo, setApiKey, removeApiKey, hasApiKey, getConfig, getProvider, setProvider, setModel, getModel, normalizeProvider, getProviderForModel, MODEL_PROVIDER_MAP, PROVIDERS } from "./config.js";
9
+ import chalk from "chalk";
10
+ import { getApiKeyInfo, setApiKey, removeApiKey, hasApiKey, getConfig, getProvider, setProvider, setModel, getModel, normalizeProvider, getProviderForModel, MODEL_PROVIDER_MAP, PROVIDERS, getThemeName, setThemeName } from "./config.js";
10
11
  import { getDefaultModel } from "./models.js";
12
+ import { themes, getAllThemeNames, isValidTheme } from "./themes.js";
13
+ import { getCurrentTheme, clearThemeCache } from "./theme-utils.js";
11
14
  const program = new Command();
15
+ // Initialize theme at startup
16
+ let theme = await getCurrentTheme();
17
+ // Configure help to use theme colors
18
+ program.configureHelp({
19
+ styleTitle: (str) => theme.colors.highlight(str),
20
+ styleCommandText: (str) => theme.colors.primary(str),
21
+ styleCommandDescription: (str) => theme.colors.secondary(str),
22
+ styleDescriptionText: (str) => theme.colors.secondary(str),
23
+ styleOptionText: (str) => theme.colors.info(str),
24
+ styleArgumentText: (str) => theme.colors.accent(str),
25
+ styleSubcommandText: (str) => theme.colors.primary(str),
26
+ });
12
27
  function isDevMode() {
13
28
  // `npm run dev` sets npm_lifecycle_event=dev
14
29
  if (process.env.npm_lifecycle_event === "dev")
@@ -23,7 +38,7 @@ function debugLog(...args) {
23
38
  if (!isDevMode())
24
39
  return;
25
40
  // stderr to avoid polluting stdout output/pipes
26
- console.error("[megabuff:debug]", ...args);
41
+ console.error(theme.colors.dim("[megabuff:debug]"), ...args);
27
42
  }
28
43
  function maskSecret(secret) {
29
44
  if (!secret)
@@ -74,7 +89,7 @@ async function getInput(inlinePrompt, options) {
74
89
  output: process.stdout,
75
90
  });
76
91
  debugLog("input.source=interactive");
77
- console.log("Enter your prompt (press Ctrl+D when done):");
92
+ console.log(theme.colors.primary("Enter your prompt (press Ctrl+D when done):"));
78
93
  const lines = [];
79
94
  rl.on("line", (line) => {
80
95
  lines.push(line);
@@ -221,8 +236,8 @@ function createSpinner(message) {
221
236
  let timer;
222
237
  let lastLen = 0;
223
238
  const render = (text) => {
224
- const frame = frames[i++ % frames.length];
225
- const line = `${frame} ${text}`;
239
+ const frame = theme.colors.primary(frames[i++ % frames.length]);
240
+ const line = `${frame} ${theme.colors.dim(text)}`;
226
241
  const padded = line + " ".repeat(Math.max(0, lastLen - line.length));
227
242
  lastLen = Math.max(lastLen, line.length);
228
243
  process.stderr.write(`\r${padded}`);
@@ -241,7 +256,7 @@ function createSpinner(message) {
241
256
  clearInterval(timer);
242
257
  timer = undefined;
243
258
  const text = finalText ?? message;
244
- const padded = text + " ".repeat(Math.max(0, lastLen - text.length));
259
+ const padded = theme.colors.success(`✓ ${text}`) + " ".repeat(Math.max(0, lastLen - text.length));
245
260
  process.stderr.write(`\r${padded}\n`);
246
261
  },
247
262
  fail(finalText) {
@@ -251,7 +266,7 @@ function createSpinner(message) {
251
266
  clearInterval(timer);
252
267
  timer = undefined;
253
268
  const text = finalText ?? message;
254
- const padded = text + " ".repeat(Math.max(0, lastLen - text.length));
269
+ const padded = theme.colors.error(`✗ ${text}`) + " ".repeat(Math.max(0, lastLen - text.length));
255
270
  process.stderr.write(`\r${padded}\n`);
256
271
  }
257
272
  };
@@ -270,14 +285,14 @@ function formatProviderLabel(p) {
270
285
  async function promptFirstRunConfig() {
271
286
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
272
287
  try {
273
- console.log("\nNo BYOK token configured yet.");
274
- console.log("Select the provider that will supply the token:\n");
288
+ console.log(theme.colors.warning("\n⚡ No BYOK token configured yet."));
289
+ console.log(theme.colors.primary("Select the provider that will supply the token:\n"));
275
290
  PROVIDERS.forEach((p, idx) => {
276
- console.log(` ${idx + 1}) ${formatProviderLabel(p)}`);
291
+ console.log(theme.colors.secondary(` ${chalk.bold(idx + 1)}) ${formatProviderLabel(p)}`));
277
292
  });
278
293
  let provider;
279
294
  while (!provider) {
280
- const raw = await rl.question("\nProvider (number or name): ");
295
+ const raw = await rl.question(theme.colors.primary("\nProvider (number or name): "));
281
296
  const trimmed = raw.trim();
282
297
  const asNum = Number(trimmed);
283
298
  if (Number.isFinite(asNum) && asNum >= 1 && asNum <= PROVIDERS.length) {
@@ -286,14 +301,14 @@ async function promptFirstRunConfig() {
286
301
  }
287
302
  provider = normalizeProvider(trimmed);
288
303
  if (!provider) {
289
- console.log(`Please choose one of: ${PROVIDERS.join(", ")}`);
304
+ console.log(theme.colors.warning(`Please choose one of: ${PROVIDERS.join(", ")}`));
290
305
  }
291
306
  }
292
- const apiKey = (await rl.question("Enter your BYOK token: ")).trim();
307
+ const apiKey = (await rl.question(theme.colors.primary("Enter your BYOK token: "))).trim();
293
308
  if (!apiKey) {
294
309
  throw new Error("No token provided.");
295
310
  }
296
- const store = (await rl.question("Store in system keychain? (Y/n): ")).trim().toLowerCase();
311
+ const store = (await rl.question(theme.colors.primary("Store in system keychain? (Y/n): "))).trim().toLowerCase();
297
312
  const useKeychain = store === "" || store === "y" || store === "yes";
298
313
  if (!provider) {
299
314
  throw new Error("No provider selected.");
@@ -308,24 +323,40 @@ async function promptFirstRunConfig() {
308
323
  * Output the result based on options
309
324
  */
310
325
  async function outputResult(original, optimized, options) {
326
+ // Calculate statistics
327
+ const originalWords = original.trim().split(/\s+/).length;
328
+ const optimizedWords = optimized.trim().split(/\s+/).length;
329
+ const wordDiff = optimizedWords - originalWords;
330
+ const wordDiffPercent = originalWords > 0 ? ((wordDiff / originalWords) * 100).toFixed(1) : "0";
331
+ const wordDiffSign = wordDiff > 0 ? "+" : "";
311
332
  // Copy to clipboard by default (unless --no-copy is used)
312
333
  if (options.copy !== false) {
313
334
  try {
314
335
  await clipboardy.default.write(optimized);
315
- console.error("✓ Copied to clipboard — press Ctrl+V (or Paste) to use the optimized prompt");
316
336
  console.error("");
317
- console.error("──────────────────────────────────────────────────");
337
+ console.error(theme.colors.primary("╭─────────────────────────────────────────────────╮"));
338
+ console.error(theme.colors.primary("│") + theme.colors.success(" ✓ Copied to clipboard! ") + theme.colors.primary("│"));
339
+ console.error(theme.colors.primary("╰─────────────────────────────────────────────────╯"));
340
+ console.error(theme.colors.dim(" Press ") + theme.colors.accent("Ctrl+V") + theme.colors.dim(" to paste your optimized prompt"));
341
+ console.error("");
342
+ console.error(theme.colors.dim(" 📊 Stats: ") + theme.colors.info(`${originalWords} → ${optimizedWords} words `) +
343
+ theme.colors.dim("(") + (wordDiff > 0 ? theme.colors.warning : theme.colors.success)(`${wordDiffSign}${wordDiffPercent}%`) + theme.colors.dim(")"));
344
+ console.error("");
345
+ console.error(theme.colors.primary("─".repeat(50)));
318
346
  console.error("");
319
347
  }
320
348
  catch (error) {
321
349
  const errMsg = error instanceof Error ? error.message : String(error);
322
- console.error(`⚠ Failed to copy to clipboard: ${errMsg}`);
350
+ console.error(theme.colors.warning(`⚠ Failed to copy to clipboard: ${errMsg}`));
323
351
  }
324
352
  }
325
353
  // If output file specified, write to file
326
354
  if (options.output) {
327
355
  await fs.writeFile(options.output, optimized, "utf-8");
328
- console.error(`✓ Optimized prompt saved to: ${options.output}`);
356
+ console.error("");
357
+ console.error(theme.colors.success(`✓ Optimized prompt saved to: `) + theme.colors.highlight(options.output));
358
+ console.error(theme.colors.dim(` ${optimizedWords} words • ${optimized.length} characters`));
359
+ console.error("");
329
360
  // Still print to stdout for piping
330
361
  if (!options.interactive) {
331
362
  console.log(optimized);
@@ -334,19 +365,62 @@ async function outputResult(original, optimized, options) {
334
365
  }
335
366
  // If interactive mode, show comparison
336
367
  if (options.interactive) {
337
- console.log("\n" + "=".repeat(50));
338
- console.log("ORIGINAL PROMPT:");
339
- console.log("=".repeat(50));
340
- console.log(original);
341
- console.log("\n" + "=".repeat(50));
342
- console.log("OPTIMIZED PROMPT:");
343
- console.log("=".repeat(50));
344
- console.log(optimized);
345
- console.log("=".repeat(50) + "\n");
368
+ const termWidth = process.stdout.columns || 80;
369
+ const boxWidth = Math.min(termWidth - 4, 100);
370
+ console.log("");
371
+ console.log(theme.colors.primary("╭" + "─".repeat(boxWidth - 2) + "╮"));
372
+ console.log(theme.colors.primary("") + theme.colors.highlight(" 📝 ORIGINAL PROMPT ".padEnd(boxWidth - 2)) + theme.colors.primary("│"));
373
+ console.log(theme.colors.primary("╰" + "─".repeat(boxWidth - 2) + "╯"));
374
+ console.log("");
375
+ // Word wrap the original prompt
376
+ const originalLines = wrapText(original, boxWidth - 4);
377
+ originalLines.forEach(line => {
378
+ console.log(theme.colors.dim(" ") + theme.colors.secondary(line));
379
+ });
380
+ console.log("");
381
+ console.log(theme.colors.dim(" 📊 ") + theme.colors.info(`${originalWords} words`) + theme.colors.dim(" • ") + theme.colors.info(`${original.length} chars`));
382
+ console.log("");
383
+ console.log(theme.colors.success("╭" + "─".repeat(boxWidth - 2) + "╮"));
384
+ console.log(theme.colors.success("│") + theme.colors.highlight(" ✨ OPTIMIZED PROMPT ".padEnd(boxWidth - 2)) + theme.colors.success("│"));
385
+ console.log(theme.colors.success("╰" + "─".repeat(boxWidth - 2) + "╯"));
386
+ console.log("");
387
+ // Word wrap the optimized prompt
388
+ const optimizedLines = wrapText(optimized, boxWidth - 4);
389
+ optimizedLines.forEach(line => {
390
+ console.log(theme.colors.dim(" ") + theme.colors.primary(line));
391
+ });
392
+ console.log("");
393
+ console.log(theme.colors.dim(" 📊 ") + theme.colors.info(`${optimizedWords} words`) + theme.colors.dim(" • ") + theme.colors.info(`${optimized.length} chars`) +
394
+ theme.colors.dim(" • ") + (wordDiff > 0 ? theme.colors.warning : theme.colors.success)(`${wordDiffSign}${wordDiff} words (${wordDiffSign}${wordDiffPercent}%)`));
395
+ console.log("");
346
396
  return;
347
397
  }
348
398
  // Default: print to stdout (pipeable)
349
399
  console.log(optimized);
400
+ console.error("");
401
+ console.error(theme.colors.primary("─".repeat(50)));
402
+ console.error("");
403
+ }
404
+ /**
405
+ * Simple word wrap utility
406
+ */
407
+ function wrapText(text, maxWidth) {
408
+ const words = text.split(/\s+/);
409
+ const lines = [];
410
+ let currentLine = "";
411
+ for (const word of words) {
412
+ if (currentLine.length + word.length + 1 <= maxWidth) {
413
+ currentLine += (currentLine ? " " : "") + word;
414
+ }
415
+ else {
416
+ if (currentLine)
417
+ lines.push(currentLine);
418
+ currentLine = word;
419
+ }
420
+ }
421
+ if (currentLine)
422
+ lines.push(currentLine);
423
+ return lines;
350
424
  }
351
425
  // Helper function to get available models by provider
352
426
  function getModelsByProvider(provider) {
@@ -358,24 +432,24 @@ function getModelsByProvider(provider) {
358
432
  async function interactiveConfig() {
359
433
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
360
434
  try {
361
- console.log("\n╭─────────────────────────────────────╮");
362
- console.log("│ MegaBuff Configuration Setup │");
363
- console.log("╰─────────────────────────────────────╯\n");
364
- console.log("What would you like to configure?\n");
365
- console.log(" 1) Set API token for a provider");
366
- console.log(" 2) Set default provider");
367
- console.log(" 3) Set model (auto-selects provider)");
368
- console.log(" 4) View current configuration");
369
- console.log(" 5) Exit\n");
370
- const choice = await rl.question("Enter your choice (1-5): ");
435
+ console.log(theme.colors.primary("\n╭─────────────────────────────────────╮"));
436
+ console.log(theme.colors.primary("│") + theme.colors.highlight(" MegaBuff Configuration Setup ") + theme.colors.primary("│"));
437
+ console.log(theme.colors.primary("╰─────────────────────────────────────╯\n"));
438
+ console.log(theme.colors.secondary("What would you like to configure?\n"));
439
+ console.log(theme.colors.secondary(` ${chalk.bold("1)")} Set API token for a provider`));
440
+ console.log(theme.colors.secondary(` ${chalk.bold("2)")} Set default provider`));
441
+ console.log(theme.colors.secondary(` ${chalk.bold("3)")} Set model (auto-selects provider)`));
442
+ console.log(theme.colors.secondary(` ${chalk.bold("4)")} View current configuration`));
443
+ console.log(theme.colors.secondary(` ${chalk.bold("5)")} Exit\n`));
444
+ const choice = await rl.question(theme.colors.primary("Enter your choice (1-5): "));
371
445
  switch (choice.trim()) {
372
446
  case "1": {
373
447
  // Set token
374
- console.log("\nSelect provider:\n");
448
+ console.log(theme.colors.primary("\nSelect provider:\n"));
375
449
  PROVIDERS.forEach((p, idx) => {
376
- console.log(` ${idx + 1}) ${formatProviderLabel(p)}`);
450
+ console.log(theme.colors.secondary(` ${chalk.bold(idx + 1)}) ${formatProviderLabel(p)}`));
377
451
  });
378
- const providerChoice = await rl.question("\nProvider (number or name): ");
452
+ const providerChoice = await rl.question(theme.colors.primary("\nProvider (number or name): "));
379
453
  const providerNum = Number(providerChoice);
380
454
  let provider;
381
455
  if (Number.isFinite(providerNum) && providerNum >= 1 && providerNum <= PROVIDERS.length) {
@@ -385,30 +459,30 @@ async function interactiveConfig() {
385
459
  provider = normalizeProvider(providerChoice);
386
460
  }
387
461
  if (!provider) {
388
- console.error(`Error: Invalid provider. Valid options: ${PROVIDERS.join(", ")}`);
462
+ console.error(theme.colors.error(`Error: Invalid provider. Valid options: ${PROVIDERS.join(", ")}`));
389
463
  process.exit(1);
390
464
  }
391
- const token = (await rl.question("Enter your API token: ")).trim();
465
+ const token = (await rl.question(theme.colors.primary("Enter your API token: "))).trim();
392
466
  if (!token) {
393
- console.error("Error: No token provided");
467
+ console.error(theme.colors.error("Error: No token provided"));
394
468
  process.exit(1);
395
469
  }
396
- const store = (await rl.question("Store in system keychain? (Y/n): ")).trim().toLowerCase();
470
+ const store = (await rl.question(theme.colors.primary("Store in system keychain? (Y/n): "))).trim().toLowerCase();
397
471
  const useKeychain = store === "" || store === "y" || store === "yes";
398
472
  await setApiKey(provider, token, useKeychain);
399
- console.log(`\n✓ ${provider} token saved${useKeychain ? " securely in system keychain" : " to config file"}`);
473
+ console.log(theme.colors.success(`\n✓ ${provider} token saved`) + theme.colors.dim(useKeychain ? " securely in system keychain" : " to config file"));
400
474
  if (!useKeychain) {
401
- console.log(" Tip: Run with --keychain flag next time for more secure storage");
475
+ console.log(theme.colors.dim(" Tip: Run with --keychain flag next time for more secure storage"));
402
476
  }
403
477
  break;
404
478
  }
405
479
  case "2": {
406
480
  // Set default provider
407
- console.log("\nSelect default provider:\n");
481
+ console.log(theme.colors.primary("\nSelect default provider:\n"));
408
482
  PROVIDERS.forEach((p, idx) => {
409
- console.log(` ${idx + 1}) ${formatProviderLabel(p)}`);
483
+ console.log(theme.colors.secondary(` ${chalk.bold(idx + 1)}) ${formatProviderLabel(p)}`));
410
484
  });
411
- const providerChoice = await rl.question("\nProvider (number or name): ");
485
+ const providerChoice = await rl.question(theme.colors.primary("\nProvider (number or name): "));
412
486
  const providerNum = Number(providerChoice);
413
487
  let provider;
414
488
  if (Number.isFinite(providerNum) && providerNum >= 1 && providerNum <= PROVIDERS.length) {
@@ -418,38 +492,38 @@ async function interactiveConfig() {
418
492
  provider = normalizeProvider(providerChoice);
419
493
  }
420
494
  if (!provider) {
421
- console.error(`Error: Invalid provider. Valid options: ${PROVIDERS.join(", ")}`);
495
+ console.error(theme.colors.error(`Error: Invalid provider. Valid options: ${PROVIDERS.join(", ")}`));
422
496
  process.exit(1);
423
497
  }
424
498
  await setProvider(provider);
425
- console.log(`\n✓ Default provider set to: ${provider}`);
499
+ console.log(theme.colors.success(`\n✓ Default provider set to: `) + theme.colors.highlight(provider));
426
500
  break;
427
501
  }
428
502
  case "3": {
429
503
  // Set model (auto-selects provider)
430
- console.log("\nAvailable models by provider:\n");
504
+ console.log(theme.colors.primary("\nAvailable models by provider:\n"));
431
505
  PROVIDERS.forEach((provider) => {
432
506
  const models = getModelsByProvider(provider);
433
507
  if (models.length > 0) {
434
- console.log(`${formatProviderName(provider)}:`);
435
- models.forEach(model => console.log(` - ${model}`));
508
+ console.log(theme.colors.warning(`${formatProviderName(provider)}:`));
509
+ models.forEach(model => console.log(theme.colors.secondary(` - ${model}`)));
436
510
  console.log();
437
511
  }
438
512
  });
439
- const modelInput = (await rl.question("Enter model name: ")).trim();
513
+ const modelInput = (await rl.question(theme.colors.primary("Enter model name: "))).trim();
440
514
  if (!modelInput) {
441
- console.error("Error: No model provided");
515
+ console.error(theme.colors.error("Error: No model provided"));
442
516
  process.exit(1);
443
517
  }
444
518
  const provider = getProviderForModel(modelInput);
445
519
  if (!provider) {
446
- console.error(`Error: Unknown model '${modelInput}'`);
447
- console.error("Tip: Use one of the models listed above");
520
+ console.error(theme.colors.error(`Error: Unknown model '${modelInput}'`));
521
+ console.error(theme.colors.warning("Tip: Use one of the models listed above"));
448
522
  process.exit(1);
449
523
  }
450
524
  await setModel(modelInput);
451
- console.log(`\n✓ Model set to: ${modelInput}`);
452
- console.log(`✓ Provider auto-set to: ${provider}`);
525
+ console.log(theme.colors.success(`\n✓ Model set to: `) + theme.colors.highlight(modelInput));
526
+ console.log(theme.colors.success(`✓ Provider auto-set to: `) + theme.colors.highlight(provider));
453
527
  break;
454
528
  }
455
529
  case "4": {
@@ -458,26 +532,29 @@ async function interactiveConfig() {
458
532
  const currentProvider = await getProvider();
459
533
  const currentModel = await getModel();
460
534
  const effectiveModel = currentModel ?? getDefaultModel(currentProvider);
535
+ const currentThemeName = await getThemeName();
536
+ const currentTheme = themes[currentThemeName];
461
537
  const providerStatuses = await Promise.all(PROVIDERS.map(async (p) => [p, await hasApiKey(p)]));
462
- console.log("\n╭─────────────────────────────────────╮");
463
- console.log("│ Current Configuration │");
464
- console.log("╰─────────────────────────────────────╯\n");
465
- console.log(`Provider: ${currentProvider}`);
466
- console.log(`Model: ${currentModel ? effectiveModel : `${effectiveModel} (default for provider)`}`);
467
- console.log(`Storage: ${config.useKeychain ? "System Keychain" : "Config File"}`);
468
- console.log("\nAPI Tokens:");
538
+ console.log(theme.colors.primary("\n╭─────────────────────────────────────╮"));
539
+ console.log(theme.colors.primary("│") + theme.colors.highlight(" Current Configuration ") + theme.colors.primary("│"));
540
+ console.log(theme.colors.primary("╰─────────────────────────────────────╯\n"));
541
+ console.log(theme.colors.secondary(`Provider: `) + theme.colors.highlight(currentProvider));
542
+ console.log(theme.colors.secondary(`Model: `) + theme.colors.highlight(currentModel ? effectiveModel : `${effectiveModel}`) + theme.colors.dim(currentModel ? "" : " (default for provider)"));
543
+ console.log(theme.colors.secondary(`Storage: `) + theme.colors.highlight(config.useKeychain ? "System Keychain" : "Config File"));
544
+ console.log(theme.colors.secondary(`Theme: `) + theme.colors.highlight(currentTheme.name));
545
+ console.log(theme.colors.secondary("\nAPI Tokens:"));
469
546
  for (const [p, ok] of providerStatuses) {
470
- console.log(` ${p}: ${ok ? "✓ Configured" : "✗ Not configured"}`);
547
+ console.log(` ${theme.colors.secondary(p)}: ${ok ? theme.colors.success("✓ Configured") : theme.colors.dim("✗ Not configured")}`);
471
548
  }
472
- console.log(`\nConfig file: ~/.megabuff/config.json`);
549
+ console.log(theme.colors.dim(`\nConfig file: ~/.megabuff/config.json`));
473
550
  break;
474
551
  }
475
552
  case "5": {
476
- console.log("\nExiting...");
553
+ console.log(theme.colors.primary("\nExiting..."));
477
554
  break;
478
555
  }
479
556
  default: {
480
- console.error("Invalid choice. Please enter 1-5.");
557
+ console.error(theme.colors.error("Invalid choice. Please enter 1-5."));
481
558
  process.exit(1);
482
559
  }
483
560
  }
@@ -496,11 +573,15 @@ const configCmd = program
496
573
  await interactiveConfig();
497
574
  }
498
575
  else {
499
- console.error("Error: Interactive mode requires a TTY. Use subcommands instead:");
500
- console.error(" megabuff config token <token> --provider <provider>");
501
- console.error(" megabuff config provider <provider>");
502
- console.error(" megabuff config model <model>");
503
- console.error(" megabuff config show");
576
+ console.error("");
577
+ console.error(theme.colors.error(" Error: ") + theme.colors.warning("Interactive mode requires a TTY"));
578
+ console.error("");
579
+ console.error(theme.colors.dim(" Use these subcommands instead:"));
580
+ console.error(theme.colors.accent(" megabuff config token <token> --provider <provider>"));
581
+ console.error(theme.colors.accent(" megabuff config provider <provider>"));
582
+ console.error(theme.colors.accent(" megabuff config model <model>"));
583
+ console.error(theme.colors.accent(" megabuff config show"));
584
+ console.error("");
504
585
  process.exit(1);
505
586
  }
506
587
  });
@@ -514,38 +595,55 @@ configCmd
514
595
  try {
515
596
  const provider = normalizeProvider(options.provider);
516
597
  if (!provider) {
517
- console.error(`Error: Invalid provider '${options.provider}'. Valid options: ${PROVIDERS.join(", ")}`);
598
+ console.error("");
599
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(`Invalid provider '${options.provider}'`));
600
+ console.error("");
601
+ console.error(theme.colors.dim(" Valid providers: ") + theme.colors.info(PROVIDERS.join(", ")));
602
+ console.error("");
518
603
  process.exit(1);
519
604
  }
520
605
  let finalToken = token;
521
606
  if (!finalToken) {
522
607
  if (!process.stdin.isTTY) {
523
- console.error("Error: Missing token argument. Provide it inline or run in an interactive terminal.");
608
+ console.error("");
609
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning("Missing token argument"));
610
+ console.error("");
611
+ console.error(theme.colors.dim(" Provide it inline or run in an interactive terminal"));
612
+ console.error("");
524
613
  process.exit(1);
525
614
  }
526
615
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
527
616
  try {
528
- finalToken = (await rl.question("Enter your API token: ")).trim();
617
+ finalToken = (await rl.question(theme.colors.primary("🔑 Enter your API token: "))).trim();
529
618
  }
530
619
  finally {
531
620
  rl.close();
532
621
  }
533
622
  }
534
623
  if (!finalToken) {
535
- console.error("Error: No token provided");
624
+ console.error("");
625
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning("No token provided"));
626
+ console.error("");
536
627
  process.exit(1);
537
628
  }
538
629
  await setApiKey(provider, finalToken, options.keychain || false);
630
+ console.error("");
539
631
  if (options.keychain) {
540
- console.log(`✓ ${provider} token saved securely in system keychain`);
632
+ console.log(theme.colors.success(`✓ ${formatProviderName(provider)} token saved securely! 🔐`));
633
+ console.log(theme.colors.dim(" Stored in system keychain for maximum security"));
541
634
  }
542
635
  else {
543
- console.log(`✓ ${provider} token saved to config file`);
544
- console.log(" Tip: Use --keychain flag for more secure storage");
636
+ console.log(theme.colors.success(`✓ ${formatProviderName(provider)} token saved! 💾`));
637
+ console.log(theme.colors.dim(" Stored in ") + theme.colors.accent("~/.megabuff/config.json"));
638
+ console.log("");
639
+ console.log(theme.colors.info(" 💡 Tip: ") + theme.colors.dim("Use ") + theme.colors.accent("--keychain") + theme.colors.dim(" flag for more secure storage"));
545
640
  }
641
+ console.error("");
546
642
  }
547
643
  catch (error) {
548
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
644
+ console.error("");
645
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
646
+ console.error("");
549
647
  process.exit(1);
550
648
  }
551
649
  });
@@ -557,19 +655,33 @@ configCmd
557
655
  try {
558
656
  if (!providerArg) {
559
657
  const p = await getProvider();
560
- console.log(`Default provider: ${p}`);
658
+ const providerEmoji = p === "openai" ? "🤖" : p === "anthropic" ? "🧠" : p === "google" ? "✨" : "🔧";
659
+ console.log("");
660
+ console.log(theme.colors.dim(" Current default provider:"));
661
+ console.log(theme.colors.primary(` ${providerEmoji} `) + theme.colors.highlight(formatProviderName(p)));
662
+ console.log("");
561
663
  return;
562
664
  }
563
665
  const p = normalizeProvider(providerArg);
564
666
  if (!p) {
565
- console.error(`Error: Invalid provider '${providerArg}'. Valid options: ${PROVIDERS.join(", ")}`);
667
+ console.error("");
668
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(`Invalid provider '${providerArg}'`));
669
+ console.error("");
670
+ console.error(theme.colors.dim(" Valid providers: ") + theme.colors.info(PROVIDERS.join(", ")));
671
+ console.error("");
566
672
  process.exit(1);
567
673
  }
568
674
  await setProvider(p);
569
- console.log(`✓ Default provider set to: ${p}`);
675
+ const providerEmoji = p === "openai" ? "🤖" : p === "anthropic" ? "🧠" : p === "google" ? "✨" : "🔧";
676
+ console.log("");
677
+ console.log(theme.colors.success(`✓ Default provider updated!`));
678
+ console.log(theme.colors.dim(" Now using: ") + theme.colors.highlight(`${providerEmoji} ${formatProviderName(p)}`));
679
+ console.log("");
570
680
  }
571
681
  catch (error) {
572
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
682
+ console.error("");
683
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
684
+ console.error("");
573
685
  process.exit(1);
574
686
  }
575
687
  });
@@ -583,29 +695,44 @@ configCmd
583
695
  const m = await getModel();
584
696
  const p = await getProvider();
585
697
  const effectiveModel = m ?? getDefaultModel(p);
586
- console.log(`Current model: ${m ? effectiveModel : `${effectiveModel} (default for provider)`}`);
587
- console.log(`Current provider: ${p}`);
698
+ const providerEmoji = p === "openai" ? "🤖" : p === "anthropic" ? "🧠" : p === "google" ? "✨" : "🔧";
699
+ console.log("");
700
+ console.log(theme.colors.dim(" Current configuration:"));
701
+ console.log(theme.colors.primary(` ${providerEmoji} Model: `) + theme.colors.highlight(effectiveModel) + theme.colors.dim(m ? "" : " (default)"));
702
+ console.log(theme.colors.dim(` Provider: `) + theme.colors.info(formatProviderName(p)));
703
+ console.log("");
588
704
  return;
589
705
  }
590
706
  const provider = getProviderForModel(modelArg);
591
707
  if (!provider) {
592
- console.error(`Error: Unknown model '${modelArg}'`);
593
- console.error("\nAvailable models:");
708
+ console.error("");
709
+ console.error(theme.colors.error(" Error: ") + theme.colors.warning(`Unknown model '${modelArg}'`));
710
+ console.error("");
711
+ console.error(theme.colors.dim(" Available models:"));
712
+ console.error("");
594
713
  PROVIDERS.forEach(p => {
595
714
  const models = getModelsByProvider(p);
596
715
  if (models.length > 0) {
597
- console.error(`\n${formatProviderName(p)}:`);
598
- models.forEach(m => console.error(` - ${m}`));
716
+ const providerEmoji = p === "openai" ? "🤖" : p === "anthropic" ? "🧠" : p === "google" ? "✨" : "🔧";
717
+ console.error(theme.colors.primary(` ${providerEmoji} ${formatProviderName(p)}:`));
718
+ models.forEach(m => console.error(theme.colors.dim(` • `) + theme.colors.info(m)));
719
+ console.error("");
599
720
  }
600
721
  });
601
722
  process.exit(1);
602
723
  }
603
724
  await setModel(modelArg);
604
- console.log(`✓ Model set to: ${modelArg}`);
605
- console.log(`✓ Provider auto-set to: ${provider}`);
725
+ const providerEmoji = provider === "openai" ? "🤖" : provider === "anthropic" ? "🧠" : provider === "google" ? "✨" : "🔧";
726
+ console.log("");
727
+ console.log(theme.colors.success(`✓ Configuration updated!`));
728
+ console.log(theme.colors.dim(" Model: ") + theme.colors.highlight(modelArg));
729
+ console.log(theme.colors.dim(" Provider: ") + theme.colors.highlight(`${providerEmoji} ${formatProviderName(provider)}`));
730
+ console.log("");
606
731
  }
607
732
  catch (error) {
608
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
733
+ console.error("");
734
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
735
+ console.error("");
609
736
  process.exit(1);
610
737
  }
611
738
  });
@@ -618,19 +745,34 @@ configCmd
618
745
  const selectedProvider = await getProvider();
619
746
  const selectedModel = await getModel();
620
747
  const effectiveModel = selectedModel ?? getDefaultModel(selectedProvider);
748
+ const currentThemeName = await getThemeName();
749
+ const currentTheme = themes[currentThemeName];
621
750
  const providerStatuses = await Promise.all(PROVIDERS.map(async (p) => [p, await hasApiKey(p)]));
622
- console.log("Current configuration:");
623
- console.log(` Provider: ${selectedProvider}`);
624
- console.log(` Model: ${selectedModel ? effectiveModel : `${effectiveModel} (default for provider)`}`);
625
- console.log(` Storage: ${config.useKeychain ? "System Keychain" : "Config File"}`);
626
- console.log("\nAPI Tokens:");
751
+ const providerEmoji = selectedProvider === "openai" ? "🤖" : selectedProvider === "anthropic" ? "🧠" : selectedProvider === "google" ? "✨" : "🔧";
752
+ console.log("");
753
+ console.log(theme.colors.primary("╭────────────────────────────────────────────╮"));
754
+ console.log(theme.colors.primary("│") + theme.colors.highlight(" ⚙️ MegaBuff Configuration ") + theme.colors.primary(""));
755
+ console.log(theme.colors.primary("╰────────────────────────────────────────────╯"));
756
+ console.log("");
757
+ console.log(theme.colors.dim(" Active Settings:"));
758
+ console.log(theme.colors.primary(` ${providerEmoji} Provider: `) + theme.colors.highlight(formatProviderName(selectedProvider)));
759
+ console.log(theme.colors.primary(` 🎯 Model: `) + theme.colors.highlight(effectiveModel) + theme.colors.dim(selectedModel ? "" : " (default)"));
760
+ console.log(theme.colors.primary(` 💾 Storage: `) + theme.colors.highlight(config.useKeychain ? "System Keychain 🔐" : "Config File"));
761
+ console.log(theme.colors.primary(` 🎨 Theme: `) + theme.colors.highlight(currentTheme.name));
762
+ console.log("");
763
+ console.log(theme.colors.dim(" API Token Status:"));
627
764
  for (const [p, ok] of providerStatuses) {
628
- console.log(` ${p}: ${ok ? " Configured" : " Not configured"}`);
765
+ const emoji = p === "openai" ? "🤖" : p === "anthropic" ? "🧠" : p === "google" ? "✨" : "🔧";
766
+ console.log(` ${emoji} ${theme.colors.secondary(formatProviderName(p).padEnd(16))}: ${ok ? theme.colors.success("✓ Configured") : theme.colors.dim("✗ Not configured")}`);
629
767
  }
630
- console.log(`\nConfig location: ~/.megabuff/config.json`);
768
+ console.log("");
769
+ console.log(theme.colors.dim(" 📁 Config location: ") + theme.colors.accent("~/.megabuff/config.json"));
770
+ console.log("");
631
771
  }
632
772
  catch (error) {
633
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
773
+ console.error("");
774
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
775
+ console.error("");
634
776
  process.exit(1);
635
777
  }
636
778
  });
@@ -642,10 +784,190 @@ configCmd
642
784
  try {
643
785
  const provider = await getProvider(options.provider);
644
786
  await removeApiKey(provider);
645
- console.log(`✓ ${provider} token removed from config and keychain`);
787
+ console.log("");
788
+ console.log(theme.colors.success(`✓ ${formatProviderName(provider)} token removed successfully! 🗑️`));
789
+ console.log(theme.colors.dim(" Cleared from config file and system keychain"));
790
+ console.log("");
646
791
  }
647
792
  catch (error) {
648
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
793
+ console.error("");
794
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
795
+ console.error("");
796
+ process.exit(1);
797
+ }
798
+ });
799
+ // Theme command
800
+ const themeCmd = program
801
+ .command("theme")
802
+ .description("Manage color themes")
803
+ .action(async () => {
804
+ // Show current theme when no subcommand
805
+ const currentThemeName = await getThemeName();
806
+ const currentTheme = themes[currentThemeName];
807
+ const { colors } = currentTheme;
808
+ console.log("");
809
+ console.log(theme.colors.primary("╭────────────────────────────────────────────╮"));
810
+ console.log(theme.colors.primary("│") + theme.colors.highlight(" 🎨 Current Theme ") + theme.colors.primary("│"));
811
+ console.log(theme.colors.primary("╰────────────────────────────────────────────╯"));
812
+ console.log("");
813
+ console.log(theme.colors.primary(` Theme: `) + theme.colors.highlight(currentTheme.name));
814
+ console.log(theme.colors.dim(` ${currentTheme.description}`));
815
+ console.log("");
816
+ console.log(theme.colors.dim(" Color preview:"));
817
+ const preview = [
818
+ colors.primary("primary"),
819
+ colors.success("success"),
820
+ colors.error("error"),
821
+ colors.warning("warning"),
822
+ colors.info("info"),
823
+ colors.accent("accent")
824
+ ].join(" ");
825
+ console.log(` ${preview}`);
826
+ console.log("");
827
+ console.log(theme.colors.dim(" 💡 Commands:"));
828
+ console.log(theme.colors.accent(" megabuff theme list") + theme.colors.dim(" - See all themes"));
829
+ console.log(theme.colors.accent(" megabuff theme set <name>") + theme.colors.dim(" - Change theme"));
830
+ console.log("");
831
+ });
832
+ themeCmd
833
+ .command("list")
834
+ .description("List all available themes")
835
+ .action(async () => {
836
+ const currentTheme = await getThemeName();
837
+ const themeNames = getAllThemeNames();
838
+ console.log("");
839
+ console.log(theme.colors.primary("╭────────────────────────────────────────────────────────────────────╮"));
840
+ console.log(theme.colors.primary("│") + theme.colors.highlight(" 🎨 Available Themes ") + theme.colors.primary("│"));
841
+ console.log(theme.colors.primary("╰────────────────────────────────────────────────────────────────────╯"));
842
+ console.log("");
843
+ for (const themeName of themeNames) {
844
+ const t = themes[themeName];
845
+ const isCurrent = themeName === currentTheme;
846
+ const { colors } = t;
847
+ // Theme name with indicator if current
848
+ if (isCurrent) {
849
+ console.log(colors.success(` ● ${t.name}`) + theme.colors.dim(" ⭐ (active)"));
850
+ }
851
+ else {
852
+ console.log(colors.primary(` ${t.name}`));
853
+ }
854
+ console.log(colors.dim(` ${t.description}`));
855
+ // Show color preview
856
+ const preview = [
857
+ colors.primary("primary"),
858
+ colors.success("success"),
859
+ colors.error("error"),
860
+ colors.warning("warning"),
861
+ colors.info("info"),
862
+ colors.accent("accent")
863
+ ].join(" ");
864
+ console.log(` ${preview}`);
865
+ console.log();
866
+ }
867
+ console.log(theme.colors.dim(` To change theme: `) + theme.colors.accent(`megabuff theme set <theme-name>`));
868
+ console.log(theme.colors.dim(` To preview theme: `) + theme.colors.accent(`megabuff theme preview <theme-name>`));
869
+ console.log("");
870
+ });
871
+ themeCmd
872
+ .command("set")
873
+ .description("Set the active theme")
874
+ .argument("<theme>", "Theme name")
875
+ .action(async (themeName) => {
876
+ try {
877
+ const normalizedTheme = themeName.toLowerCase();
878
+ if (!isValidTheme(normalizedTheme)) {
879
+ console.error("");
880
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(`Unknown theme '${themeName}'`));
881
+ console.error("");
882
+ console.error(theme.colors.dim(" Available themes:"));
883
+ getAllThemeNames().forEach(name => {
884
+ console.error(theme.colors.info(` • ${name}`));
885
+ });
886
+ console.error("");
887
+ process.exit(1);
888
+ }
889
+ await setThemeName(normalizedTheme);
890
+ clearThemeCache(); // Clear cache so next command uses new theme
891
+ const newTheme = themes[normalizedTheme];
892
+ const { colors } = newTheme;
893
+ console.log("");
894
+ console.log(colors.success(`✓ Theme changed successfully! 🎨`));
895
+ console.log(colors.dim(` Now using: `) + colors.highlight(newTheme.name));
896
+ console.log(colors.dim(` ${newTheme.description}`));
897
+ // Show preview
898
+ console.log("");
899
+ console.log(colors.dim(" Color preview:"));
900
+ const preview = [
901
+ colors.primary("primary"),
902
+ colors.success("success"),
903
+ colors.error("error"),
904
+ colors.warning("warning"),
905
+ colors.info("info"),
906
+ colors.accent("accent")
907
+ ].join(" ");
908
+ console.log(` ${preview}`);
909
+ console.log("");
910
+ }
911
+ catch (error) {
912
+ console.error("");
913
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
914
+ console.error("");
915
+ process.exit(1);
916
+ }
917
+ });
918
+ themeCmd
919
+ .command("preview")
920
+ .description("Preview a theme without setting it")
921
+ .argument("<theme>", "Theme name to preview")
922
+ .action(async (themeName) => {
923
+ try {
924
+ const normalizedTheme = themeName.toLowerCase();
925
+ if (!isValidTheme(normalizedTheme)) {
926
+ console.error("");
927
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(`Unknown theme '${themeName}'`));
928
+ console.error("");
929
+ console.error(theme.colors.dim(" Available themes:"));
930
+ getAllThemeNames().forEach(name => {
931
+ console.error(theme.colors.info(` • ${name}`));
932
+ });
933
+ console.error("");
934
+ process.exit(1);
935
+ }
936
+ const previewTheme = themes[normalizedTheme];
937
+ const { colors } = previewTheme;
938
+ console.log("");
939
+ console.log(colors.primary("╭────────────────────────────────────────────────────────╮"));
940
+ console.log(colors.primary("│") + colors.highlight(` 🎨 ${previewTheme.name} Theme Preview `.padEnd(55)) + colors.primary("│"));
941
+ console.log(colors.primary("╰────────────────────────────────────────────────────────╯"));
942
+ console.log("");
943
+ console.log(colors.dim(` ${previewTheme.description}`));
944
+ console.log("");
945
+ console.log(colors.dim(" Color Palette:"));
946
+ console.log("");
947
+ console.log(colors.primary(" ● Primary text and headings"));
948
+ console.log(colors.secondary(" ● Secondary text and descriptions"));
949
+ console.log(colors.success(" ✓ Success messages and confirmations"));
950
+ console.log(colors.error(" ✗ Error messages and warnings"));
951
+ console.log(colors.warning(" ⚠ Warning and important notes"));
952
+ console.log(colors.info(" ℹ Info messages and details"));
953
+ console.log(colors.highlight(" ★ Highlighted and emphasized text"));
954
+ console.log(colors.accent(" ◆ Accent colors and special elements"));
955
+ console.log(colors.dim(" ○ Dimmed and secondary information"));
956
+ console.log("");
957
+ const currentTheme = await getThemeName();
958
+ if (currentTheme !== normalizedTheme) {
959
+ console.log(colors.dim(" To activate this theme: ") + colors.accent(`megabuff theme set ${normalizedTheme}`));
960
+ console.log("");
961
+ }
962
+ else {
963
+ console.log(colors.success(" ⭐ This is your current active theme!"));
964
+ console.log("");
965
+ }
966
+ }
967
+ catch (error) {
968
+ console.error("");
969
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
970
+ console.error("");
649
971
  process.exit(1);
650
972
  }
651
973
  });
@@ -669,7 +991,10 @@ program
669
991
  });
670
992
  const original = await getInput(inlinePrompt, options);
671
993
  if (!original.trim()) {
672
- console.error("Error: No prompt provided");
994
+ console.error("");
995
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning("No prompt provided"));
996
+ console.error(theme.colors.dim(" Provide a prompt inline, via --file, or through stdin"));
997
+ console.error("");
673
998
  process.exit(1);
674
999
  }
675
1000
  let provider = await getProvider(options.provider);
@@ -688,16 +1013,23 @@ program
688
1013
  debugLog("token.resolved.afterFirstRun", { provider, source, token: maskSecret(apiKey) });
689
1014
  }
690
1015
  if (!apiKey) {
691
- throw new Error(`No token configured for provider '${provider}'. ` +
692
- `Run: megabuff config set --provider ${provider} <token> ` +
693
- `or set the appropriate environment variable.`);
1016
+ console.error("");
1017
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(`No API key configured for ${formatProviderName(provider)}`));
1018
+ console.error("");
1019
+ console.error(theme.colors.dim(" Configure your API key using:"));
1020
+ console.error(theme.colors.accent(` megabuff config set --provider ${provider} <your-api-key>`));
1021
+ console.error("");
1022
+ console.error(theme.colors.dim(" Or set an environment variable for this provider"));
1023
+ console.error("");
1024
+ process.exit(1);
694
1025
  }
695
1026
  // Get the configured model (if any) for this provider
696
1027
  const configuredModel = await getModel();
697
1028
  const modelToUse = configuredModel && getProviderForModel(configuredModel) === provider ? configuredModel : undefined;
698
1029
  debugLog("model.selected", { configuredModel, modelToUse, provider });
699
1030
  // Route to the appropriate provider's optimization function
700
- const spinner = createSpinner(`Optimizing with ${formatProviderName(provider)}${modelToUse ? ` (${modelToUse})` : ""}...`);
1031
+ const providerEmoji = provider === "openai" ? "🤖" : provider === "anthropic" ? "🧠" : "";
1032
+ const spinner = createSpinner(`${providerEmoji} Optimizing your prompt with ${formatProviderName(provider)}${modelToUse ? ` (${modelToUse})` : ""}...`);
701
1033
  spinner.start();
702
1034
  let optimized;
703
1035
  const t0 = Date.now();
@@ -712,21 +1044,28 @@ program
712
1044
  optimized = await optimizePromptGemini(original, apiKey, modelToUse);
713
1045
  }
714
1046
  else {
715
- throw new Error(`Provider '${provider}' is not supported yet in optimize. ` +
716
- `Supported providers: openai, anthropic, google`);
1047
+ console.error("");
1048
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(`Provider '${provider}' is not supported for optimization`));
1049
+ console.error("");
1050
+ console.error(theme.colors.dim(" Supported providers: ") + theme.colors.info("openai, anthropic, google"));
1051
+ console.error("");
1052
+ process.exit(1);
717
1053
  }
1054
+ const duration = ((Date.now() - t0) / 1000).toFixed(1);
718
1055
  debugLog("optimize.done", { provider, ms: Date.now() - t0, optimizedLength: optimized.length });
719
- spinner.stop(`✓ Optimized with ${formatProviderName(provider)}`);
1056
+ spinner.stop(`✨ Optimization complete in ${duration}s!`);
720
1057
  }
721
1058
  catch (e) {
722
1059
  debugLog("optimize.error", { provider, ms: Date.now() - t0, error: e instanceof Error ? e.message : String(e) });
723
- spinner.fail(`✗ Optimization failed (${formatProviderName(provider)})`);
1060
+ spinner.fail(`💥 Optimization failed with ${formatProviderName(provider)}`);
724
1061
  throw e;
725
1062
  }
726
1063
  await outputResult(original, optimized, options);
727
1064
  }
728
1065
  catch (error) {
729
- console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
1066
+ console.error("");
1067
+ console.error(theme.colors.error("❌ Error: ") + theme.colors.warning(error instanceof Error ? error.message : String(error)));
1068
+ console.error("");
730
1069
  process.exit(1);
731
1070
  }
732
1071
  });