Spinner/status line doesn't overwrite in-place when running inside a non-standard PTY relay (Tauri + xterm.js)

Ouverte
#770 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
48/100
Type d'issue
Bug
Clarté
Plutôt claire
Activité
Active
Stack technique
rust, tauri
Domaine
cli, desktop

Piste de recherche

Les points d’entrée pertinents nommés sont registerInkControl(), createPhysicalLogUpdate(), physicalLineCount() et hardResetForResize() dans dist/cli.mjs. Reproduisez le problème avec CMD_FLICKER_DEBUG=1 dans le PTY Tauri/xterm.js et comparez le rendu incrémentiel avec le chemin de réinitialisation déclenché par le redimensionnement. C’est terminé lorsque les frames du spinner écrasent une ligne dans ce relay au lieu d’accumuler des lignes, tandis que le comportement dans un terminal normal reste intact.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

Summary

flicker.log

Summary

The animated status line ("Preparing… esc to interrupt • Ns • ↓ 0") prints a new line
on every animation frame instead of overwriting the previous one, when Command Code
runs inside a PTY that isn't a native terminal (specifically: a Tauri desktop app
embedding xterm.js, with the shell spawned via portable-pty in Rust). The same
package version works correctly in a normal terminal (tested in kitty).

Environment
  • command-code v1.38.2 (models: claude-sonnet-4.6 · taste-1)
  • Host terminal: kitty (macOS)
  • Reproduces inside: a Tauri app's embedded terminal tab (xterm.js 5.x +
    portable-pty, PTY spawned with a real TTY, TERM=xterm-256color, CI unset,
    TERMINFO unset for the test — none of these changed the behavior)
  • process.stdout.isTTY / process.stdin.isTTY: both true inside the broken session
  • CMD_FLICKER_DEBUG=1 log shows a stable cols=95 on every STATUS_RENDER during
    the broken bursts — the reported terminal width is not fluctuating or falling
    back to a default.
Repro
  1. Run cmd inside the embedded PTY described above.
  2. Ask any question that triggers the "thinking" status spinner.
  3. Every animation frame (~80ms cadence) appears on its own new line instead of
    overwriting the previous frame in place.
What we found digging into dist/cli.mjs

registerInkControl() replaces Ink's internal log/throttledLog with a custom
createPhysicalLogUpdate() renderer that does cursor-up + erase-line (via
ansi-escapes's eraseLines, wrapped in DEC synchronized-output \x1b[?2026h/l)
instead of Ink's built-in log-update. This custom renderer is what fails to
correctly erase the previous frame in our environment.

Notably: hardResetForResize() (triggered on every SIGWINCH-driven resize) does a
full clearTerminal + resets Ink's internal lastOutput/fullStaticOutput state.
While we are actively resizing the host window (continuous SIGWINCH), every frame
goes through this hard-reset path and renders correctly. The instant resizing
stops, rendering falls back to the incremental createPhysicalLogUpdate path and
immediately starts stacking new lines again — even though getCols() keeps
reporting a stable, plausible cols value throughout.

This suggests the incremental cursor-up/erase-line path in
createPhysicalLogUpdate/physicalLineCount has an assumption that doesn't hold
in this PTY relay (order/timing of writes vs. Ink's own "Static" content stream,
possibly), while the "hard reset" path is unaffected because it doesn't rely on
tracked cursor position at all.

Attached

~/.commandcode/logs/flicker.log (CMD_FLICKER_DEBUG=1 session covering a broken burst)

Expected Behavior

While Command Code is streaming its "thinking" status (spinner + "esc to interrupt • Ns • ↓ 0"), each animation frame should overwrite the previous one in place — the status line should stay on a single line/row, not accumulate new lines.

Actual Behavior

Every animation frame (~80ms cadence) is printed as a brand-new line instead of overwriting the previous one, so the terminal fills up with dozens of stacked "Preparing… esc to interrupt • 0s • ↓ 0" / "Sculpting… esc to interrupt • 1s • ↓ 0" lines for a single status update. This only happens inside a PTY relay that isn't a native terminal (a Tauri desktop app embedding xterm.js, shell spawned via Rust's portable-pty) — the exact same cmd install works correctly in a normal terminal (tested in kitty on the same machine).

Diagnostics ruled out: process.stdout.isTTY/process.stdin.isTTY are both true; CI/CONTINUOUS_INTEGRATION are unset; TERMINFO unset makes no difference; CMD_FLICKER_DEBUG=1 shows a stable cols=95 on every STATUS_RENDER during the broken bursts (not falling back to a default, not fluctuating).

Notably: while continuously resizing the host window (which sends repeated SIGWINCH → hardResetForResize() → full clearTerminal + Ink internal state reset), the status line renders correctly — the bug only reappears once resizing stops and rendering falls back to the incremental createPhysicalLogUpdate (cursor-up + eraseLines, wrapped in \x1b[?2026h/l) path that registerInkControl() patches onto Ink's internal logger.

Steps to reproduce the issue
  1. Run command-code (v1.38.2) inside a PTY that is not a native terminal — specifically, a Tauri app with a shell spawned via portable-pty in Rust and rendered in the frontend with xterm.js (env: TERM=xterm-256color, real TTY, CI unset).
  2. Ask any question that triggers the "thinking" status spinner (e.g. "analiza en qué fase vamos").
  3. Observe: each spinner frame appears on a new line instead of overwriting the previous one.
  4. (Optional, to confirm) Run with CMD_FLICKER_DEBUG=1 cmd, reproduce, then inspect ~/.commandcode/logs/flicker.log — cols= stays stable throughout the broken burst.
  5. (Optional, to see the masking effect) While the status is streaming, continuously resize the host window — the spinner overwrites correctly during the resize, then reverts to stacking lines the moment resizing stops.
Command Code Version

1.38.2

Operating System

macOS

Terminal/IDE

Unknown

Shell

zsh

Session file (optional)

No response

Fix prompt (optional)

No response

Additional context

OS: macOS Tahoe 26.6.2

Langage dominant
Aucune donnée de langage
Étoiles
4k
Forks
350
Métriques de merge des PR
Aucune PR mergée en 30 j

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de CommandCodeAI/command-code

Toutes les issues de CommandCodeAI/command-code

Issues similaires

Plus d'issues CLI

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.