native CC 2.1.117 regression: /usage via PTY drops first char after / in slash-command mode
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 withdisplay: "sage"at 15 / 30 / 45-minute intervals, each with a uniquesessionId, 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.7VS Code extension on a 900 s refresh timer
Ask
- 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. - Expose a non-interactive quota endpoint — e.g.
claude quota --jsonor 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.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗