native CC 2.1.117 regression: /usage via PTY drops first char after / in slash-command mode

Resolved 💬 1 comment Opened Apr 22, 2026 by Evey-Vendetta Closed Apr 22, 2026

Summary

Native Claude Code (claude.exe 2.1.117 on Windows) has a regression in PTY input handling: when a wrapper script writes /usage\r to a spawned claude --dangerously-skip-permissions process, the first character after / is dropped. The u is consumed by the slash-command mode state transition, and the remaining sage\r is submitted as a plain user prompt — creating a real persisted session instead of executing the /usage slash command.

Impact

Third-party analytics tooling that scrapes /usage TUI output via PTY spawns (e.g. cc-analytics VS Code extension) creates a phantom Claude session per scrape. Over 48 hours at a 15-minute refresh cadence, this produced 30+ phantom sessions on the affected machine, each submitting the prompt sage (the surviving chars of /usage after /u is dropped) and consuming quota for the assistant's "did you mean?" clarification.

Reproduction

Pseudocode matching the pattern used in cc-analytics@2.3.7/tools/fetch-usage.js:

const pty = require('node-pty').spawn('claude', ['--dangerously-skip-permissions'], {
  name: 'xterm-256color', cols: 120, rows: 80,
});
pty.write('\x1B');              // Esc to dismiss any pending notification
setTimeout(() => {
  pty.write('/usage\r');         // expected: execute /usage slash command
}, 500);

Expected: /usage panel renders with quota data.
Actual: sage submits as a user prompt; a new session is created and persisted to ~/.claude/projects/<slug>/<session-id>.jsonl; the assistant responds with a "did you mean?" message.

Evidence

  • ~/.claude/history.jsonl: 30 consecutive entries with display: "sage" at 15 / 30 / 45-minute intervals, each with a unique sessionId, each correlating to an actual session transcript file.
  • pastedContents: {} on every entry — confirms the text arrived via keystroke injection, not paste.
  • First assistant response in each phantom session is a clarification question about what "sage" means.

Hypothesis

Slash-command mode activation appears to race with keystroke consumption. When / arrives, the TUI transitions to slash-command-input state, but that transition is async — the next character (u) may arrive before the state machine is ready to consume it, landing in the submit-as-prompt handler. The tail (sage\r) then completes as a normal prompt submission.

Worked on npm, broke on native

This scrape pattern reportedly worked on the npm-distributed @anthropic-ai/claude-code package. The regression appears in native claude.exe 2.x — likely timing or state-machine differences between the embedded Node runtime and the bundled native binary.

Environment

  • OS: Windows 11 Pro 10.0.26200
  • claude --version: 2.1.117 (Claude Code)
  • Install path: ~/.local/bin/claude.exe (Windows native installer)
  • PTY wrapper: node-pty, xterm-256color, 120x80 grid
  • Consumer: cc-analytics@2.3.7 VS Code extension on a 900 s refresh timer

Ask

  1. Fix the input race so the first character after / isn't dropped when keystrokes arrive in a PTY faster than the slash-command state-machine can settle.
  2. Expose a non-interactive quota endpoint — e.g. claude quota --json or a stable file at ~/.claude/quota.json — so third-party analytics tooling doesn't have to scrape the TUI at all. This would close the whole class of issue.

A workaround patch with --no-session-persistence + defensive keystroke timing is being filed against the cc-analytics repo.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗