lazyclaw 3.99.8 → 3.99.10
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/README.md +1 -1
- package/cli.mjs +84 -26
- package/package.json +1 -1
package/README.md
CHANGED
package/cli.mjs
CHANGED
|
@@ -1521,6 +1521,13 @@ async function _arrowMenu({ title, subtitle, footer, items, defaultIdx = 0 }) {
|
|
|
1521
1521
|
const readline = await import('node:readline');
|
|
1522
1522
|
readline.emitKeypressEvents(process.stdin);
|
|
1523
1523
|
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
1524
|
+
// A previous `readline.createInterface(...).close()` (e.g. from
|
|
1525
|
+
// `_quickPrompt`) leaves stdin paused — the keypress listener we
|
|
1526
|
+
// attach below would never fire and the menu would appear frozen
|
|
1527
|
+
// instead of responding to arrow keys. Resume + ref defensively
|
|
1528
|
+
// before drawing so the picker always receives the first keypress.
|
|
1529
|
+
process.stdin.resume();
|
|
1530
|
+
if (process.stdin.ref) process.stdin.ref();
|
|
1524
1531
|
let idx = Math.max(0, Math.min(items.length - 1, defaultIdx));
|
|
1525
1532
|
const accent = (s) => `\x1b[38;5;208m${s}\x1b[0m`;
|
|
1526
1533
|
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
@@ -3407,16 +3414,23 @@ async function cmdSetup(_sub, _positional, flags = {}) {
|
|
|
3407
3414
|
try {
|
|
3408
3415
|
await cmdOnboard({ pick: true });
|
|
3409
3416
|
} catch (e) {
|
|
3417
|
+
// Don't kill the process — the setup wizard is often called
|
|
3418
|
+
// from inside cmdLauncher's loop, and a process.exit there
|
|
3419
|
+
// would close the launcher entirely (the surface bug the
|
|
3420
|
+
// user reported as "Setup 누르고 엔터 누르니까 바로 꺼져").
|
|
3421
|
+
// Surface the error and let the caller decide.
|
|
3410
3422
|
process.stderr.write(`onboard error: ${e?.message || e}\n`);
|
|
3411
|
-
|
|
3423
|
+
return;
|
|
3412
3424
|
}
|
|
3413
3425
|
// Re-read config after onboard wrote it. If the user aborted with
|
|
3414
3426
|
// no provider set, bail out early — the rest of the wizard depends
|
|
3415
|
-
// on a provider being configured.
|
|
3427
|
+
// on a provider being configured. `return` (not process.exit) so a
|
|
3428
|
+
// launcher caller can re-prompt or fall back gracefully.
|
|
3416
3429
|
const cfgAfterOnboard = readConfig();
|
|
3417
3430
|
if (!cfgAfterOnboard.provider) {
|
|
3418
|
-
process.stdout.write(`\n ${warn('Setup
|
|
3419
|
-
process.
|
|
3431
|
+
process.stdout.write(`\n ${warn('Setup not completed — provider was not configured.')}\n`);
|
|
3432
|
+
process.stdout.write(` ${dim('Run `lazyclaw setup` again when ready, or pick "Onboard" from the menu for a single-step picker.')}\n\n`);
|
|
3433
|
+
return;
|
|
3420
3434
|
}
|
|
3421
3435
|
process.stdout.write(`\n ${ok('✓ provider:')} ${cfgAfterOnboard.provider} ${dim('model:')} ${cfgAfterOnboard.model || '(default)'}\n\n`);
|
|
3422
3436
|
|
|
@@ -3543,34 +3557,69 @@ async function _runFirstTimeOnboard() {
|
|
|
3543
3557
|
process.stdout.write('\n');
|
|
3544
3558
|
}
|
|
3545
3559
|
|
|
3560
|
+
// Marker exception used by the launcher's process.exit guard. See
|
|
3561
|
+
// _dispatchMenuChoice below for why intercepting process.exit is
|
|
3562
|
+
// the cleanest way to keep the menu loop alive.
|
|
3563
|
+
class _DispatchExit extends Error {
|
|
3564
|
+
constructor(code) {
|
|
3565
|
+
super(`subcommand requested exit ${code}`);
|
|
3566
|
+
this.name = 'DispatchExit';
|
|
3567
|
+
this.exitCode = Number.isFinite(code) ? code : 0;
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
|
|
3546
3571
|
// Direct dispatch from a launcher pick. Replaces the previous
|
|
3547
3572
|
// `process.argv = [...]; await main()` round-trip so we can reuse
|
|
3548
3573
|
// the launcher across multiple iterations without compounding
|
|
3549
|
-
// state.
|
|
3550
|
-
//
|
|
3574
|
+
// state.
|
|
3575
|
+
//
|
|
3576
|
+
// Subcommand functions across this CLI freely call `process.exit()`
|
|
3577
|
+
// to signal their result — perfectly fine for one-shot CLI use,
|
|
3578
|
+
// fatal to a launcher loop because the first exit kills the whole
|
|
3579
|
+
// process before we can redraw the menu. Intercept process.exit for
|
|
3580
|
+
// the duration of the dispatch and turn it into a thrown exception
|
|
3581
|
+
// the loop can catch + log + continue from. This mirrors how Python
|
|
3582
|
+
// CLI frameworks handle SystemExit when running inside a REPL.
|
|
3551
3583
|
async function _dispatchMenuChoice(argv) {
|
|
3552
3584
|
const sub = argv[0];
|
|
3553
3585
|
const rest = argv.slice(1);
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3586
|
+
const realExit = process.exit.bind(process);
|
|
3587
|
+
process.exit = (code) => { throw new _DispatchExit(code); };
|
|
3588
|
+
try {
|
|
3589
|
+
switch (sub) {
|
|
3590
|
+
case 'chat': return await cmdChat({});
|
|
3591
|
+
case 'agent': return await cmdAgent(rest[0] || '-', {});
|
|
3592
|
+
case 'onboard': return await cmdOnboard({});
|
|
3593
|
+
case 'setup': return await cmdSetup(undefined, rest, {});
|
|
3594
|
+
case 'workspace': return await cmdWorkspace(rest[0], rest.slice(1), {});
|
|
3595
|
+
case 'browse': return await cmdBrowse(rest[0], {});
|
|
3596
|
+
case 'skills': return await cmdSkills(rest[0], rest.slice(1), {});
|
|
3597
|
+
case 'sessions': return await cmdSessions(rest[0], rest.slice(1), {});
|
|
3598
|
+
case 'providers': return await cmdProviders(rest[0], rest.slice(1), {});
|
|
3599
|
+
case 'cron': return await cmdCron(rest[0], rest.slice(1), {});
|
|
3600
|
+
case 'auth': return await cmdAuth(rest[0], rest.slice(1), {});
|
|
3601
|
+
case 'pairing': return await cmdPairing(rest[0], rest.slice(1), {});
|
|
3602
|
+
case 'nodes': return await cmdNodes(rest[0], rest.slice(1), {});
|
|
3603
|
+
case 'message': return await cmdMessage(rest[0], rest.slice(1), {});
|
|
3604
|
+
case 'doctor': return await cmdDoctor();
|
|
3605
|
+
case 'status': return await cmdStatus();
|
|
3606
|
+
case 'help': return cmdHelp();
|
|
3607
|
+
case 'dashboard': return await cmdDashboard({});
|
|
3608
|
+
default: throw new Error(`unknown menu choice: ${sub}`);
|
|
3609
|
+
}
|
|
3610
|
+
} catch (e) {
|
|
3611
|
+
if (e instanceof _DispatchExit) {
|
|
3612
|
+
// Subcommand wanted to exit. Surface a non-zero code so the
|
|
3613
|
+
// user knows something flagged, but DON'T propagate — we want
|
|
3614
|
+
// the launcher loop to continue.
|
|
3615
|
+
if (e.exitCode !== 0) {
|
|
3616
|
+
process.stderr.write(` \x1b[2m(subcommand returned exit code ${e.exitCode})\x1b[0m\n`);
|
|
3617
|
+
}
|
|
3618
|
+
return;
|
|
3619
|
+
}
|
|
3620
|
+
throw e;
|
|
3621
|
+
} finally {
|
|
3622
|
+
process.exit = realExit;
|
|
3574
3623
|
}
|
|
3575
3624
|
}
|
|
3576
3625
|
|
|
@@ -3723,6 +3772,15 @@ async function cmdLauncher() {
|
|
|
3723
3772
|
async function _quickPrompt(label) {
|
|
3724
3773
|
const readline = await import('node:readline');
|
|
3725
3774
|
process.stdout.write('\n');
|
|
3775
|
+
// Make sure stdin is in cooked / line-buffered mode for the
|
|
3776
|
+
// duration of the prompt — a prior `_arrowMenu` may have left raw
|
|
3777
|
+
// mode on, in which case readline.question() never fires its
|
|
3778
|
+
// line-event because each byte is delivered as a keypress instead.
|
|
3779
|
+
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
3780
|
+
try { process.stdin.setRawMode(false); } catch (_) {}
|
|
3781
|
+
}
|
|
3782
|
+
process.stdin.resume();
|
|
3783
|
+
if (process.stdin.ref) process.stdin.ref();
|
|
3726
3784
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
3727
3785
|
const ans = await new Promise((resolve) => rl.question(label, resolve));
|
|
3728
3786
|
rl.close();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazyclaw",
|
|
3
|
-
"version": "3.99.
|
|
3
|
+
"version": "3.99.10",
|
|
4
4
|
"description": "Lazy, elegant terminal CLI for chatting with Claude / OpenAI / Gemini / Ollama and orchestrating multi-step LLM workflows. Banner-on-launch, slash-command ghost autocomplete, persistent sessions, local HTTP gateway.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|