Agent CLI: interactive overlay pagers (`/mcp list`, `/skills`, etc.) freeze agent on close (Windows)

Where does the bug appear (feature/product)?

Cursor CLI

Describe the Bug

cli on windows freezes after hitting ESC or q from the /mdp list menu

Steps to Reproduce

Reproduction

/mcp list

  1. Run agent to start interactive mode
  2. Type /mcp list
  3. The MCP server list renders correctly (navigation with arrow keys works)
  4. Press Esc, q, or Ctrl+C to close the pager
  5. Result: Agent is frozen. No keyboard input is accepted. Must kill the terminal.

/skills

  1. Run agent to start interactive mode
  2. Type /skills, select a skill, let it run
  3. When the skill finishes and the agent should prompt for input
  4. Result: Agent is frozen.

Expected Behavior

Expected: Pager/overlay closes and the agent prompt resumes accepting input.

Operating System

Windows 10/11

Version Information

Environment

  • Agent version: [email protected] (latest as of 2026-03-05)
  • Node.js: v24.5.0 (bundled)
  • OS: Windows 10 22631
  • Terminal: Windows Terminal
  • Shell: PowerShell 5.1 and PowerShell 7.5.4 (same behavior in both)

Additional Information

used claude opus 4.6 high thinking to diagnose this, here what it said:

Root Cause Analysis

Reverse-engineered from the minified bundle (434.index.js).

The deferred transition wrapper (ln) in src/run-agent.tsx

All overlay onClose handlers route through ln():

ln = useCallback((callback) => {
  const { promise, resolve } = Promise.withResolvers();
  tn(true);           // Step 1: set transition flag (disables ALL UI rendering)
  setTimeout(() => {
    callback();        // Step 2a: execute state change (hide overlay)
    clearScreen(stdout); // Step 2b: write \x1b[H\x1b[2J\x1b[3J
    setTimeout(() => {
      tn(false);       // Step 3: clear transition flag (should re-enable UI)
      resolve();
    }, 10);
  }, 10);
  return promise;
}, [stdout]);

Why it freezes

The main prompt/chat UI only renders when ALL of these flags are false:

!en && !_n && !Yn && !ro && !Vn && !to && "chat" === viewMode

en is the transition flag controlled by tn(). During the deferred transition:

  1. tn(true) sets en = true – all UI stops rendering, all Ink useInput handlers deactivate
  2. The overlay’s useInput hooks deregister from stdin (component unmounts)
  3. After 10ms+10ms, tn(false) sets en = false – Ink should re-render and reactivate the prompt’s useInput

On Windows, step 3 fails: Ink does not re-attach the stdin data listener after the
deferred unmount/remount cycle. The UI may re-render visually, but keyboard input is
silently dropped because no handler is listening on stdin.

Confirmed by patching

Tested three patches to isolate the root cause:

  1. Screen clear no-op (replace \x1b[H\x1b[2J\x1b[3J with void 0): Still freezes.
    Rules out the escape sequence as the cause.

  2. Bypass ln() for MCP pager only (replace onClose:()=>{ln((()=>{Zn(!1)}))} with
    onClose:()=>{Zn(!1)}): MCP pager no longer freezes. Confirms ln() is the bug.
    But /skills still freezes because it also calls ln() via Jn().

  3. Make ln() synchronous (remove tn(true)/setTimeout/tn(false), call callback
    and screen clear immediately): Fixes ALL overlays. This is the working patch.

Working patch (applied to minified 434.index.js)

Replace the deferred ln implementation with a synchronous version:

// Before (broken on Windows):
ln = useCallback((e) => __awaiter(function*() {
  const { promise, resolve } = Promise.withResolvers();
  tn(true);
  setTimeout(() => __awaiter(function*() {
    yield e();
    clearScreen(stdout);
    setTimeout(() => { tn(false); resolve(); }, 10);
  }), 10);
  return promise;
}), [stdout]);

// After (synchronous, works on Windows):
ln = useCallback((e) => { e(); clearScreen(stdout); return Promise.resolve(); }, [stdout]);

Source Files (from source map references in the bundle)

  • src/run-agent.tsx – deferred transition wrapper (ln) and main render tree
  • src/components/mcp-servers-pager.tsx – MCP pager with three useInput handlers
  • src/components/alt-screen.tsx – screen clear function
  • src/components/items-pager.tsx – skills/rules/commands pager (uses Jn which calls ln)

Workaround

For /mcp list specifically: use agent mcp list (non-interactive CLI mode).

For the general fix: patch ln() to be synchronous as described above.

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Hey there.

Thanks for reporting this, and for the deep investigation! I can reproduce it and I’ve filed a bug with the team.

1 Like

This topic was automatically closed 22 days after the last reply. New replies are no longer allowed.