Skip to content

[BUG] Terminal scrolls to top during agent execution (Windows Terminal + PowerShell) #34794

@cruzlauroiii

Description

@cruzlauroiii

Description

While Claude Code is executing tools/commands, the terminal viewport jumps to the topmost position of the PowerShell/terminal buffer. This happens repeatedly during agent work, forcing the user to scroll back down constantly. Expected behavior: maintain the current scroll position so the user can continue working.

Root Cause Analysis (from cli.js v2.1.76 source)

Claude Code uses Ink (React-based terminal UI) for rendering. Ink's rendering strategy:

  1. On each re-render, it moves the cursor UP to the start of the component area
  2. Erases all previous lines (eraseLines(height))
  3. Writes the new content

The core rendering code (from the bundled @inquirer/core and Ink output):

// Moves cursor up, erases, and rewrites
this.write(
  cursorDown(this.extraLinesUnderPrompt) +
  eraseLines(this.height) + 
  newContent
)

Where eraseLines is:

eraseLines = (count) => {
  let str = "";
  for (let i = 0; i < count; i++)
    str += eraseLine + (i < count - 1 ? cursorUp() : "");
  if (count) str += cursorLeft;
  return str;
}

The problem: When the output grows large (streaming responses, tool results), eraseLines(this.height) moves the cursor far up in the terminal buffer. On Windows Terminal, this cursor movement causes the viewport to follow the cursor to the top of the render area, even if the user has scrolled away.

This is a fundamental issue with Ink's diff-based terminal rendering on Windows Terminal. macOS Terminal.app and iTerm2 are also affected (per #34400, #34765).

Steps to Reproduce

  1. Start Claude Code in Windows Terminal (PowerShell)
  2. Give it a multi-step task (e.g., searching files, running commands)
  3. While it's working, scroll up to read previous output
  4. Observe: viewport snaps to top of terminal buffer on each re-render

Suggested Fix

Option A: Use alternate screen buffer for the active render area

Ink supports alternate screen buffer mode which prevents scroll interference:

// Enter alternate screen before rendering
process.stdout.write('\x1b[?1049h');
// Exit alternate screen when done
process.stdout.write('\x1b[?1049l');

Option B: Minimize re-render height

Instead of erasing the entire component and redrawing, only erase/redraw the lines that actually changed (incremental rendering).

Option C: Disable cursor movement when user has scrolled

Detect if the user has scrolled away from the bottom and suppress cursor-up movements until they scroll back.

Environment

  • OS: Windows 11 Pro 10.0.26200
  • Terminal: Windows Terminal
  • Shell: PowerShell / bash (Git Bash)
  • Claude Code: 2.1.76
  • Source: cli.js SHA-256 38b8fd29d0817e5f75202b2bb211fe959d4b6a4f2224b8118dabf876e503b50b

Related Issues

Metadata

Metadata

Assignees

Labels

area:tuibugSomething isn't workingduplicateThis issue or pull request already existsplatform:windowsIssue specifically occurs on Windows

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions