feat(llm_cli): integrate Claude Code CLI as LLM_PROVIDER#1168
Conversation
Greptile SummaryThis PR adds Confidence Score: 5/5Safe to merge; all findings are P2 style/documentation suggestions that do not affect runtime correctness. The implementation cleanly follows the existing Codex adapter pattern, satisfies every item on the AGENTS.md provider checklist, correctly isolates ANTHROPIC_* credentials from other CLI subprocesses, and ships 29 unit tests covering the main paths. Only two P2 issues were found: a slightly inaccurate diagnostic string and a missing timeout test case. app/integrations/llm_cli/claude_code.py — the darwin-branch detail string; tests/integrations/llm_cli/test_claude_code_adapter.py — missing TimeoutExpired test. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A([invoke prompt]) --> B[detect: claude --version]
B -->|fails / timeout| C([RuntimeError: not installed])
B -->|success| D{classify auth}
D -->|env var set| E[logged_in = True]
D -->|OAuth file present| E
D -->|macOS, no file| F[logged_in = None]
D -->|Linux/Win, no file| G[logged_in = False]
G --> H([RuntimeError: run claude login])
E --> I[build: argv + env overrides]
F --> I
I --> J[_build_subprocess_env: safe keys + CLAUDE_ prefix + overrides]
J --> K[subprocess.run claude -p]
K -->|rc != 0| L([RuntimeError: explain_failure])
K -->|rc == 0| M([LLMResponse: stripped stdout])
Reviews (1): Last reviewed commit: "feat(llm_cli): integrate Claude Code CLI..." | Re-trigger Greptile |
|
@davincios @VaibhavUpreti kindly review |
|
🤖 CI passed. Linter didn't scream. Reviewer typed LGTM. @Davidson3556, every machine in this pipeline just slow-clapped. 🖥️✨ 👋 Join us on Discord - OpenSRE : hang out, contribute, or hunt for features and issues. Everyone's welcome. |
|
Done, congratulations on the great work 🎉 @Davidson3556 |
|
thank you. working on two other issues before I go to bed haha @davincios |
Resolves conflicts in auto-generated bot files: docs/daily-updates/overview.mdx, docs/daily-updates/2026-04-30.mdx, docs/daily-updates/2026-05-01.mdx, and README.md (contributors section). All four are produced by scheduled bot workflows in main and have no relationship to this PR; resolved by accepting the upstream main version verbatim. Pulls in 35+ commits from main since this PR was opened, including: refactor of integrations module (Tracer-Cloud#1165), Splunk integration (Tracer-Cloud#791), interactive shell improvements (Tracer-Cloud#1159, Tracer-Cloud#1167), Claude Code CLI provider (Tracer-Cloud#1168), and CI quality gate restoration. None of these touch the OpenSearch wizard, detect_sources, or the validation modules this PR modifies. Refs: Tracer-Cloud#1143

Fixes #1108
Describe the changes you have made in this PR -
Adds Anthropic Claude Code as a non-interactive CLI-backed LLM provider, selectable via
LLM_PROVIDER=claude-code. The integration mirrors the existing OpenAI Codex CLI adapter shape end-to-end (adapter, registry, wizard option, env forwarding, fallback binary paths) so users can runopensre investigateagainst an alert using their localclaudebinary instead of an HTTP API key.Highlights:
ClaudeCodeAdapterinapp/integrations/llm_cli/claude_code.pythat runsclaude -p --output-format textwith the prompt on stdin — strictly non-interactive, no TTY, no approval loops.ANTHROPIC_API_KEYenv, then~/.claude/.credentials.json. On macOS, when neither is present, returnsNoneinstead ofFalsebecauseclaude loginstores OAuth tokens in Keychain on darwin — a strict file-only check would falsely block Keychain-authenticated users.resolve_cli_binaryhelper —CLAUDE_CODE_BINoverride → PATH → npm/Homebrew/volta/pnpm/XDG fallbacks.CLI_PROVIDER_REGISTRYand selectable through the onboarding wizard under "Local CLI providers".CLAUDE_CODE_BINoverride and PATH fallback, fallback paths on macOS / Linux / Windows (mockedsys.platform), registry registration, and env-forwarding security guarantees (specifically:ANTHROPIC_API_KEYis forwarded ONLY by the Claude Code invocation, not via the global allowlist, so credentials don't leak into Codex subprocesses)..env.exampledocumentsCLAUDE_CODE_BINandCLAUDE_CODE_MODEL.runner.py: addedCLAUDE_to_SAFE_SUBPROCESS_ENV_PREFIXESsoCLAUDE_CODE_*settings reach the subprocess; addedUSERandLOGNAMEto safe keys (macOS Keychain item lookup needsUSER).Demo/Screenshot for feature changes and bug fixes -
Code Understanding and AI Usage
Did you use AI assistance (ChatGPT, Claude, Copilot, etc.) to write any part of this code?
If you used AI assistance:
Explain your implementation approach:
Problem: OpenSRE supported Codex CLI as a non-interactive LLM provider but had no equivalent for Claude Code. Issue #1108 asks for the same shape: a one-shot, scriptable CLI invocation that runs to completion under
subprocess.runwith no TTY, no approval prompts, and no REPL.Alternatives considered:
claudewith stdin) — rejected because the issue explicitly excludes interactive flows; the agent runtime can't sit in a REPL waiting for prompts.LLM_PROVIDER=anthropic. The point of this issue is to support users whose only auth isclaude login(no API key on file), via subprocess to their local CLI.claude doctor) — rejected because Claude Code has no clean machine-readable auth-status command, so I went with a fast file-and-env check that doesn't fork a process for the probe at all.Why this implementation:
The repo already has a clean
LLMCLIAdapterProtocol andCLIBackedLLMClientrunner that drive Codex. Implementing Claude Code as another adapter (instead of carving a new pathway) means I get caching of probes, ANSI stripping, env allowlisting, structured output, and timeout handling for free, and the change stays tightly scoped. Following the same shape is also explicitly requested byapp/integrations/llm_cli/AGENTS.md.Key components:
ClaudeCodeAdapter._resolve_binary()— defers to the sharedresolve_cli_binarysoCLAUDE_CODE_BINoverride behaviour matches Codex (blank/invalid env value falls back instead of erroring).ClaudeCodeAdapter._probe_binary()— runsclaude --versionwith a generous 8s timeout (Codex's 3s was too tight: Claude Code does config/cache init at startup and times out when anotherclaudeprocess holds shared state). On version success, defers to_classify_claude_code_auth()._classify_claude_code_auth()— env-var check first, then OAuth credentials file. The macOS branch deliberately returnsNone(notFalse) when both are absent, because Claude Code on darwin stores tokens in Keychain and the runner only blocks invocation onFalse;Nonelets the actualclaude -pcall surface the real auth state viaexplain_failure.ClaudeCodeAdapter.build()— producesargv = (claude, -p, --output-format, text [, --model …])with the prompt on stdin andNO_COLOR=1in env. It explicitly forwardsANTHROPIC_API_KEY/ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKENhere rather than via the global subprocess allowlist — this prevents Anthropic credentials from leaking into other CLI adapters' subprocesses (e.g. Codex).parse()/explain_failure()— minimal, mirroring Codex;parsereturns trimmed stdout,explain_failureincludes the return code and a clipped stderr/stdout for the runner to surface.Edge cases tested:
CLAUDE_CODE_BINpoints at a non-existent path → falls back to PATHclaude --versionfails →installed=Falsewith diagnostic detailFalse(definitive); on macOS →None(Keychain may hold tokens)Nonemodel →--modelflag omitted (CLI default applies)parse()with whitespace-only stdoutexplain_failure()falls back to stdout when stderr is emptysys.platformCLAUDE_*reaches the subprocess;ANTHROPIC_API_KEYreaches Claude Code's subprocess only, not the global allowlist (so it isn't forwarded to Codex runs)