blun-king-cli 5.1.7 → 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.
- package/blun-cli.js +42 -98
- package/package.json +1 -1
package/blun-cli.js
CHANGED
|
@@ -2604,84 +2604,71 @@ async function main() {
|
|
|
2604
2604
|
|
|
2605
2605
|
if (!isTrusted) {
|
|
2606
2606
|
console.log("");
|
|
2607
|
-
console.log(C.yellow +
|
|
2608
|
-
console.log(C.gray + "
|
|
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: "
|
|
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[
|
|
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 +
|
|
2623
|
+
process.stdout.write("\x1b[2K" + prefix + color + opt.label + "\x1b[0m\n");
|
|
2624
2624
|
});
|
|
2625
2625
|
}
|
|
2626
|
-
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(
|
|
2634
|
-
if (key === "
|
|
2635
|
-
|
|
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
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
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
2655
|
// API Key check — prompt if missing (BEFORE header, so user sees it immediately)
|
|
2680
|
-
|
|
2681
|
-
|
|
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) {
|
|
2682
2666
|
console.log("");
|
|
2683
|
-
console.log(C.yellow +
|
|
2684
|
-
|
|
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);
|
|
2685
2672
|
process.stdout.write(C.brightBlue + " API Key: " + C.reset);
|
|
2686
2673
|
var apiKeyAnswer = await new Promise(function(resolve) {
|
|
2687
2674
|
var buf = "";
|
|
@@ -2693,6 +2680,7 @@ async function main() {
|
|
|
2693
2680
|
if (key === "\r" || key === "\n") {
|
|
2694
2681
|
process.stdin.removeListener("data", onKeyApi);
|
|
2695
2682
|
process.stdin.setRawMode(false);
|
|
2683
|
+
process.stdin.pause();
|
|
2696
2684
|
process.stdout.write("\n");
|
|
2697
2685
|
resolve(buf.trim());
|
|
2698
2686
|
return;
|
|
@@ -2709,15 +2697,10 @@ async function main() {
|
|
|
2709
2697
|
if (apiKeyAnswer) {
|
|
2710
2698
|
config.api.key = apiKeyAnswer;
|
|
2711
2699
|
config.auth.api_key = apiKeyAnswer;
|
|
2712
|
-
|
|
2713
|
-
var cfgData = {};
|
|
2714
|
-
if (fs.existsSync(cfgFile)) { try { cfgData = JSON.parse(fs.readFileSync(cfgFile, "utf8")); } catch(e) {} }
|
|
2715
|
-
if (!cfgData.api) cfgData.api = {};
|
|
2716
|
-
if (!cfgData.auth) cfgData.auth = {};
|
|
2717
|
-
cfgData.api.key = apiKeyAnswer;
|
|
2718
|
-
cfgData.auth.api_key = apiKeyAnswer;
|
|
2719
|
-
fs.writeFileSync(cfgFile, JSON.stringify(cfgData, null, 2));
|
|
2700
|
+
saveConfig(config);
|
|
2720
2701
|
printSuccess("API key saved.");
|
|
2702
|
+
} else {
|
|
2703
|
+
printError("No API key — some features won't work.");
|
|
2721
2704
|
}
|
|
2722
2705
|
}
|
|
2723
2706
|
|
|
@@ -2738,47 +2721,8 @@ async function main() {
|
|
|
2738
2721
|
printInfo("Run: blun setup — to configure");
|
|
2739
2722
|
}
|
|
2740
2723
|
|
|
2741
|
-
//
|
|
2742
|
-
var sessionMode = "
|
|
2743
|
-
if (!process.argv.includes("--agent") && !process.argv.includes("--chat")) {
|
|
2744
|
-
console.log("");
|
|
2745
|
-
console.log(C.brightWhite + C.bold + " How do you want to work?" + C.reset);
|
|
2746
|
-
console.log("");
|
|
2747
|
-
var modeOptions = [
|
|
2748
|
-
{ label: "Chat \u2014 talk back and forth", mode: "chat" },
|
|
2749
|
-
{ label: "Agent \u2014 give a goal, I do the rest autonomously", mode: "agent" }
|
|
2750
|
-
];
|
|
2751
|
-
var modeSel = 0;
|
|
2752
|
-
var modeChoice = await new Promise(function(resolve) {
|
|
2753
|
-
function render() {
|
|
2754
|
-
process.stdout.write("\x1b[2A\r");
|
|
2755
|
-
modeOptions.forEach(function(opt, i) {
|
|
2756
|
-
var prefix = i === modeSel ? "\x1b[32m\x1b[1m \u276F " : " ";
|
|
2757
|
-
var color = i === modeSel ? "\x1b[97m\x1b[1m" : "\x1b[90m";
|
|
2758
|
-
process.stdout.write("\x1b[2K" + prefix + color + (i + 1) + ". " + opt.label + "\x1b[0m\n");
|
|
2759
|
-
});
|
|
2760
|
-
}
|
|
2761
|
-
console.log(""); console.log("");
|
|
2762
|
-
render();
|
|
2763
|
-
process.stdin.setRawMode(true);
|
|
2764
|
-
process.stdin.resume();
|
|
2765
|
-
process.stdin.setEncoding("utf8");
|
|
2766
|
-
function onKey(key) {
|
|
2767
|
-
if (key === "\x1b[A") { modeSel = 0; render(); return; }
|
|
2768
|
-
if (key === "\x1b[B") { modeSel = 1; render(); return; }
|
|
2769
|
-
if (key === "1") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve("chat"); return; }
|
|
2770
|
-
if (key === "2") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve("agent"); return; }
|
|
2771
|
-
if (key === "\r" || key === "\n") { process.stdin.removeListener("data", onKey); process.stdin.setRawMode(false); resolve(modeOptions[modeSel].mode); return; }
|
|
2772
|
-
if (key === "\x03") { process.exit(0); }
|
|
2773
|
-
}
|
|
2774
|
-
process.stdin.on("data", onKey);
|
|
2775
|
-
});
|
|
2776
|
-
sessionMode = modeChoice;
|
|
2777
|
-
printSuccess("Mode: " + (sessionMode === "agent" ? "Agent (autonomous)" : "Chat (interactive)"));
|
|
2778
|
-
console.log("");
|
|
2779
|
-
} else {
|
|
2780
|
-
sessionMode = process.argv.includes("--agent") ? "agent" : "chat";
|
|
2781
|
-
}
|
|
2724
|
+
// Auto-detect mode from input (no startup selection — like Claude Code)
|
|
2725
|
+
var sessionMode = process.argv.includes("--agent") ? "agent" : "chat";
|
|
2782
2726
|
|
|
2783
2727
|
var ALL_COMMANDS = [
|
|
2784
2728
|
"/help", "/clear", "/skills", "/skill", "/search", "/learn", "/learn-url",
|