blun-king-cli 5.1.7 → 5.2.1
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 +44 -106
- package/package.json +1 -1
package/blun-cli.js
CHANGED
|
@@ -2595,93 +2595,74 @@ async function main() {
|
|
|
2595
2595
|
}
|
|
2596
2596
|
|
|
2597
2597
|
if (!process.argv.includes("--no-workdir-prompt") && !process.argv.includes("--trust")) {
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
if (fs.existsSync(trustFile)) {
|
|
2601
|
-
try { trustedDirs = JSON.parse(fs.readFileSync(trustFile, "utf8")); } catch(e) {}
|
|
2602
|
-
}
|
|
2603
|
-
var isTrusted = trustedDirs.includes(process.cwd());
|
|
2604
|
-
|
|
2605
|
-
if (!isTrusted) {
|
|
2598
|
+
// Always ask — like Claude Code (every session)
|
|
2599
|
+
{
|
|
2606
2600
|
console.log("");
|
|
2607
|
-
console.log(C.yellow +
|
|
2608
|
-
console.log(C.gray + "
|
|
2601
|
+
console.log(C.yellow + C.bold + " Quick safety check:" + C.reset);
|
|
2602
|
+
console.log(C.gray + " Is " + C.brightWhite + process.cwd() + C.gray + " a project you trust?" + C.reset);
|
|
2603
|
+
console.log(C.dim + " BLUN King will read and write files in this folder." + C.reset);
|
|
2609
2604
|
console.log("");
|
|
2610
2605
|
|
|
2611
2606
|
var trustOptions = [
|
|
2612
|
-
{ label: "Yes, trust this folder", action: "trust" },
|
|
2613
|
-
{ label: "
|
|
2614
|
-
{ label: "Choose another folder", action: "choose" }
|
|
2607
|
+
{ label: "Yes, I trust this folder", action: "trust" },
|
|
2608
|
+
{ label: "No, exit", action: "exit" }
|
|
2615
2609
|
];
|
|
2616
2610
|
var trustSel = 0;
|
|
2617
2611
|
var trustResult = await new Promise(function(resolve) {
|
|
2618
2612
|
function render() {
|
|
2619
|
-
process.stdout.write("\x1b[
|
|
2613
|
+
process.stdout.write("\x1b[2A\r");
|
|
2620
2614
|
trustOptions.forEach(function(opt, i) {
|
|
2621
2615
|
var prefix = i === trustSel ? "\x1b[32m\x1b[1m \u276F " : " ";
|
|
2622
2616
|
var color = i === trustSel ? "\x1b[97m\x1b[1m" : "\x1b[90m";
|
|
2623
|
-
process.stdout.write("\x1b[2K" + prefix + color +
|
|
2617
|
+
process.stdout.write("\x1b[2K" + prefix + color + opt.label + "\x1b[0m\n");
|
|
2624
2618
|
});
|
|
2625
2619
|
}
|
|
2626
|
-
console.log(""); console.log("");
|
|
2620
|
+
console.log(""); console.log("");
|
|
2627
2621
|
render();
|
|
2628
2622
|
process.stdin.setRawMode(true);
|
|
2629
2623
|
process.stdin.resume();
|
|
2630
2624
|
process.stdin.setEncoding("utf8");
|
|
2631
2625
|
function onKey(key) {
|
|
2632
2626
|
if (key === "\x1b[A") { trustSel = Math.max(0, trustSel - 1); render(); return; }
|
|
2633
|
-
if (key === "\x1b[B") { trustSel = Math.min(
|
|
2634
|
-
if (key === "
|
|
2635
|
-
|
|
2627
|
+
if (key === "\x1b[B") { trustSel = Math.min(1, trustSel + 1); render(); return; }
|
|
2628
|
+
if (key === "\r" || key === "\n") {
|
|
2629
|
+
process.stdin.removeListener("data", onKey);
|
|
2630
|
+
process.stdin.setRawMode(false);
|
|
2631
|
+
process.stdin.pause();
|
|
2632
|
+
resolve(trustOptions[trustSel]);
|
|
2633
|
+
return;
|
|
2634
|
+
}
|
|
2636
2635
|
if (key === "\x03") { process.exit(0); }
|
|
2637
2636
|
}
|
|
2638
2637
|
process.stdin.on("data", onKey);
|
|
2639
2638
|
});
|
|
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
|
-
}
|
|
2639
|
+
|
|
2640
|
+
if (trustResult.action === "exit") {
|
|
2641
|
+
console.log("");
|
|
2642
|
+
printInfo("Bye!");
|
|
2643
|
+
process.exit(0);
|
|
2673
2644
|
}
|
|
2674
|
-
// trust = trust for this session only
|
|
2675
2645
|
console.log("");
|
|
2676
2646
|
}
|
|
2677
2647
|
}
|
|
2678
2648
|
|
|
2679
2649
|
// API Key check — prompt if missing (BEFORE header, so user sees it immediately)
|
|
2680
|
-
|
|
2681
|
-
|
|
2650
|
+
// Test if key actually works by hitting /health
|
|
2651
|
+
var hasKey = false;
|
|
2652
|
+
var existingKey = config.api.key || config.auth.api_key || "";
|
|
2653
|
+
if (existingKey.length > 3) {
|
|
2654
|
+
try {
|
|
2655
|
+
var testH = await apiCall("GET", "/health");
|
|
2656
|
+
hasKey = (testH.status === 200);
|
|
2657
|
+
} catch(e) { hasKey = false; }
|
|
2658
|
+
}
|
|
2659
|
+
if (!hasKey) {
|
|
2682
2660
|
console.log("");
|
|
2683
|
-
console.log(C.yellow +
|
|
2684
|
-
|
|
2661
|
+
console.log(C.yellow + C.bold + " API Key required" + C.reset);
|
|
2662
|
+
if (existingKey.length > 3) {
|
|
2663
|
+
console.log(C.red + " Saved key didn't work — please re-enter." + C.reset);
|
|
2664
|
+
}
|
|
2665
|
+
console.log(C.gray + " Enter your BLUN King API key:" + C.reset);
|
|
2685
2666
|
process.stdout.write(C.brightBlue + " API Key: " + C.reset);
|
|
2686
2667
|
var apiKeyAnswer = await new Promise(function(resolve) {
|
|
2687
2668
|
var buf = "";
|
|
@@ -2693,6 +2674,7 @@ async function main() {
|
|
|
2693
2674
|
if (key === "\r" || key === "\n") {
|
|
2694
2675
|
process.stdin.removeListener("data", onKeyApi);
|
|
2695
2676
|
process.stdin.setRawMode(false);
|
|
2677
|
+
process.stdin.pause();
|
|
2696
2678
|
process.stdout.write("\n");
|
|
2697
2679
|
resolve(buf.trim());
|
|
2698
2680
|
return;
|
|
@@ -2709,15 +2691,10 @@ async function main() {
|
|
|
2709
2691
|
if (apiKeyAnswer) {
|
|
2710
2692
|
config.api.key = apiKeyAnswer;
|
|
2711
2693
|
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));
|
|
2694
|
+
saveConfig(config);
|
|
2720
2695
|
printSuccess("API key saved.");
|
|
2696
|
+
} else {
|
|
2697
|
+
printError("No API key — some features won't work.");
|
|
2721
2698
|
}
|
|
2722
2699
|
}
|
|
2723
2700
|
|
|
@@ -2738,47 +2715,8 @@ async function main() {
|
|
|
2738
2715
|
printInfo("Run: blun setup — to configure");
|
|
2739
2716
|
}
|
|
2740
2717
|
|
|
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
|
-
}
|
|
2718
|
+
// Auto-detect mode from input (no startup selection — like Claude Code)
|
|
2719
|
+
var sessionMode = process.argv.includes("--agent") ? "agent" : "chat";
|
|
2782
2720
|
|
|
2783
2721
|
var ALL_COMMANDS = [
|
|
2784
2722
|
"/help", "/clear", "/skills", "/skill", "/search", "/learn", "/learn-url",
|