# Clavue v8.13.5 — Interactive-failure hint for `!` mode

Released: 2026-05-20

## Why this exists

A user upgrading to v8.13.4 hit:

```
! npm i -g clavue@8.13.5
EAGAIN: resource temporarily unavailable, read
```

Followed by a reasonable complaint: "通过 `!` 这种方式基本上没办法执行
成功，是程序和机制不兼容吧?"

The complaint was half right. The `!cmd` mode in Clavue runs the command
through `BashTool` with **stdin redirected from `/dev/null`**
(`shouldAddStdinRedirect` in `src/utils/bash/shellQuoting.ts`) and a
non-TTY pipe-based subprocess. That is intentional — forwarding the
TUI's controlling TTY into a child would race the user's keystrokes
between agent and child. The trade-off is that any command which calls
`readline()` / `getpass()` / launches a TTY editor fails, but with a
cryptic `EAGAIN: ... read` instead of a useful explanation.

This release does **not** change `!` mode's semantics. It adds a
recognizer + a friendly hint so users see exactly what failed and what
to do about it.

## What changed

### 1. `src/utils/processUserInput/interactiveDetection.ts` (new)

Pure function `detectInteractiveFailure(stdout, stderr)` matches a
curated list of patterns that reliably indicate "this command needed a
real TTY":

- `EAGAIN: ... read`, `resource temporarily unavailable, read`
- `stdin is not a tty`, `not a tty`, `Inappropriate ioctl`
- `sudo: a terminal is required`
- `Pseudo-terminal will not be allocated`
- `gpg: problem with the agent`, `could not open /dev/tty`
- npm 2FA: `This operation requires a one-time password`
- vim/editor: `Vim: Warning: Output is not to a terminal`,
  `editor returned a non-zero exit code`

When matched, `buildInteractiveFailureHint(command)` returns a short
message wrapped in `<bash-clavue-hint>` tags telling the user to run
the command in their own terminal.

### 2. `src/utils/processUserInput/processBashCommand.tsx`

Three call sites — the success path and both `ShellError` branches in
`catch` — now append the hint when detection fires. The `<bash-stderr>`
payload still ships the original stderr verbatim (the model needs the
real error to reason about it); the hint sits inside the same tag with
a wrapper, so:

```xml
<bash-stderr>
EAGAIN: resource temporarily unavailable, read
<bash-clavue-hint>This command appears to need an interactive terminal
(password / 2FA / editor / TTY prompt). Clavue runs `!` commands in a
non-interactive subprocess, so they can't read keystrokes. Run it
directly in your own terminal instead:
  npm login
Then return to Clavue to continue.</bash-clavue-hint>
</bash-stderr>
```

### 3. `src/tools/BashTool/prompt.ts`

Added an explicit instruction to the model: when a user-typed `!cmd`
looks interactive, **don't retry it** — instruct the user to run it in
their own terminal. Lists common non-interactive equivalents
(`npm publish --otp=<code>`, `gh auth login --with-token`,
`gcloud auth activate-service-account --key-file`, `git commit -m`).
Without this, the model would sometimes loop on the same failing
`!cmd` retry.

## What we did NOT do

- **Did not introduce `node-pty`.** A real PTY backend would solve the
  underlying limitation but breaks the single-file dist bundle (native
  module), pollutes the user's terminal state, and rewrites every
  downstream assumption about stdout/stderr formatting and stall
  detection. Deferred to a future minor.
- **Did not remove `< /dev/null`.** The redirect prevents pipe
  subprocesses from blocking on stdin forever and is load-bearing for
  the existing test matrix.
- **Did not change BashTool semantics for model-initiated commands.**
  Only the user-typed `!cmd` path was touched; the model already had a
  prompt-level guardrail since 8.x.

The result is a small surface that fixes the UX papercut without
touching the core invariant.

## Verification

- `tests/interactive-detection.test.mjs`: 13 unit tests covering each
  pattern, the false-positive cases (regular compiler errors, command
  not found), undefined/empty inputs, stdout-vs-stderr coverage, and
  hint truncation.
- Full suite: 1550 tests / 1544 pass / 6 skipped / 0 fail.
- `dist/cli.js --version` → `8.13.5 (Clavue)`.
- `npm run check` full release gate green.

## What changes for users

- `!cmd`-mode failures that are actually TTY-related (npm login,
  gcloud auth login, sudo without `-S`, ssh password prompt, gpg
  passphrase, `git commit` without `-m`, vim/less, etc.) now ship a
  one-line hint telling you to run the command in your own terminal.
- The model is now explicitly told to *not* retry such commands and to
  surface non-interactive flags when they exist.
- No change for non-interactive commands. They behave exactly as
  before.

## Upgrade

```bash
npm i -g clavue@8.13.5
# or
npx -y clavue@8.13.5
# or with the v8.13.4+ self-healing installer
curl -fsSL https://unpkg.com/clavue@8.13.5/install.sh | bash -s -- 8.13.5
```
