@bobsworkshop/cli 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +10 -10
  2. package/dist/{analyse-auto-OBCDWYWX.js → analyse-auto-HYYDD4OQ.js} +1 -1
  3. package/dist/{bin/analyse-results-5XEQNL5W.js → analyse-results-7TS24WG7.js} +1 -1
  4. package/dist/bob.js +3612 -941
  5. package/dist/{bin/chunk-KDCVG7F2.js → chunk-CI36GGK2.js} +124 -88
  6. package/package.json +3 -1
  7. package/dist/analyse-results-QSOD3KVC.js +0 -8
  8. package/dist/bin/analyse-auto-6T42LV3G.js +0 -529
  9. package/dist/bin/analyse-auto-BHLEMIH5.js +0 -529
  10. package/dist/bin/analyse-auto-CM7XEPKT.js +0 -529
  11. package/dist/bin/analyse-auto-KZNPVVCR.js +0 -529
  12. package/dist/bin/analyse-auto-OBCDWYWX.js +0 -529
  13. package/dist/bin/analyse-auto-QUGN5LES.js +0 -529
  14. package/dist/bin/analyse-auto-RDYNFTCD.js +0 -529
  15. package/dist/bin/analyse-auto-Y5QUQU4G.js +0 -529
  16. package/dist/bin/analyse-results-F7TH2YMO.js +0 -8
  17. package/dist/bin/analyse-results-KM5C7XL7.js +0 -8
  18. package/dist/bin/analyse-results-KU3KEG3E.js +0 -8
  19. package/dist/bin/analyse-results-OU6F6TRX.js +0 -8
  20. package/dist/bin/analyse-results-QSOD3KVC.js +0 -8
  21. package/dist/bin/analyse-results-UHU4DPO3.js +0 -8
  22. package/dist/bin/analyse-results-ZM5ABNEL.js +0 -8
  23. package/dist/bin/bob.js +0 -5144
  24. package/dist/bin/chunk-IYYF7MYV.js +0 -964
  25. package/dist/bin/chunk-J4RRWEHU.js +0 -939
  26. package/dist/bin/chunk-KSHHT2WT.js +0 -939
  27. package/dist/bin/chunk-L554PTBY.js +0 -888
  28. package/dist/bin/chunk-LHWBSCJ4.js +0 -878
  29. package/dist/bin/chunk-RSOPJT6F.js +0 -883
  30. package/dist/bin/chunk-TXCQFX4W.js +0 -946
  31. package/dist/chunk-LHWBSCJ4.js +0 -878
@@ -304,16 +304,26 @@ async function callLocalModel(endpoint, messages) {
304
304
  }
305
305
  );
306
306
  if (response.data?.message?.content) {
307
- return response.data.message.content;
307
+ return {
308
+ text: response.data.message.content,
309
+ evalCount: response.data.eval_count || void 0,
310
+ promptEvalCount: response.data.prompt_eval_count || void 0,
311
+ evalDurationMs: response.data.eval_duration ? Math.round(response.data.eval_duration / 1e6) : void 0,
312
+ totalDurationMs: response.data.total_duration ? Math.round(response.data.total_duration / 1e6) : void 0
313
+ };
308
314
  }
309
315
  const choice = response.data?.choices?.[0];
310
316
  if (choice?.message?.content) {
311
- return choice.message.content;
317
+ return {
318
+ text: choice.message.content,
319
+ evalCount: response.data.usage?.completion_tokens || void 0,
320
+ promptEvalCount: response.data.usage?.prompt_tokens || void 0
321
+ };
312
322
  }
313
323
  if (typeof response.data?.response === "string") {
314
- return response.data.response;
324
+ return { text: response.data.response };
315
325
  }
316
- return "No response received from local model.";
326
+ return { text: "No response received from local model." };
317
327
  } catch (error) {
318
328
  if (error.code === "ECONNREFUSED") {
319
329
  throw new Error("Cannot connect to local model. Is Ollama running? Check your endpoint: " + endpoint);
@@ -369,6 +379,13 @@ import * as fs2 from "fs";
369
379
  import * as path2 from "path";
370
380
  import * as readline2 from "readline";
371
381
  import chalk2 from "chalk";
382
+ var SUCCESS = chalk2.hex("#66BB6A");
383
+ var INFO = chalk2.hex("#26C6DA");
384
+ var WARNING = chalk2.hex("#FFC107");
385
+ var ERROR = chalk2.hex("#EF5350");
386
+ var MUTED = chalk2.hex("#78909C");
387
+ var BRAND_SECONDARY = chalk2.hex("#FFAB00");
388
+ var BORDER = chalk2.hex("#455A64");
372
389
  function extractAllProposedFiles(response) {
373
390
  const proposals = [];
374
391
  const codeBlockRegex = /```[\w]*\n([\s\S]*?)```/g;
@@ -433,19 +450,19 @@ async function processAllProposedFiles(response, autoApprove = false, existingRl
433
450
  function displayExternalFile(proposed) {
434
451
  const totalLines = proposed.content.split("\n").length;
435
452
  console.log("");
436
- console.log(chalk2.yellow(` \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`));
437
- console.log(chalk2.yellow(` \u2502 \u{1F4CB} EXTERNAL: ${proposed.filePath}`));
438
- console.log(chalk2.yellow(` \u2502 This file belongs to another project.`));
439
- console.log(chalk2.yellow(` \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524`));
453
+ console.log(WARNING(` \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
454
+ console.log(WARNING(` \u2551`) + BRAND_SECONDARY(` \u{1F4CB} EXTERNAL: ${proposed.filePath}`));
455
+ console.log(WARNING(` \u2551`) + MUTED(` This file belongs to another project.`));
456
+ console.log(WARNING(` \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`));
440
457
  const previewLines = proposed.content.split("\n").slice(0, 6);
441
458
  for (const line of previewLines) {
442
- console.log(chalk2.gray(` \u2502 ${line}`));
459
+ console.log(WARNING(` \u2551`) + MUTED(` ${line}`));
443
460
  }
444
461
  if (totalLines > 6) {
445
- console.log(chalk2.gray(` \u2502 ... (${totalLines - 6} more lines)`));
462
+ console.log(WARNING(` \u2551`) + MUTED(` ... (${totalLines - 6} more lines)`));
446
463
  }
447
- console.log(chalk2.yellow(` \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`));
448
- console.log(chalk2.gray(` Copy this file manually to your project at: ${proposed.filePath}`));
464
+ console.log(WARNING(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
465
+ console.log(MUTED(` Copy this file manually to your project at: ${proposed.filePath}`));
449
466
  console.log("");
450
467
  }
451
468
  async function proposeAndWriteFile(proposed, autoApprove = false, existingRl) {
@@ -456,37 +473,43 @@ async function proposeAndWriteFile(proposed, autoApprove = false, existingRl) {
456
473
  const absolutePath = path2.join(process.cwd(), proposed.filePath);
457
474
  const action = proposed.isNew ? "CREATE" : "UPDATE";
458
475
  const icon = proposed.isNew ? "\u{1F4C4}" : "\u270F\uFE0F";
459
- const color = proposed.isNew ? chalk2.green : chalk2.yellow;
476
+ const accentColor = proposed.isNew ? SUCCESS : BRAND_SECONDARY;
460
477
  const totalLines = proposed.content.split("\n").length;
461
478
  if (!autoApprove) {
462
479
  console.log("");
463
- console.log(color(` \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`));
464
- console.log(color(` \u2502 ${icon} ${action}: ${proposed.filePath} (${totalLines} lines)`));
465
- console.log(color(` \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524`));
480
+ console.log(BORDER(` \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`));
481
+ console.log(BORDER(` \u2551`) + accentColor(` ${icon} ${action}: `) + chalk2.white(`${proposed.filePath}`) + MUTED(` (${totalLines} lines)`));
482
+ console.log(BORDER(` \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563`));
466
483
  const previewLines = proposed.content.split("\n").slice(0, 6);
467
484
  for (const line of previewLines) {
468
- console.log(chalk2.gray(` \u2502 ${line}`));
485
+ console.log(BORDER(` \u2551`) + MUTED(` ${line}`));
469
486
  }
470
487
  if (totalLines > 6) {
471
- console.log(chalk2.gray(` \u2502 ... (${totalLines - 6} more lines)`));
488
+ console.log(BORDER(` \u2551`) + MUTED(` ... (${totalLines - 6} more lines)`));
472
489
  }
473
- console.log(color(` \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`));
490
+ console.log(BORDER(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D`));
474
491
  console.log("");
492
+ const promptText = INFO(` \u{1F4BE} ${action === "CREATE" ? "Write this file" : "Apply changes"}? `) + MUTED(`(y/n/path): `);
475
493
  let answer;
476
494
  if (existingRl) {
477
- answer = await new Promise((resolve3) => {
478
- existingRl.question(chalk2.cyan(` \u{1F4BE} ${action === "CREATE" ? "Write this file" : "Apply changes"}? (y/n/path): `), resolve3);
479
- });
495
+ existingRl.pause();
496
+ process.stdout.write(promptText);
497
+ const buf = Buffer.alloc(1024);
498
+ const bytesRead = fs2.readSync(0, buf, 0, 1024, null);
499
+ answer = buf.toString("utf-8", 0, bytesRead).replace(/\r?\n/, "").trim();
500
+ existingRl.resume();
480
501
  } else {
481
502
  const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
482
503
  answer = await new Promise((resolve3) => {
483
- rl.question(chalk2.cyan(` \u{1F4BE} ${action === "CREATE" ? "Write this file" : "Apply changes"}? (y/n/path): `), resolve3);
504
+ rl.question(promptText, (ans) => {
505
+ rl.close();
506
+ resolve3(ans);
507
+ });
484
508
  });
485
- rl.close();
486
509
  }
487
510
  const trimmed = answer.trim().toLowerCase();
488
511
  if (trimmed === "n" || trimmed === "no") {
489
- console.log(chalk2.gray(" \u23ED\uFE0F Skipped."));
512
+ console.log(MUTED(" \u23ED\uFE0F Skipped."));
490
513
  return false;
491
514
  }
492
515
  if (trimmed !== "y" && trimmed !== "yes" && trimmed.length > 0) {
@@ -509,14 +532,14 @@ function writeFile(targetPath, content, originalFilePath, isNew) {
509
532
  }
510
533
  fs2.writeFileSync(targetPath, content, "utf-8");
511
534
  const relativePath = path2.relative(process.cwd(), targetPath);
512
- console.log(chalk2.green(` \u2705 Written: ${relativePath}`));
535
+ console.log(SUCCESS(` \u2705 Written: ${relativePath}`));
513
536
  if (!isNew) {
514
- console.log(chalk2.gray(` \u{1F4E6} Backup saved to .bob-backups/`));
537
+ console.log(MUTED(` \u{1F4E6} Backup saved to .bob-backups/`));
515
538
  }
516
539
  console.log("");
517
540
  return true;
518
541
  } catch (error) {
519
- console.log(chalk2.red(` \u274C Write failed: ${error.message}`));
542
+ console.log(ERROR(` \u274C Write failed: ${error.message}`));
520
543
  return false;
521
544
  }
522
545
  }
@@ -608,13 +631,15 @@ function markSuggestionById(id, category, status, metadata) {
608
631
  }
609
632
 
610
633
  // src/commands/analyse-results.ts
611
- var RED = chalk3.hex("#EF5350");
612
- var PURPLE = chalk3.hex("#AB47BC");
613
- var BLUE = chalk3.hex("#42A5F5");
614
- var TEAL = chalk3.hex("#26A69A");
615
- var AMBER = chalk3.hex("#FFAB00");
616
- var GRAY = chalk3.gray;
617
- var BORDER = chalk3.hex("#455A64");
634
+ var BRAND_PRIMARY = chalk3.hex("#E66F24");
635
+ var BRAND_SECONDARY2 = chalk3.hex("#FFAB00");
636
+ var SUCCESS2 = chalk3.hex("#66BB6A");
637
+ var INFO2 = chalk3.hex("#26C6DA");
638
+ var WARNING2 = chalk3.hex("#FFC107");
639
+ var ERROR2 = chalk3.hex("#EF5350");
640
+ var MUTED2 = chalk3.hex("#78909C");
641
+ var BORDER2 = chalk3.hex("#455A64");
642
+ var MODE_CONSULTANT = chalk3.hex("#AB47BC");
618
643
  var PRIORITY_COLORS = {
619
644
  "critical": chalk3.bgHex("#B71C1C").white,
620
645
  "high": chalk3.hex("#FF6D00"),
@@ -622,10 +647,16 @@ var PRIORITY_COLORS = {
622
647
  "low": chalk3.hex("#66BB6A")
623
648
  };
624
649
  var CATEGORY_COLORS = {
625
- "bugs": RED,
626
- "features": PURPLE,
627
- "improvements": BLUE,
628
- "upgrades": TEAL
650
+ "bugs": ERROR2,
651
+ "features": MODE_CONSULTANT,
652
+ "improvements": INFO2,
653
+ "upgrades": SUCCESS2
654
+ };
655
+ var CATEGORY_ICONS = {
656
+ "bugs": "\u{1F534}",
657
+ "features": "\u{1F7E3}",
658
+ "improvements": "\u{1F535}",
659
+ "upgrades": "\u{1F7E2}"
629
660
  };
630
661
  async function showInteractiveResults(config, category, sort, search) {
631
662
  let allSuggestions = [];
@@ -639,7 +670,7 @@ async function showInteractiveResults(config, category, sort, search) {
639
670
  });
640
671
  allSuggestions = result?.suggestions || [];
641
672
  } catch (error) {
642
- console.log(chalk3.red(` \u274C ${error.message}`));
673
+ console.log(ERROR2(` \u274C ${error.message}`));
643
674
  return;
644
675
  }
645
676
  } else {
@@ -654,33 +685,34 @@ async function showInteractiveResults(config, category, sort, search) {
654
685
  sortSuggestions(allSuggestions, sort || "priority");
655
686
  if (allSuggestions.length === 0) {
656
687
  console.log("");
657
- console.log(chalk3.green(" \u2705 No items found. Clean!"));
688
+ console.log(SUCCESS2(" \u2705 No items found. Clean!"));
658
689
  console.log("");
659
690
  return;
660
691
  }
661
- const color = CATEGORY_COLORS[category] || GRAY;
692
+ const color = CATEGORY_COLORS[category] || MUTED2;
693
+ const icon = CATEGORY_ICONS[category] || "\u25C6";
662
694
  let running = true;
663
695
  let displaySuggestions = [...allSuggestions];
664
696
  let currentSort = sort || "priority";
665
697
  while (running) {
666
698
  console.log("");
667
- console.log(color(` \u25C6 ${category.toUpperCase()} (${displaySuggestions.length} items) | Sort: ${currentSort}`));
668
- console.log(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
699
+ console.log(color(` ${icon} ${category.toUpperCase()} (${displaySuggestions.length} items) \u2502 Sort: ${currentSort}`));
700
+ console.log(MUTED2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
669
701
  console.log("");
670
702
  const choices = [];
671
703
  choices.push({
672
- name: chalk3.cyan(" \u{1F500} Toggle sort"),
704
+ name: INFO2(" \u{1F500} Toggle sort"),
673
705
  value: "__sort__",
674
706
  short: "Sort"
675
707
  });
676
- choices.push(new inquirer.Separator(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
708
+ choices.push(new inquirer.Separator(MUTED2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
677
709
  for (let idx = 0; idx < displaySuggestions.length; idx++) {
678
710
  const item = displaySuggestions[idx];
679
- const pColor = PRIORITY_COLORS[item.priority?.toLowerCase()] || GRAY;
680
- const priorityLabel = (item.priority || "MEDIUM").toUpperCase().padEnd(9);
681
- const filePath = (item.filePath || "unknown").split("/").pop() || "unknown";
682
- const desc = (item.description || item.title || "No description").slice(0, 42);
683
- const displayName = `${pColor(priorityLabel)} ${chalk3.cyan(filePath.padEnd(18))} ${chalk3.white(desc)}`;
711
+ const pColor = PRIORITY_COLORS[item.priority?.toLowerCase()] || MUTED2;
712
+ const priorityLabel = pColor((item.priority || "MEDIUM").toUpperCase().padEnd(9));
713
+ const fileName = (item.filePath || "unknown").split("/").pop() || "unknown";
714
+ const title = (item.title || item.description || "No description").slice(0, 40);
715
+ const displayName = ` ${priorityLabel} ${INFO2(fileName.padEnd(20))} ${chalk3.white(title)}`;
684
716
  choices.push({
685
717
  name: displayName,
686
718
  value: idx,
@@ -688,9 +720,9 @@ async function showInteractiveResults(config, category, sort, search) {
688
720
  description: `${item.priority} ${item.filePath} ${item.title} ${item.description}`
689
721
  });
690
722
  }
691
- choices.push(new inquirer.Separator(GRAY(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
723
+ choices.push(new inquirer.Separator(MUTED2(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
692
724
  choices.push({
693
- name: chalk3.gray(" \u2190 Quit"),
725
+ name: MUTED2(" \u2190 Quit"),
694
726
  value: "__quit__",
695
727
  short: "Quit"
696
728
  });
@@ -720,7 +752,7 @@ async function showInteractiveResults(config, category, sort, search) {
720
752
  if (selected === "__sort__") {
721
753
  currentSort = currentSort === "priority" ? "file" : "priority";
722
754
  sortSuggestions(displaySuggestions, currentSort);
723
- console.log(chalk3.cyan(` Sort changed to: ${currentSort}`));
755
+ console.log(INFO2(` Sort changed to: ${currentSort}`));
724
756
  continue;
725
757
  }
726
758
  if (typeof selected === "number") {
@@ -741,48 +773,49 @@ async function showInteractiveResults(config, category, sort, search) {
741
773
  displaySuggestions.splice(selected, 1);
742
774
  const originalIdx = allSuggestions.findIndex((s) => s.id === item.id);
743
775
  if (originalIdx !== -1) allSuggestions.splice(originalIdx, 1);
744
- console.log(chalk3.gray(" \u23ED\uFE0F Dismissed and logged."));
776
+ console.log(MUTED2(" \u23ED\uFE0F Dismissed and logged."));
745
777
  }
746
778
  }
747
779
  }
748
780
  }
749
781
  async function showExpandedView(item, category) {
750
- const color = CATEGORY_COLORS[category] || GRAY;
751
- const pColor = PRIORITY_COLORS[item.priority?.toLowerCase()] || GRAY;
782
+ const color = CATEGORY_COLORS[category] || MUTED2;
783
+ const pColor = PRIORITY_COLORS[item.priority?.toLowerCase()] || MUTED2;
784
+ const icon = CATEGORY_ICONS[category] || "\u25C6";
752
785
  console.log("");
753
- console.log(color(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
754
- console.log(color(" \u2551 ") + pColor(`${(item.priority || "MEDIUM").toUpperCase()} ${category.toUpperCase().slice(0, -1)}`));
755
- console.log(color(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
756
- console.log(color(" \u2551") + chalk3.gray(" File: ") + chalk3.cyan(item.filePath || "unknown"));
757
- console.log(color(" \u2551") + chalk3.gray(" Priority: ") + pColor((item.priority || "medium").toUpperCase()));
758
- console.log(color(" \u2551"));
759
- console.log(color(" \u2551") + chalk3.gray(" Title:"));
760
- console.log(color(" \u2551") + chalk3.white.bold(` ${item.title || "No title"}`));
761
- console.log(color(" \u2551"));
762
- console.log(color(" \u2551") + chalk3.gray(" Description:"));
786
+ console.log(BORDER2(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
787
+ console.log(BORDER2(" \u2551") + ` ${icon} ` + pColor(`${(item.priority || "MEDIUM").toUpperCase()} ${category.toUpperCase().slice(0, -1)}`));
788
+ console.log(BORDER2(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
789
+ console.log(BORDER2(" \u2551") + MUTED2(" File: ") + INFO2(item.filePath || "unknown"));
790
+ console.log(BORDER2(" \u2551") + MUTED2(" Priority: ") + pColor((item.priority || "medium").toUpperCase()));
791
+ console.log(BORDER2(" \u2551"));
792
+ console.log(BORDER2(" \u2551") + MUTED2(" Title:"));
793
+ console.log(BORDER2(" \u2551") + chalk3.white.bold(` ${item.title || "No title"}`));
794
+ console.log(BORDER2(" \u2551"));
795
+ console.log(BORDER2(" \u2551") + MUTED2(" Description:"));
763
796
  const descLines = wrapText(item.description || "No description", 54);
764
797
  for (const line of descLines) {
765
- console.log(color(" \u2551") + chalk3.white(` ${line}`));
798
+ console.log(BORDER2(" \u2551") + chalk3.white(` ${line}`));
766
799
  }
767
800
  if (item.implementation) {
768
- console.log(color(" \u2551"));
769
- console.log(color(" \u2551") + chalk3.gray(" Implementation:"));
801
+ console.log(BORDER2(" \u2551"));
802
+ console.log(BORDER2(" \u2551") + MUTED2(" Implementation:"));
770
803
  const implLines = wrapText(item.implementation, 54);
771
804
  for (const line of implLines) {
772
- console.log(color(" \u2551") + chalk3.white(` ${line}`));
805
+ console.log(BORDER2(" \u2551") + chalk3.white(` ${line}`));
773
806
  }
774
807
  }
775
- console.log(color(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
808
+ console.log(BORDER2(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
776
809
  console.log("");
777
810
  const { action } = await inquirer.prompt([
778
811
  {
779
812
  type: "select",
780
813
  name: "action",
781
- message: "What do you want to do?",
814
+ message: BRAND_SECONDARY2("What do you want to do?"),
782
815
  choices: [
783
- { name: chalk3.green(" \u{1F527} Implement this fix"), value: "implement" },
784
- { name: chalk3.red(" \u{1F5D1}\uFE0F Dismiss"), value: "dismiss" },
785
- { name: chalk3.gray(" \u2190 Back to list"), value: "back" }
816
+ { name: SUCCESS2(" \u{1F527} Implement this fix"), value: "implement" },
817
+ { name: ERROR2(" \u{1F5D1}\uFE0F Dismiss"), value: "dismiss" },
818
+ { name: MUTED2(" \u2190 Back to list"), value: "back" }
786
819
  ]
787
820
  }
788
821
  ]);
@@ -790,12 +823,12 @@ async function showExpandedView(item, category) {
790
823
  }
791
824
  async function handleImplement(item, config, category) {
792
825
  console.log("");
793
- console.log(chalk3.cyan(" \u{1F527} Implementing fix..."));
826
+ console.log(INFO2(" \u{1F527} Implementing fix..."));
794
827
  console.log("");
795
828
  if (config.provider === "local" && config.localEndpoint) {
796
829
  const fileContent = readFileContent(item.filePath);
797
830
  if (!fileContent) {
798
- console.log(chalk3.red(` \u274C Could not read file: ${item.filePath}`));
831
+ console.log(ERROR2(` \u274C Could not read file: ${item.filePath}`));
799
832
  return;
800
833
  }
801
834
  const prompt = `You are MiniBob \u2014 a junior engineer making SURGICAL code fixes under strict supervision.
@@ -827,7 +860,8 @@ Return the complete file content now:`;
827
860
  { role: "system", content: "You are MiniBob, a junior engineer making SURGICAL fixes. Return ONLY valid source code. NO markdown. NO code fences. NO explanation. Start with // File: comment. Make the ABSOLUTE MINIMUM change needed. Do NOT restructure, refactor, or touch ANYTHING beyond the specific fix. If unsure, return the file unchanged." },
828
861
  { role: "user", content: prompt }
829
862
  ];
830
- const response = await callLocalModel(config.localEndpoint, messages);
863
+ const localResult = await callLocalModel(config.localEndpoint, messages);
864
+ const response = typeof localResult === "object" && localResult.text ? localResult.text : localResult;
831
865
  const lines = response.split("\n");
832
866
  const firstLine = lines[0].trim();
833
867
  let newContent;
@@ -837,25 +871,26 @@ Return the complete file content now:`;
837
871
  newContent = response.trim();
838
872
  }
839
873
  if (newContent.includes("```") || newContent.includes("## ") || newContent.startsWith("Here") || newContent.startsWith("I have") || newContent.startsWith("Sure")) {
840
- console.log(chalk3.yellow(" \u26A0\uFE0F MiniBob returned explanation instead of code. Fix rejected."));
874
+ console.log(WARNING2(" \u26A0\uFE0F MiniBob returned explanation instead of code. Fix rejected."));
841
875
  return;
842
876
  }
843
877
  if (newContent.length < fileContent.length * 0.5) {
844
- console.log(chalk3.yellow(` \u26A0\uFE0F MiniBob's output is ${Math.round(newContent.length / fileContent.length * 100)}% of original size. Rejecting.`));
878
+ console.log(WARNING2(` \u26A0\uFE0F MiniBob's output is ${Math.round(newContent.length / fileContent.length * 100)}% of original size. Rejecting.`));
845
879
  return;
846
880
  }
847
881
  const originalExports = fileContent.match(/export\s+(function|class|const|interface|type|async\s+function)\s+\w+/g) || [];
848
882
  for (const exp of originalExports) {
849
883
  const exportName = exp.split(/\s+/).pop();
850
884
  if (!newContent.includes(exportName)) {
851
- console.log(chalk3.yellow(` \u26A0\uFE0F MiniBob removed export "${exportName}". Rejecting.`));
885
+ console.log(WARNING2(` \u26A0\uFE0F MiniBob removed export "${exportName}". Rejecting.`));
852
886
  return;
853
887
  }
854
888
  }
855
889
  await proposeAndWriteFile({
856
890
  filePath: item.filePath,
857
891
  content: newContent,
858
- isNew: false
892
+ isNew: false,
893
+ isLocal: true
859
894
  });
860
895
  if (item.id) {
861
896
  markSuggestionById(item.id, category, "implemented", {
@@ -864,7 +899,7 @@ Return the complete file content now:`;
864
899
  });
865
900
  }
866
901
  } catch (error) {
867
- console.log(chalk3.red(` \u274C Implementation failed: ${error.message}`));
902
+ console.log(ERROR2(` \u274C Implementation failed: ${error.message}`));
868
903
  }
869
904
  } else if (config.loggedIn && config.conversationId) {
870
905
  try {
@@ -876,7 +911,7 @@ Return the complete file content now:`;
876
911
  jobId: `cli_impl_${Date.now()}`
877
912
  });
878
913
  if (result?.success) {
879
- console.log(chalk3.green(` \u2705 ${result.message}`));
914
+ console.log(SUCCESS2(` \u2705 ${result.message}`));
880
915
  if (item.id) {
881
916
  markSuggestionById(item.id, category, "implemented", {
882
917
  reason: "Platform implementation",
@@ -884,13 +919,13 @@ Return the complete file content now:`;
884
919
  });
885
920
  }
886
921
  } else {
887
- console.log(chalk3.red(" \u274C Implementation failed on platform."));
922
+ console.log(ERROR2(" \u274C Implementation failed on platform."));
888
923
  }
889
924
  } catch (error) {
890
- console.log(chalk3.red(` \u274C ${error.message}`));
925
+ console.log(ERROR2(` \u274C ${error.message}`));
891
926
  }
892
927
  } else {
893
- console.log(chalk3.red(" \u274C No provider configured for implementation."));
928
+ console.log(ERROR2(" \u274C No provider configured for implementation."));
894
929
  }
895
930
  console.log("");
896
931
  }
@@ -954,6 +989,7 @@ export {
954
989
  callLocalModel,
955
990
  buildLocalContext,
956
991
  readFileContent,
992
+ extractAllProposedFiles,
957
993
  extractProposedFile,
958
994
  stripCodeBlockFromResponse,
959
995
  processAllProposedFiles,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobsworkshop/cli",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "Bob's CLI — AI coding assistant and Forge orchestrator",
5
5
  "type": "module",
6
6
  "files": [
@@ -38,6 +38,7 @@
38
38
  "cli-table3": "^0.6.5",
39
39
  "commander": "^15.0.0",
40
40
  "conf": "^15.1.0",
41
+ "diff": "^9.0.0",
41
42
  "inquirer": "^14.0.2",
42
43
  "marked": "^18.0.4",
43
44
  "marked-terminal": "^7.3.0",
@@ -47,6 +48,7 @@
47
48
  "ws": "^8.21.0"
48
49
  },
49
50
  "devDependencies": {
51
+ "@types/diff": "^8.0.0",
50
52
  "@types/node": "^25.9.1",
51
53
  "tsup": "^8.5.1",
52
54
  "typescript": "^6.0.3",
@@ -1,8 +0,0 @@
1
- import {
2
- loadLocalSuggestions,
3
- showInteractiveResults
4
- } from "./chunk-LHWBSCJ4.js";
5
- export {
6
- loadLocalSuggestions,
7
- showInteractiveResults
8
- };