blun-king-cli 5.1.6 → 5.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 (2) hide show
  1. package/blun-cli.js +46 -101
  2. package/package.json +1 -1
package/blun-cli.js CHANGED
@@ -2604,86 +2604,71 @@ async function main() {
2604
2604
 
2605
2605
  if (!isTrusted) {
2606
2606
  console.log("");
2607
- console.log(C.yellow + " " + BOX.bot + " Working in: " + C.brightWhite + process.cwd() + C.reset);
2608
- console.log(C.gray + " Do you trust this folder? BLUN King will read/write files here." + C.reset);
2607
+ console.log(C.yellow + C.bold + " Quick safety check:" + C.reset);
2608
+ console.log(C.gray + " Is " + C.brightWhite + process.cwd() + C.gray + " a project you trust?" + C.reset);
2609
+ console.log(C.dim + " BLUN King will read and write files in this folder." + C.reset);
2609
2610
  console.log("");
2610
2611
 
2611
2612
  var trustOptions = [
2612
- { label: "Yes, trust this folder", action: "trust" },
2613
- { label: "Always trust this folder", action: "always" },
2614
- { label: "Choose another folder", action: "choose" }
2613
+ { label: "Yes, I trust this folder", action: "trust" },
2614
+ { label: "No, exit", action: "exit" }
2615
2615
  ];
2616
2616
  var trustSel = 0;
2617
2617
  var trustResult = await new Promise(function(resolve) {
2618
2618
  function render() {
2619
- process.stdout.write("\x1b[3A\r");
2619
+ process.stdout.write("\x1b[2A\r");
2620
2620
  trustOptions.forEach(function(opt, i) {
2621
2621
  var prefix = i === trustSel ? "\x1b[32m\x1b[1m \u276F " : " ";
2622
2622
  var color = i === trustSel ? "\x1b[97m\x1b[1m" : "\x1b[90m";
2623
- process.stdout.write("\x1b[2K" + prefix + color + (i + 1) + ". " + opt.label + "\x1b[0m\n");
2623
+ process.stdout.write("\x1b[2K" + prefix + color + opt.label + "\x1b[0m\n");
2624
2624
  });
2625
2625
  }
2626
- console.log(""); console.log(""); console.log("");
2626
+ console.log(""); console.log("");
2627
2627
  render();
2628
2628
  process.stdin.setRawMode(true);
2629
2629
  process.stdin.resume();
2630
2630
  process.stdin.setEncoding("utf8");
2631
2631
  function onKey(key) {
2632
2632
  if (key === "\x1b[A") { trustSel = Math.max(0, trustSel - 1); render(); return; }
2633
- if (key === "\x1b[B") { trustSel = Math.min(2, trustSel + 1); render(); return; }
2634
- if (key === "1" || key === "2" || key === "3") { trustSel = parseInt(key) - 1; process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve(trustOptions[trustSel]); return; }
2635
- if (key === "\r" || key === "\n") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve(trustOptions[trustSel]); return; }
2633
+ if (key === "\x1b[B") { trustSel = Math.min(1, trustSel + 1); render(); return; }
2634
+ if (key === "\r" || key === "\n") {
2635
+ process.stdin.removeListener("data", onKey);
2636
+ process.stdin.setRawMode(false);
2637
+ process.stdin.pause();
2638
+ resolve(trustOptions[trustSel]);
2639
+ return;
2640
+ }
2636
2641
  if (key === "\x03") { process.exit(0); }
2637
2642
  }
2638
2643
  process.stdin.on("data", onKey);
2639
2644
  });
2640
- var trustChoice = trustResult.action;
2641
-
2642
- if (trustChoice === "always") {
2643
- trustedDirs.push(process.cwd());
2644
- fs.writeFileSync(trustFile, JSON.stringify(trustedDirs, null, 2));
2645
- printSuccess("Folder trusted permanently.");
2646
- } else if (trustChoice === "choose") {
2647
- // Open Windows folder picker if available
2648
- var newDir = null;
2649
- if (process.platform === "win32") {
2650
- try {
2651
- printInfo("Opening folder picker...");
2652
- var psCmd = 'powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; $f = New-Object System.Windows.Forms.FolderBrowserDialog; $f.Description = \'Choose BLUN King workspace\'; $f.ShowNewFolderButton = $true; if($f.ShowDialog() -eq \'OK\'){ Write-Output $f.SelectedPath } else { Write-Output \'CANCELLED\' }"';
2653
- newDir = require("child_process").execSync(psCmd, { encoding: "utf8", timeout: 60000 }).trim();
2654
- if (newDir === "CANCELLED") newDir = null;
2655
- } catch(e) { newDir = null; }
2656
- }
2657
- // Fallback to text input
2658
- if (!newDir) {
2659
- var rlDir = readline.createInterface({ input: process.stdin, output: process.stdout });
2660
- newDir = await new Promise(function(resolve) {
2661
- rlDir.question(C.brightBlue + " Enter path: " + C.reset, resolve);
2662
- });
2663
- rlDir.close();
2664
- newDir = newDir ? newDir.trim() : null;
2665
- }
2666
- if (newDir && fs.existsSync(newDir)) {
2667
- config.workdir = newDir;
2668
- try { fs.writeFileSync(path.join(CONFIG_DIR, "last-workdir.json"), JSON.stringify({ dir: config.workdir, ts: new Date().toISOString() })); } catch(e) {}
2669
- printSuccess("Workspace: " + config.workdir);
2670
- } else if (newDir) {
2671
- printError("Invalid path, using current directory.");
2672
- }
2645
+
2646
+ if (trustResult.action === "exit") {
2647
+ console.log("");
2648
+ printInfo("Bye!");
2649
+ process.exit(0);
2673
2650
  }
2674
- // trust = trust for this session only
2675
2651
  console.log("");
2676
2652
  }
2677
2653
  }
2678
2654
 
2679
- printHeader();
2680
- checkForUpdates();
2681
-
2682
- // API Key check prompt if missing (uses raw stdin since readline may not work after setRawMode)
2683
- if (!config.api.key && !config.auth.api_key && !process.argv.includes("--api-key")) {
2655
+ // API Key check — prompt if missing (BEFORE header, so user sees it immediately)
2656
+ // Test if key actually works by hitting /health
2657
+ var hasKey = false;
2658
+ var existingKey = config.api.key || config.auth.api_key || "";
2659
+ if (existingKey.length > 3) {
2660
+ try {
2661
+ var testH = await apiCall("GET", "/health");
2662
+ hasKey = (testH.status === 200);
2663
+ } catch(e) { hasKey = false; }
2664
+ }
2665
+ if (!hasKey) {
2684
2666
  console.log("");
2685
- console.log(C.yellow + " " + BOX.bot + " No API key configured." + C.reset);
2686
- console.log(C.gray + " Enter your BLUN King API key (or press Enter to skip):" + C.reset);
2667
+ console.log(C.yellow + C.bold + " API Key required" + C.reset);
2668
+ if (existingKey.length > 3) {
2669
+ console.log(C.red + " Saved key didn't work — please re-enter." + C.reset);
2670
+ }
2671
+ console.log(C.gray + " Enter your BLUN King API key:" + C.reset);
2687
2672
  process.stdout.write(C.brightBlue + " API Key: " + C.reset);
2688
2673
  var apiKeyAnswer = await new Promise(function(resolve) {
2689
2674
  var buf = "";
@@ -2695,6 +2680,7 @@ async function main() {
2695
2680
  if (key === "\r" || key === "\n") {
2696
2681
  process.stdin.removeListener("data", onKeyApi);
2697
2682
  process.stdin.setRawMode(false);
2683
+ process.stdin.pause();
2698
2684
  process.stdout.write("\n");
2699
2685
  resolve(buf.trim());
2700
2686
  return;
@@ -2711,18 +2697,16 @@ async function main() {
2711
2697
  if (apiKeyAnswer) {
2712
2698
  config.api.key = apiKeyAnswer;
2713
2699
  config.auth.api_key = apiKeyAnswer;
2714
- var cfgFile = path.join(CONFIG_DIR, "config.json");
2715
- var cfgData = {};
2716
- if (fs.existsSync(cfgFile)) { try { cfgData = JSON.parse(fs.readFileSync(cfgFile, "utf8")); } catch(e) {} }
2717
- if (!cfgData.api) cfgData.api = {};
2718
- if (!cfgData.auth) cfgData.auth = {};
2719
- cfgData.api.key = apiKeyAnswer;
2720
- cfgData.auth.api_key = apiKeyAnswer;
2721
- fs.writeFileSync(cfgFile, JSON.stringify(cfgData, null, 2));
2700
+ saveConfig(config);
2722
2701
  printSuccess("API key saved.");
2702
+ } else {
2703
+ printError("No API key — some features won't work.");
2723
2704
  }
2724
2705
  }
2725
2706
 
2707
+ printHeader();
2708
+ checkForUpdates();
2709
+
2726
2710
  // Load session history for this workdir
2727
2711
  var session = loadSessionHistory();
2728
2712
  chatHistory = session.history.slice(-20);
@@ -2737,47 +2721,8 @@ async function main() {
2737
2721
  printInfo("Run: blun setup — to configure");
2738
2722
  }
2739
2723
 
2740
- // Mode selection at startup
2741
- var sessionMode = "chat"; // chat or agent
2742
- if (!process.argv.includes("--agent") && !process.argv.includes("--chat")) {
2743
- console.log("");
2744
- console.log(C.brightWhite + C.bold + " How do you want to work?" + C.reset);
2745
- console.log("");
2746
- var modeOptions = [
2747
- { label: "Chat \u2014 talk back and forth", mode: "chat" },
2748
- { label: "Agent \u2014 give a goal, I do the rest autonomously", mode: "agent" }
2749
- ];
2750
- var modeSel = 0;
2751
- var modeChoice = await new Promise(function(resolve) {
2752
- function render() {
2753
- process.stdout.write("\x1b[2A\r");
2754
- modeOptions.forEach(function(opt, i) {
2755
- var prefix = i === modeSel ? "\x1b[32m\x1b[1m \u276F " : " ";
2756
- var color = i === modeSel ? "\x1b[97m\x1b[1m" : "\x1b[90m";
2757
- process.stdout.write("\x1b[2K" + prefix + color + (i + 1) + ". " + opt.label + "\x1b[0m\n");
2758
- });
2759
- }
2760
- console.log(""); console.log("");
2761
- render();
2762
- process.stdin.setRawMode(true);
2763
- process.stdin.resume();
2764
- process.stdin.setEncoding("utf8");
2765
- function onKey(key) {
2766
- if (key === "\x1b[A") { modeSel = 0; render(); return; }
2767
- if (key === "\x1b[B") { modeSel = 1; render(); return; }
2768
- if (key === "1") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve("chat"); return; }
2769
- if (key === "2") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve("agent"); return; }
2770
- if (key === "\r" || key === "\n") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve(modeOptions[modeSel].mode); return; }
2771
- if (key === "\x03") { process.exit(0); }
2772
- }
2773
- process.stdin.on("data", onKey);
2774
- });
2775
- sessionMode = modeChoice;
2776
- printSuccess("Mode: " + (sessionMode === "agent" ? "Agent (autonomous)" : "Chat (interactive)"));
2777
- console.log("");
2778
- } else {
2779
- sessionMode = process.argv.includes("--agent") ? "agent" : "chat";
2780
- }
2724
+ // Auto-detect mode from input (no startup selection like Claude Code)
2725
+ var sessionMode = process.argv.includes("--agent") ? "agent" : "chat";
2781
2726
 
2782
2727
  var ALL_COMMANDS = [
2783
2728
  "/help", "/clear", "/skills", "/skill", "/search", "/learn", "/learn-url",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "5.1.6",
3
+ "version": "5.2.0",
4
4
  "description": "BLUN King CLI — Your local AI assistant powered by Gemma4",
5
5
  "type": "commonjs",
6
6
  "bin": {