lazyclaw 3.99.9 → 3.99.11

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 (3) hide show
  1. package/README.md +1 -1
  2. package/cli.mjs +37 -2
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -71,7 +71,7 @@ What you see on launch (TTY only):
71
71
  │ | |__ _ _____ _ _ │
72
72
  │ | / _` |_ / || | '_| │
73
73
  │ |_\__,_/__\_, |_| │
74
- │ LazyClaw |__/ 3.99.9
74
+ │ LazyClaw |__/ 3.99.11
75
75
  ╰──────────────────────────────╯
76
76
 
77
77
  provider · anthropic
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`;
@@ -3648,8 +3655,15 @@ async function cmdLauncher() {
3648
3655
  // Outer loop — each iteration is one menu render → pick →
3649
3656
  // dispatch round. Subcommand return drops back here and the menu
3650
3657
  // is redrawn. Quit / Esc / Ctrl-C breaks the loop and returns,
3651
- // which lets the calling main() exit naturally (no process.exit
3652
- // so the buffered writer / open file descriptors close cleanly).
3658
+ // which lets the calling main() exit naturally.
3659
+ //
3660
+ // try/finally below is load-bearing: the loop body keeps stdin
3661
+ // ref'd so the picker's keypress events fire. If we just `return`
3662
+ // on Quit, stdin stays ref'd and Node's event loop never empties
3663
+ // → the `lazyclaw` process hangs forever after the user picked
3664
+ // Quit. The finally explicitly pauses + unrefs stdin so the
3665
+ // process exits cleanly the moment the user picks Quit.
3666
+ try {
3653
3667
  while (true) {
3654
3668
  // First-run / config-missing guard: a fresh install has no
3655
3669
  // `provider` set, so any menu pick that calls a provider would
@@ -3760,11 +3774,32 @@ async function cmdLauncher() {
3760
3774
  await _quickPrompt(` ${dim('Press Enter to return to the menu… ')}`);
3761
3775
  }
3762
3776
  }
3777
+ } finally {
3778
+ // Drop the stdin holds we kept open while the menu was active.
3779
+ // Without this, the Node event loop never empties on Quit and
3780
+ // the `lazyclaw` process hangs at the shell prompt. Mirrors the
3781
+ // cleanup path cmdChat installed in v3.92 for the same reason.
3782
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
3783
+ try { process.stdin.setRawMode(false); } catch (_) {}
3784
+ }
3785
+ try { process.stdout.write('\x1b[?25h'); } catch (_) {} // restore cursor
3786
+ try { process.stdin.pause(); } catch (_) {}
3787
+ try { process.stdin.unref(); } catch (_) {}
3788
+ }
3763
3789
  }
3764
3790
 
3765
3791
  async function _quickPrompt(label) {
3766
3792
  const readline = await import('node:readline');
3767
3793
  process.stdout.write('\n');
3794
+ // Make sure stdin is in cooked / line-buffered mode for the
3795
+ // duration of the prompt — a prior `_arrowMenu` may have left raw
3796
+ // mode on, in which case readline.question() never fires its
3797
+ // line-event because each byte is delivered as a keypress instead.
3798
+ if (process.stdin.isTTY && process.stdin.setRawMode) {
3799
+ try { process.stdin.setRawMode(false); } catch (_) {}
3800
+ }
3801
+ process.stdin.resume();
3802
+ if (process.stdin.ref) process.stdin.ref();
3768
3803
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3769
3804
  const ans = await new Promise((resolve) => rl.question(label, resolve));
3770
3805
  rl.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazyclaw",
3
- "version": "3.99.9",
3
+ "version": "3.99.11",
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",