blun-king-cli 1.7.0 → 2.0.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/bin/blun.js +264 -59
- package/package.json +1 -1
package/bin/blun.js
CHANGED
|
@@ -2018,7 +2018,7 @@ function showSlashMenu(filter) {
|
|
|
2018
2018
|
var menuLines = [];
|
|
2019
2019
|
show.forEach(function(c) {
|
|
2020
2020
|
var cmdPad = (c.cmd + " ".repeat(20)).slice(0, 18);
|
|
2021
|
-
menuLines.push(
|
|
2021
|
+
menuLines.push(" " + C.green + cmdPad + C.white + c.desc.slice(0, 55) + C.reset);
|
|
2022
2022
|
});
|
|
2023
2023
|
if (filtered.length > 8) {
|
|
2024
2024
|
menuLines.push(C.dim + " ... " + (filtered.length - 8) + " more" + C.reset);
|
|
@@ -2067,14 +2067,54 @@ async function main() {
|
|
|
2067
2067
|
}
|
|
2068
2068
|
|
|
2069
2069
|
// Interactive mode
|
|
2070
|
-
// Workdir selection (like Claude Code)
|
|
2071
|
-
|
|
2070
|
+
// Workdir selection (like Claude Code — trust prompt)
|
|
2071
|
+
config.workdir = process.cwd();
|
|
2072
|
+
|
|
2073
|
+
if (!process.argv.includes("--no-workdir-prompt") && !process.argv.includes("--trust")) {
|
|
2072
2074
|
var cwdHasBlun = fs.existsSync(path.join(process.cwd(), ".blun"));
|
|
2073
2075
|
var cwdHasGit = fs.existsSync(path.join(process.cwd(), ".git"));
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
+
var trustedDirs = [];
|
|
2077
|
+
var trustFile = path.join(CONFIG_DIR, "trusted.json");
|
|
2078
|
+
if (fs.existsSync(trustFile)) {
|
|
2079
|
+
try { trustedDirs = JSON.parse(fs.readFileSync(trustFile, "utf8")); } catch(e) {}
|
|
2080
|
+
}
|
|
2081
|
+
var isTrusted = trustedDirs.includes(process.cwd());
|
|
2082
|
+
|
|
2083
|
+
if (!isTrusted && (cwdHasBlun || cwdHasGit)) {
|
|
2084
|
+
console.log("");
|
|
2085
|
+
console.log(C.yellow + " " + BOX.bot + " Working in: " + C.brightWhite + process.cwd() + C.reset);
|
|
2086
|
+
console.log(C.gray + " Do you trust this folder? BLUN King will read/write files here." + C.reset);
|
|
2087
|
+
console.log(C.gray + " [y] Yes, trust [n] Choose another folder [a] Always trust" + C.reset);
|
|
2088
|
+
|
|
2089
|
+
var trustAnswer = await new Promise(function(resolve) {
|
|
2090
|
+
process.stdin.setRawMode(true);
|
|
2091
|
+
process.stdin.resume();
|
|
2092
|
+
process.stdin.setEncoding("utf8");
|
|
2093
|
+
process.stdin.once("data", function(key) {
|
|
2094
|
+
process.stdin.setRawMode(false);
|
|
2095
|
+
resolve(key.toLowerCase());
|
|
2096
|
+
});
|
|
2097
|
+
});
|
|
2098
|
+
|
|
2099
|
+
if (trustAnswer === "a") {
|
|
2100
|
+
trustedDirs.push(process.cwd());
|
|
2101
|
+
fs.writeFileSync(trustFile, JSON.stringify(trustedDirs, null, 2));
|
|
2102
|
+
printSuccess("Folder trusted permanently.");
|
|
2103
|
+
} else if (trustAnswer === "n") {
|
|
2104
|
+
var rlDir = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
2105
|
+
var newDir = await new Promise(function(resolve) {
|
|
2106
|
+
rlDir.question(C.brightBlue + " Enter path: " + C.reset, resolve);
|
|
2107
|
+
});
|
|
2108
|
+
rlDir.close();
|
|
2109
|
+
if (newDir.trim() && fs.existsSync(newDir.trim())) {
|
|
2110
|
+
config.workdir = newDir.trim();
|
|
2111
|
+
} else {
|
|
2112
|
+
printError("Invalid path, using current directory.");
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
// y or anything else = trust for this session
|
|
2116
|
+
console.log("");
|
|
2076
2117
|
}
|
|
2077
|
-
// Show workdir in header
|
|
2078
2118
|
}
|
|
2079
2119
|
|
|
2080
2120
|
printHeader();
|
|
@@ -2104,76 +2144,241 @@ async function main() {
|
|
|
2104
2144
|
"/compact", "/review", "/cost", "/exit"
|
|
2105
2145
|
];
|
|
2106
2146
|
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2147
|
+
// ── Bordered Input Box + Live Slash Menu ──
|
|
2148
|
+
var inputBuffer = "";
|
|
2149
|
+
var inputHistory = [];
|
|
2150
|
+
var historyIdx = -1;
|
|
2151
|
+
var menuVisible = false;
|
|
2152
|
+
var menuItems = [];
|
|
2153
|
+
var menuSelected = 0;
|
|
2154
|
+
var cursorPos = 0;
|
|
2155
|
+
var processing = false;
|
|
2156
|
+
|
|
2157
|
+
function getTermWidth() { return process.stdout.columns || 80; }
|
|
2158
|
+
|
|
2159
|
+
function renderInputBox() {
|
|
2160
|
+
var w = Math.min(getTermWidth() - 4, 76);
|
|
2161
|
+
var displayText = inputBuffer;
|
|
2162
|
+
if (displayText.length > w - 4) displayText = displayText.slice(-(w - 4));
|
|
2163
|
+
var pad = Math.max(0, w - 2 - displayText.length);
|
|
2164
|
+
process.stdout.write("\r\x1b[K"); // clear line
|
|
2165
|
+
process.stdout.write(C.brightBlue + " " + BOX.tl + BOX.h.repeat(w - 2) + BOX.tr + C.reset + "\n");
|
|
2166
|
+
process.stdout.write(C.brightBlue + " " + BOX.v + " " + C.reset + C.brightWhite + displayText + C.reset + " ".repeat(pad) + C.brightBlue + BOX.v + C.reset + "\n");
|
|
2167
|
+
process.stdout.write(C.brightBlue + " " + BOX.bl + BOX.h.repeat(w - 2) + BOX.br + C.reset);
|
|
2168
|
+
// Move cursor back to input position
|
|
2169
|
+
process.stdout.write("\x1b[1A"); // up 1
|
|
2170
|
+
process.stdout.write("\r\x1b[" + (4 + displayText.length) + "C"); // position cursor
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
function clearInputBox() {
|
|
2174
|
+
process.stdout.write("\r\x1b[K"); // clear current line
|
|
2175
|
+
process.stdout.write("\x1b[1B\r\x1b[K"); // clear line below
|
|
2176
|
+
process.stdout.write("\x1b[1A\r"); // back up
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
function renderMenu() {
|
|
2180
|
+
if (!menuVisible || menuItems.length === 0) return;
|
|
2181
|
+
// Save cursor, move below input box, render menu
|
|
2182
|
+
var show = menuItems.slice(0, 8);
|
|
2183
|
+
process.stdout.write("\x1b[s"); // save cursor
|
|
2184
|
+
process.stdout.write("\n\n"); // below input box
|
|
2185
|
+
show.forEach(function(item, i) {
|
|
2186
|
+
var prefix = i === menuSelected ? C.green + C.bold + " " + BOX.arrow + " " : " ";
|
|
2187
|
+
var cmdStr = (item.cmd + " ".repeat(20)).slice(0, 18);
|
|
2188
|
+
var descStr = item.desc.slice(0, getTermWidth() - 28);
|
|
2189
|
+
var cmdColor = i === menuSelected ? C.green + C.bold : C.green;
|
|
2190
|
+
var descColor = i === menuSelected ? C.brightWhite : C.white;
|
|
2191
|
+
process.stdout.write(prefix + cmdColor + cmdStr + descColor + descStr + C.reset + "\x1b[K\n");
|
|
2192
|
+
});
|
|
2193
|
+
if (menuItems.length > 8) {
|
|
2194
|
+
process.stdout.write(C.dim + " ... " + (menuItems.length - 8) + " more" + C.reset + "\x1b[K\n");
|
|
2195
|
+
}
|
|
2196
|
+
process.stdout.write("\x1b[u"); // restore cursor
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
function clearMenu() {
|
|
2200
|
+
if (!menuVisible) return;
|
|
2201
|
+
var lines = Math.min(menuItems.length, 8) + (menuItems.length > 8 ? 1 : 0) + 2;
|
|
2202
|
+
process.stdout.write("\x1b[s"); // save
|
|
2203
|
+
for (var i = 0; i < lines; i++) {
|
|
2204
|
+
process.stdout.write("\n\x1b[K");
|
|
2205
|
+
}
|
|
2206
|
+
process.stdout.write("\x1b[u"); // restore
|
|
2207
|
+
menuVisible = false;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
function updateMenu() {
|
|
2211
|
+
if (inputBuffer.startsWith("/")) {
|
|
2212
|
+
var filter = inputBuffer.toLowerCase();
|
|
2213
|
+
menuItems = COMMAND_DESCRIPTIONS.filter(function(c) {
|
|
2214
|
+
return c.cmd.includes(filter) || c.desc.toLowerCase().includes(filter.slice(1));
|
|
2215
|
+
});
|
|
2216
|
+
if (menuItems.length > 0) {
|
|
2217
|
+
menuVisible = true;
|
|
2218
|
+
menuSelected = Math.min(menuSelected, menuItems.length - 1);
|
|
2219
|
+
renderMenu();
|
|
2220
|
+
} else {
|
|
2221
|
+
clearMenu();
|
|
2115
2222
|
}
|
|
2116
|
-
|
|
2223
|
+
} else {
|
|
2224
|
+
if (menuVisible) clearMenu();
|
|
2117
2225
|
}
|
|
2118
|
-
}
|
|
2226
|
+
}
|
|
2119
2227
|
|
|
2120
|
-
|
|
2228
|
+
function drawPrompt() {
|
|
2229
|
+
if (processing) return;
|
|
2230
|
+
console.log(""); // spacing
|
|
2231
|
+
renderInputBox();
|
|
2232
|
+
updateMenu();
|
|
2233
|
+
}
|
|
2121
2234
|
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2235
|
+
async function processInput(input) {
|
|
2236
|
+
processing = true;
|
|
2237
|
+
clearMenu();
|
|
2238
|
+
clearInputBox();
|
|
2239
|
+
// Move below the box area
|
|
2240
|
+
process.stdout.write("\x1b[2B\r\x1b[K\x1b[1A\r\x1b[K\x1b[1A\r\x1b[K");
|
|
2125
2241
|
|
|
2126
|
-
// ! prefix = direct bash
|
|
2127
2242
|
if (input.startsWith("!")) {
|
|
2128
2243
|
cmdShell(input.slice(1).trim());
|
|
2129
|
-
}
|
|
2130
|
-
// # prefix = memory shortcut
|
|
2131
|
-
else if (input.startsWith("#save ")) {
|
|
2244
|
+
} else if (input.startsWith("#save ")) {
|
|
2132
2245
|
var memParts = input.slice(6).split(/\s+/);
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
fs.writeFileSync(path.join(MEMORY_DIR, memKey + ".txt"), memVal);
|
|
2137
|
-
printSuccess("Memory saved: " + memKey);
|
|
2246
|
+
if (memParts[0] && memParts.length > 1) {
|
|
2247
|
+
fs.writeFileSync(path.join(MEMORY_DIR, memParts[0] + ".txt"), memParts.slice(1).join(" "));
|
|
2248
|
+
printSuccess("Memory saved: " + memParts[0]);
|
|
2138
2249
|
}
|
|
2250
|
+
} else if (input.startsWith("#")) {
|
|
2251
|
+
var memKey = input.slice(1).trim();
|
|
2252
|
+
var memFile = path.join(MEMORY_DIR, memKey + ".txt");
|
|
2253
|
+
if (fs.existsSync(memFile)) console.log(C.brightCyan + fs.readFileSync(memFile, "utf8") + C.reset);
|
|
2254
|
+
else printError("Memory not found: " + memKey);
|
|
2255
|
+
} else if (input.startsWith("/")) {
|
|
2256
|
+
runHook("pre", input.split(/\s+/)[0].slice(1));
|
|
2257
|
+
await handleCommand(input);
|
|
2258
|
+
runHook("post", input.split(/\s+/)[0].slice(1));
|
|
2259
|
+
} else {
|
|
2260
|
+
await sendChat(input);
|
|
2139
2261
|
}
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2262
|
+
|
|
2263
|
+
inputBuffer = "";
|
|
2264
|
+
cursorPos = 0;
|
|
2265
|
+
menuSelected = 0;
|
|
2266
|
+
processing = false;
|
|
2267
|
+
drawPrompt();
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
// ── Raw Input Handler ──
|
|
2271
|
+
process.stdin.setRawMode(true);
|
|
2272
|
+
process.stdin.resume();
|
|
2273
|
+
process.stdin.setEncoding("utf8");
|
|
2274
|
+
|
|
2275
|
+
drawPrompt();
|
|
2276
|
+
|
|
2277
|
+
process.stdin.on("data", function(key) {
|
|
2278
|
+
if (processing) return;
|
|
2279
|
+
|
|
2280
|
+
// Ctrl+C
|
|
2281
|
+
if (key === "\x03") {
|
|
2282
|
+
session.history = chatHistory.slice(-50);
|
|
2283
|
+
saveSessionHistory(session);
|
|
2284
|
+
clearMenu();
|
|
2285
|
+
console.log(C.dim + "\nBye." + C.reset);
|
|
2286
|
+
process.exit(0);
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
// Ctrl+D
|
|
2290
|
+
if (key === "\x04") {
|
|
2291
|
+
session.history = chatHistory.slice(-50);
|
|
2292
|
+
saveSessionHistory(session);
|
|
2293
|
+
clearMenu();
|
|
2294
|
+
console.log(C.dim + "\nBye." + C.reset);
|
|
2295
|
+
process.exit(0);
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
// Enter
|
|
2299
|
+
if (key === "\r" || key === "\n") {
|
|
2300
|
+
var input = inputBuffer.trim();
|
|
2301
|
+
// If menu is visible and a command is selected, use that
|
|
2302
|
+
if (menuVisible && menuItems.length > 0 && inputBuffer.startsWith("/") && !inputBuffer.includes(" ")) {
|
|
2303
|
+
input = menuItems[menuSelected].cmd;
|
|
2304
|
+
}
|
|
2305
|
+
if (!input) return;
|
|
2306
|
+
inputHistory.unshift(input);
|
|
2307
|
+
historyIdx = -1;
|
|
2308
|
+
processInput(input);
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
// Tab — autocomplete from menu
|
|
2313
|
+
if (key === "\t") {
|
|
2314
|
+
if (menuVisible && menuItems.length > 0) {
|
|
2315
|
+
inputBuffer = menuItems[menuSelected].cmd + " ";
|
|
2316
|
+
cursorPos = inputBuffer.length;
|
|
2317
|
+
clearMenu();
|
|
2318
|
+
renderInputBox();
|
|
2319
|
+
updateMenu();
|
|
2147
2320
|
}
|
|
2321
|
+
return;
|
|
2148
2322
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2323
|
+
|
|
2324
|
+
// Backspace
|
|
2325
|
+
if (key === "\x7f" || key === "\b") {
|
|
2326
|
+
if (inputBuffer.length > 0) {
|
|
2327
|
+
inputBuffer = inputBuffer.slice(0, -1);
|
|
2328
|
+
cursorPos = inputBuffer.length;
|
|
2329
|
+
clearMenu();
|
|
2330
|
+
renderInputBox();
|
|
2331
|
+
updateMenu();
|
|
2332
|
+
}
|
|
2333
|
+
return;
|
|
2152
2334
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
if (
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2335
|
+
|
|
2336
|
+
// Arrow keys (escape sequences)
|
|
2337
|
+
if (key === "\x1b[A") { // Up
|
|
2338
|
+
if (menuVisible && menuItems.length > 0) {
|
|
2339
|
+
menuSelected = Math.max(0, menuSelected - 1);
|
|
2340
|
+
clearMenu();
|
|
2341
|
+
renderMenu();
|
|
2342
|
+
} else if (inputHistory.length > 0) {
|
|
2343
|
+
historyIdx = Math.min(historyIdx + 1, inputHistory.length - 1);
|
|
2344
|
+
inputBuffer = inputHistory[historyIdx];
|
|
2345
|
+
cursorPos = inputBuffer.length;
|
|
2346
|
+
renderInputBox();
|
|
2162
2347
|
}
|
|
2348
|
+
return;
|
|
2163
2349
|
}
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2350
|
+
if (key === "\x1b[B") { // Down
|
|
2351
|
+
if (menuVisible && menuItems.length > 0) {
|
|
2352
|
+
menuSelected = Math.min(menuItems.length - 1, menuSelected + 1);
|
|
2353
|
+
clearMenu();
|
|
2354
|
+
renderMenu();
|
|
2355
|
+
} else if (historyIdx > 0) {
|
|
2356
|
+
historyIdx--;
|
|
2357
|
+
inputBuffer = inputHistory[historyIdx];
|
|
2358
|
+
cursorPos = inputBuffer.length;
|
|
2359
|
+
renderInputBox();
|
|
2360
|
+
}
|
|
2361
|
+
return;
|
|
2167
2362
|
}
|
|
2168
|
-
rl.prompt();
|
|
2169
|
-
});
|
|
2170
2363
|
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2364
|
+
// Escape — close menu
|
|
2365
|
+
if (key === "\x1b" || key === "\x1b\x1b") {
|
|
2366
|
+
if (menuVisible) {
|
|
2367
|
+
clearMenu();
|
|
2368
|
+
menuVisible = false;
|
|
2369
|
+
}
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
// Ignore other escape sequences
|
|
2374
|
+
if (key.startsWith("\x1b")) return;
|
|
2375
|
+
|
|
2376
|
+
// Regular character
|
|
2377
|
+
inputBuffer += key;
|
|
2378
|
+
cursorPos = inputBuffer.length;
|
|
2379
|
+
clearMenu();
|
|
2380
|
+
renderInputBox();
|
|
2381
|
+
updateMenu();
|
|
2177
2382
|
});
|
|
2178
2383
|
}
|
|
2179
2384
|
|