fix(pty): detect npm Claude Code through node wrapper - #62
Conversation
Canonicalize the npm package argv so agent-specific attention and auto-approve gates remain active.
`npm i -g` puts a `bin/claude` symlink in front of a `#!/usr/bin/env node` script, and execve hands the interpreter the unresolved symlink path, so the wrapper's argv is `["node", "/opt/homebrew/bin/claude"]` — no package name in it. The previous commit only matched `@anthropic-ai/claude-code`, so the most common install still reported `node` and classified Generic. Fold the per-agent predicates into a `WRAPPED_AGENTS` table and resolve in two passes: an explicit npm package reference wins outright for any agent, then the executable-basename heuristic runs over the leading positional arguments only. That gives claude the basename fallback codex already had, stops a Claude Code launch with `--add-dir ~/codex` from being branded Codex, and keeps flag values like `--plugin claude-code` from branding unrelated scripts. Tests cover the symlink argv, npx/bunx by package and by bin name, near misses, and the cross-agent ordering cases. Update the `classify_agent` doc and the auto-approve spec, which still described wrapper launches as an accepted Generic limitation.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8a849a9c28
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .iter() | ||
| .find_map(|arg| { | ||
| WRAPPED_AGENTS | ||
| .iter() | ||
| .find(|agent| arg_mentions_package(arg, agent.package)) |
There was a problem hiding this comment.
Restrict package matches to the launched command
This first pass treats an exact package reference anywhere in the wrapper's argv as proof that the wrapper is running that agent. For example, an unrelated node audit-package.js @anthropic-ai/claude-code is canonicalized to claude, causing incorrect branding and BEL escalation and, in an auto-approve-enabled tab, satisfying the agent gate for any matching prompt text. Inspect only the wrapper's actual script/package command position rather than arbitrary operands or option values.
Useful? React with 👍 / 👎.
Summary
Claude Code installed via npm (
npm i -g @anthropic-ai/claude-code) runs under anodewrapper, so the tty's foreground process name wasnodeandclassify_agentreturnedGeneric: no BEL→attention promotion, no Dock attention, sidebar badge "node", and the auto-approve fire gate (classify_agent(process) != signature.agent()) rejected every candidate.claudethe same way they are already canonicalized tocodex.npm i -gputs abin/claudesymlink in front of a#!/usr/bin/env nodescript, and execve passes the unresolved symlink path, so argv is["node", "/opt/homebrew/bin/claude"]with no package name in it. A basename heuristic (which codex already had) covers this.WRAPPED_AGENTStable resolved in two passes: an explicit npm package reference wins for any agent, then the executable-basename heuristic runs over the leading positional arguments only. This stops a Claude Code launch with--add-dir ~/codexfrom being branded Codex, and keeps flag values like--plugin claude-codefrom branding unrelated scripts.classify_agentdoc comment anddocs/specs/auto-approve-mode.md, which still described wrapper launches as an acceptedGenericlimitation.Native-installer Claude Code (
…/claude/versions/2.1.260) was already handled viais_bare_versionand is unaffected.Test plan
cargo test -p noa-pty(unsandboxed, 41 passed ×3): new cases for the symlink argv,npx claude,npx @anthropic-ai/claude-code@latest,bun x @anthropic-ai/claude-code, directcli.js, near misses (claude-code-sdk,claudette,--plugin claude-code), non-wrapper names, and cross-agent ordering for both claude and codexcargo test -p noa-app classify_agent auto_approvecargo clippy -p noa-pty -p noa-app --all-targets: no new warningsclaudein a noa pane and confirm the sidebar badge shows Claude Code and BEL promotes to attentionFollow-up (out of scope):
gemini/@google/gemini-cliis known toclassify_agentbut not yet inWRAPPED_AGENTS; one table row.