diff --git a/agent/anthropic_adapter.py b/agent/anthropic_adapter.py index cf22c2ab8a9..389b21831fb 100644 --- a/agent/anthropic_adapter.py +++ b/agent/anthropic_adapter.py @@ -1164,9 +1164,12 @@ def run_oauth_setup_token() -> Optional[str]: "Install it with: npm install -g @anthropic-ai/claude-code" ) - # Run interactively — stdin/stdout/stderr inherited so user can interact + # Run interactively — stdin/stdout/stderr inherited so the user can + # complete the OAuth login prompt. Must keep inherited stdin; the TUI-EOF + # concern does not apply to an interactive login the user explicitly + # invokes. noqa: subprocess-stdin try: - subprocess.run([claude_path, "setup-token"], stdin=subprocess.DEVNULL) + subprocess.run([claude_path, "setup-token"]) except (KeyboardInterrupt, EOFError): return None diff --git a/scripts/check_subprocess_stdin.py b/scripts/check_subprocess_stdin.py index ccec7a3bfcf..4d312a5949e 100644 --- a/scripts/check_subprocess_stdin.py +++ b/scripts/check_subprocess_stdin.py @@ -44,6 +44,13 @@ KNOWN_SAFE = { "plugins/security-guidance/patterns.py", # subprocess mentions are in reminder strings, not calls } +# Inline marker that exempts a single subprocess call from this check. +# Put it in a comment on (or within) the call when the process MUST inherit +# stdin — e.g. an interactive login the user explicitly invokes. Travels with +# the line, so it survives edits that shift line numbers (unlike a pinned +# file:line entry). +EXEMPT_MARKER = "noqa: subprocess-stdin" + # Directories to skip entirely. SKIP_DIRS = { "tests/", @@ -105,6 +112,14 @@ def find_subprocess_calls(content: str, filepath: str) -> list[dict]: if "input=" in call_text: break + # Inline exemption marker on the call itself or within + # the few comment lines immediately above it → the call + # intentionally inherits stdin. + window_start = max(0, i - 4) + preceding = "\n".join(lines[window_start:i]) + if EXEMPT_MARKER in call_text or EXEMPT_MARKER in preceding: + break + violations.append({ "file": filepath, "line": i + 1,