Skip to content

deerbox: post-session prompt ignores keystrokes, won't proceed on Enter #297

Description

@KakkoiDev

Summary

Kapture.2026-04-23.at.15.49.19.mp4

The deerbox post-session prompt (the menu shown after a session ends, asking whether to create a PR, keep the worktree, etc.) does not behave like an interactive menu:

  • Pressing 1 writes 1 on the screen instead of selecting an option.
  • Pressing Enter does not proceed; it just inserts a newline.

Expected behavior

The menu is rendered with single-letter hotkeys (p, k, s, m, d) and advertises k as the default. Pressing a letter, or Enter for the default, should immediately commit the choice and proceed.

Actual behavior

  • Individual keystrokes are echoed to the terminal (1 appears on screen).
  • Enter does not advance the flow.
  • The menu effectively hangs.

Environment

  • Repro happens when invoking deerbox and reaching the post-session menu.
  • Platform: macOS (darwin).

Root cause

File: packages/deerbox/src/post-session.ts

The interactive prompt uses Node's readline in the default line-buffered mode:

// post-session.ts:255-269
export async function interactivePromptChoice(...) {
  process.stderr.write(renderPromptMenu(...));
  const readline = await import("readline");
  return new Promise((resolve) => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stderr,
    });
    rl.once("line", (line: string) => {
      rl.close();
      resolve(parseChoice(line));
    });
  });
}

Issues:

  1. process.stdin.setRawMode(true) is never called. The terminal stays in cooked/canonical mode: keys are echoed locally and buffered until Enter.
  2. rl.once("line", ...) only fires on a newline. There is no way to capture a single-key selection.
  3. The menu advertises no numeric options, so users following conventional "numbered menu" muscle memory (pressing 1) have no path to the first option.
  4. The prompt renders to stderr only (post-session.ts:256, 262). If stderr is not connected to the user's terminal in some invocation paths, the prompt appears invisible.
  5. Enter on empty input should resolve to "k" (keep) via parseChoice("") at post-session.ts:124-131. The reporter observes that Enter does not proceed at all, which suggests stdin is not connected to the user's terminal in the failing case (e.g., piped from a parent process or tmux pane).

Suggested fix

Switch to raw-mode single-key input:

  • Set process.stdin.setRawMode(true) when stdin is a TTY.
  • Listen for a single data event (or use a keypress library) and resolve on the first keystroke.
  • Map Enter (\r / \n) to the default (k).
  • Accept numeric aliases (1 -> p, 2 -> k, ...) or render numbers in the menu, whichever matches the intended UX.
  • Fall back to the current line-buffered readline path when stdin is not a TTY (e.g., piped input in automated runs).
  • Restore cooked mode on exit, SIGINT, and error paths to avoid leaving the terminal in raw mode.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions