Skip to content

feat: CLI AI providers — use Claude Code, Codex CLI, or Gemini CLI instead of API tokens#2280

Merged
aknysh merged 39 commits intomainfrom
aknysh/atmos-ai-cli-providers
Apr 2, 2026
Merged

feat: CLI AI providers — use Claude Code, Codex CLI, or Gemini CLI instead of API tokens#2280
aknysh merged 39 commits intomainfrom
aknysh/atmos-ai-cli-providers

Conversation

@aknysh
Copy link
Copy Markdown
Member

@aknysh aknysh commented Apr 1, 2026

what

  • Add 3 new CLI AI providers that invoke locally installed AI tools as subprocesses: claude-code, codex-cli, gemini-cli
  • MCP pass-through for CLI providers — configured MCP servers are automatically passed to the CLI tool via its native config format
  • ATMOS_ env var injection* for Codex CLI MCP servers (they don't inherit parent process env)
  • No API keys needed — CLI providers reuse the user's existing subscription (Claude Pro/Max, ChatGPT Plus/Pro, Google account free tier)

Available CLI Providers

Provider Config Key Binary Auth MCP Support
Claude Code claude-code claude Claude Pro/Max subscription Full
OpenAI Codex codex-cli codex ChatGPT Plus/Pro subscription Full
Gemini CLI gemini-cli gemini Google account (free tier) Blocked for personal accounts

MCP Pass-Through

Each provider uses its native config format:

Provider Config Method Approval Flag
Claude Code --mcp-config <temp-file> --dangerously-skip-permissions
Codex CLI Write to ~/.codex/config.toml (backup/restore) --dangerously-bypass-approvals-and-sandbox
Gemini CLI .gemini/settings.json in cwd --approval-mode auto_edit

Quick Start

ai:
  enabled: true
  default_provider: "claude-code"  # or "codex-cli" or "gemini-cli"
  providers:
    claude-code:
      max_turns: 10
brew install --cask claude-code && claude auth login
atmos ai ask "What did we spend on EC2 last month?"

See It in Action

All outputs below are from a real AWS account.

Claude Code — Security Posture

$ atmos ai ask "What is our security posture in us-east-2 region?"

ℹ MCP servers configured: 8 (config: /tmp/atmos-mcp-config.json)
ℹ AI provider: claude-code
👽 Thinking...

  ## Security Posture Summary for us-east-2

  ### ✅ Enabled Security Services (2/6)

   GuardDuty - Fully operational
    • Status: ENABLED
    • Active data sources: CloudTrail, DNS Logs, VPC Flow Logs,
      S3 Logs, EKS Audit Logs, RDS Login Events
    • Current findings: None (clean)

   Inspector - Partially enabled
    • Status: ENABLED but no scan types active

  ### ❌ Disabled Security Services (4/6)
    1. IAM Access Analyzer - Not configured
    2. Security Hub - Not enabled
    3. Trusted Advisor - Error checking status
    4. Macie - Not enabled

  ### Key Recommendations
   High Priority:
    1. Enable Security Hub for centralized findings aggregation
    2. Enable IAM Access Analyzer
    3. Enable at least one Inspector scan type

Codex CLI — EC2 Billing

$ atmos ai ask "What did we spend on EC2 last month in each region?"

ℹ MCP servers configured: 8 (in ~/.codex/config.toml)
ℹ AI provider: codex-cli
👽 Thinking...

   For last month, I used the previous calendar month:
   March 1, 2026 through April 1, 2026.

   EC2 spend by region:
    • us-east-2: $88.10

   Cost Explorer returned only us-east-2 for Amazon Elastic Compute
   Cloud - Compute, so that appears to be the only region with EC2
   spend in that period.

Both providers automatically selected the right MCP server and returned answers from real AWS data — no manual server selection needed.

why

Why run Claude Code through Atmos instead of the other way around?

Both directions work — you can start a Claude Code session and call Atmos inside it, or use Atmos to invoke Claude Code. Here's when each approach makes sense:

Starting Claude Code → calling Atmos (via MCP server)

claude
# Then inside Claude Code: @atmos-expert "list all stacks"

How it works: Claude Code connects to the Atmos MCP server (atmos mcp start) and uses Atmos tools directly from the IDE.

Best for:

  • Long interactive coding sessions where you need AI + infrastructure context
  • IDE-driven workflows (Claude Code in VS Code, Cursor, etc.)
  • When you want Claude Code's full toolset (file editing, bash, web search) alongside Atmos tools
  • Exploratory work — "help me understand and modify this infrastructure"

Limitations:

  • Requires setting up the MCP server in your IDE config (.claude/mcp.json)
  • Only has access to native Atmos tools — no external AWS MCP servers (billing, security, IAM) unless you configure them separately in Claude Code's own MCP config
  • Auth credentials for AWS MCP servers must be configured manually in the IDE config

Atmos → starting Claude Code (this PR)

atmos ai ask "What did we spend on EC2 last month?"

How it works: Atmos invokes claude -p as a subprocess, passing MCP servers with auth credentials pre-configured.

Best for:

  • Quick one-shot queries from the terminal (atmos ai ask)
  • When you need external MCP servers (AWS billing, security, IAM, CloudTrail) with automatic auth
  • CI-adjacent workflows — scripting AI analysis into your workflow
  • Multi-provider switching — same question to Claude Code, Codex, or Gemini
  • Teams that manage MCP servers centrally in atmos.yaml — one config, every developer gets the same tools

Key advantage — centralized MCP + auth orchestration:

# atmos.yaml — one config for the whole team
mcp:
  servers:
    aws-billing:
      command: uvx
      args: ["awslabs.billing-cost-management-mcp-server@latest"]
      identity: "readonly"   # Atmos Auth handles SSO → credentials automatically

auth:
  providers:
    aws-sso:
      kind: aws/iam-identity-center
      start_url: "https://your-org.awsapps.com/start"
  identities:
    readonly:
      kind: aws/permission-set
      provider: aws-sso
      principal:
        permission_set: "ReadOnlyAccess"
        account:
          id: "123456789012"

When you run atmos ai ask, Atmos:

  1. Authenticates via SSO (once, cached)
  2. Generates MCP config with credential injection (atmos auth exec -i readonly --)
  3. Injects toolchain PATH so uvx is available
  4. Passes everything to Claude Code via --mcp-config
  5. Claude Code uses AWS MCP tools with real credentials — zero manual setup

With the "Claude Code first" approach, each developer would need to manually configure AWS credentials, MCP server paths, and toolchain binaries in their personal IDE config.

Summary

Claude Code → Atmos Atmos → Claude Code
Setup MCP server in IDE config atmos.yaml (shared)
Auth Manual per-developer Centralized via Atmos Auth
External MCP Manual config per tool Automatic from atmos.yaml
Toolchain Must install uvx globally Atmos Toolchain manages it
Best for IDE coding sessions Terminal queries, team workflows
Multi-provider Claude only Claude, Codex, Gemini
CI/CD Not practical atmos ai exec with JSON output

They're complementary, not competing. Use Claude Code directly for coding sessions in your IDE. Use atmos ai ask/chat for quick infrastructure queries with centralized MCP + auth. Both can be configured in the same project.

Additional motivation

  • Many developers already have Claude Code or Codex installed with active subscriptions ($20-200/mo). CLI providers let them reuse that investment instead of purchasing separate API tokens.
  • CLI providers handle their own tool execution loop, which means they can use all their built-in tools (file search, code editing, web browsing) alongside MCP-provided AWS tools.
  • This completes the AI provider ecosystem: 7 API providers + 3 CLI providers = 10 total providers.

Key Findings During Implementation

Codex CLI

  • -c flag overrides do NOT register MCP servers as tools — must write to ~/.codex/config.toml
  • --full-auto only auto-approves file writes, not MCP tool calls — need --dangerously-bypass-approvals-and-sandbox
  • JSONL output uses item.type="agent_message" with item.text directly (not documented "message" with nested content[])
  • MCP servers don't inherit parent env — ATMOS_* vars must be explicitly injected

Gemini CLI

  • MCP is blocked server-side for ALL personal Google accounts (oauth-personal auth) regardless of subscription tier
  • This is a Google infrastructure restriction (proxy cloudcode-pa.googleapis.com has MCP feature flag disabled)
  • The Atmos implementation is complete — MCP will work when Google enables it for personal accounts
  • Switching to API key auth enables MCP but loses the free-tier benefit (equivalent to existing gemini API provider)

New Files

Code

  • pkg/ai/agent/claudecode/ — Claude Code CLI provider with MCP pass-through
  • pkg/ai/agent/codexcli/ — OpenAI Codex CLI provider with ~/.codex/config.toml backup/restore and ATMOS_* injection
  • pkg/ai/agent/geminicli/ — Gemini CLI provider with .gemini/settings.json in cwd
  • pkg/mcp/client/mcpconfig.go — Shared MCP config generation (env uppercasing, PATH dedup, toolchain injection)

Documentation

  • Blog post: website/blog/2026-04-01-ai-cli-providers.mdx
  • Example: examples/ai-claude-code/ — Complete example with AWS MCP servers and automatic auth
  • PRD: docs/prd/atmos-ai-local-providers.md (v1.6)
  • Website docs updated: ai/ai.mdx, ai/troubleshooting.mdx, cli/configuration/ai/index.mdx, cli/configuration/ai/providers.mdx, cli/configuration/mcp/index.mdx, cli/commands/ai/ask.mdx, cli/commands/ai/exec.mdx, cli/commands/ai/usage.mdx
  • Roadmap: 4 shipped milestones added to ai-assistant initiative

Tests

  • 40+ unit tests across all three CLI provider packages
  • Coverage: claudecode 63%, codexcli 72%, geminicli 66%, mcp/client 83%, base 88%

references

Summary by CodeRabbit

  • New Features

    • Added three CLI AI providers: Claude Code, Codex CLI, and Gemini CLI (use local CLI subscriptions; no API keys)
    • CLI providers support MCP pass-through (Atmos forwards configured MCP servers to the CLI)
    • New per-provider settings: binary, max_turns, max_budget_usd, allowed_tools, full_auto
    • UI now displays the selected AI provider
    • New example project demonstrating Claude Code CLI usage and Terraform VPC example
  • Documentation

    • New quick-starts, guides, troubleshooting, and PRD updates distinguishing API vs CLI providers and MCP pass-through workflows

aknysh and others added 30 commits March 31, 2026 15:51
Add 3 new AI providers that invoke locally installed CLI tools as subprocesses,
reusing the user's existing subscriptions instead of requiring API tokens.

Providers:
- claude-code: invokes `claude -p --output-format json` (Claude Pro/Max)
- codex-cli: invokes `codex exec --json` (ChatGPT Plus/Pro)
- gemini-cli: invokes `gemini -p --output-format json` (Google account, free tier)

Each provider:
- Implements full registry.Client interface (5 methods + GetModel/GetMaxTokens)
- Auto-detects binary via exec.LookPath with helpful install hints
- Parses structured JSON/JSONL output with plain text fallback
- Returns ErrCLIProviderToolsNotSupported for tool-use methods
- Registered via init() in register.go

Schema:
- Add CLI-specific fields to AIProviderConfig: binary, max_turns, max_budget_usd,
  allowed_tools, full_auto

Tests: 40 unit tests across 3 providers
PRD: Updated with tool/MCP integration analysis and phase statuses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When mcp.servers is configured in atmos.yaml and a CLI provider (claude-code)
is selected, Atmos generates a temp .mcp.json and passes it to the CLI tool
via --mcp-config. This gives Claude Code access to all configured MCP servers
(AWS billing, security, IAM, etc.) with automatic auth credential injection.

Implementation:
- Add pkg/mcp/client/mcpconfig.go — shared MCP config generation with toolchain
  PATH injection and auth wrapping
- Update claudecode client to capture MCP servers and generate temp config
- Resolve toolchain PATH and inject into server env so uvx/npx are available
  when the CLI tool starts MCP server subprocesses
- Temp config cleaned up after CLI tool exits

Tests:
- 12 tests for mcpconfig (BuildMCPJSONEntry, GenerateMCPConfig, WriteMCPConfigToTempFile,
  copyEnv, toolchain PATH injection, immutability)
- 4 new tests for claudecode (resolveToolchainPATH, MCP server capture)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Create examples/ai-claude-code/ with full Claude Code provider config,
  8 AWS MCP servers, auth, toolchain, and comprehensive README
- README explains API vs CLI providers, mixing providers, comparison table
- Update infra-live .atmos.d/ai.yaml to use claude-code as default provider
  (anthropic kept as fallback)
- Update atmos-ai-global-flag.md status to Shipped (v3.1)
- Update atmos-ai-local-providers.md PRD with Phase 3 MCP pass-through details,
  toolchain PATH injection strategy, and phase statuses

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CLI providers (claude-code, codex-cli, gemini-cli) handle MCP via --mcp-config
pass-through. They should not trigger MCP server routing or start servers
through the Atmos tool registry — that path is for API providers only.

- Add isCLIProvider() check in initializeAIToolsAndExecutor
- Skip registerMCPServerTools when provider is a CLI provider
- Add 7 table-driven tests for isCLIProvider

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix Viper lowercasing MCP server env keys (aws_region → AWS_REGION)
  in both atmos mcp export and CLI provider MCP pass-through
- Pre-generate MCP config in NewClient so info shows before "Thinking..."
- Show AI provider name after tool initialization
- Add TestCopyEnv_UppercasesKeys verifying lowercase → UPPERCASE restoration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…EADME

- Add --dangerously-skip-permissions when MCP servers are configured
  (required because -p mode cannot show approval prompts)
- Update PRD: Phase 3 marked as shipped for Claude Code
- Add "See It in Action" section to ai-claude-code example README
  with real security posture output

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gemini CLI reads MCP servers from .gemini/settings.json (project-level).
Atmos creates a temp directory with the settings file and sets cmd.Dir
so Gemini CLI picks it up. Uses --yolo flag to auto-approve tool calls.

- Add mcpServers, toolchainPATH, mcpSettingsDir to Gemini CLI Client
- Add writeMCPSettingsFile to create temp .gemini/settings.json
- Inject toolchain PATH and uppercase env keys
- Use errUtils.ErrWrapFormat for consistent error wrapping
- 15 tests including settings file generation, auth wrapping, toolchain PATH
- Update PRD: Gemini CLI MCP pass-through marked as shipped

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codex CLI reads MCP servers from .codex/config.toml using TOML
[mcp_servers.<name>] tables. Atmos creates a temp directory with the
config file and sets cmd.Dir so Codex CLI picks it up.

- Add writeMCPConfigTOML to generate TOML format config
- Add writeTOMLServer helper for individual server sections
- Inject toolchain PATH and uppercase env keys
- Uses --full-auto flag for non-interactive tool approval
- 18 tests including TOML generation, auth wrapping, args formatting
- Coverage: 49.3% → 57.6%
- Update PRD: all 3 providers now have MCP pass-through shipped
- Add Codex CLI config and advanced config links to references

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e README

- Add EC2 billing query example to See It in Action
- Add 11-step execution flow diagram showing complete chain:
  atmos.yaml → toolchain → MCP config → Claude Code → auth exec →
  MCP server → AWS API → AI analysis → response
- Clarify that MCP tool returns raw data, AI analyzes it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add TestSendMessageWithToolsAndHistory_NotSupported for codexcli and geminicli
- Add TestFormatMessages and TestFormatMessages_Empty for codexcli and geminicli
- Coverage: codexcli 57.6% → 63.2%, geminicli 47.1% → 53.9%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rmat

- Write .gemini/settings.json to cwd (not temp dir) to respect Trusted Folders
- Fix JSON response field: "result" → "response" (actual Gemini CLI format)
- Use --approval-mode auto_edit instead of --yolo (admin may block yolo)
- Pass --allowed-mcp-server-names to explicitly enable configured servers
- Add filterStderr to strip deprecation warnings from error output
- Pass prompt as --prompt flag value (not stdin)
- Update PRD with Trusted Folders, enterprise admin restrictions, workarounds
- Update tests for cwd-based settings and new response format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The toolchain PATH injection was creating duplicated entries because
os.Getenv("PATH") already contains duplicates from the shell environment.

- Add deduplicatePATH() to remove duplicate PATH entries while preserving order
- Apply deduplication in injectToolchainPATH after combining toolchain + base PATH
- Add TestDeduplicatePATH (5 cases) and TestInjectToolchainPATH_Deduplicates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Google disables MCP on the server-side proxy for personal Google accounts
using oauth-personal auth. The gemini-cli provider works for prompt-only
queries but cannot use external MCP servers with the free tier. Switching
to API key auth enables MCP but makes it equivalent to the existing gemini
API provider.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s regardless of tier

The MCP restriction is based on account type (oauth-personal), not subscription
tier. Even paid Gemini 3 Pro users are affected. Added auth mode comparison table,
investigation details, proxy architecture explanation, and future outlook.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…njection

Three fixes for Codex CLI provider:

1. ExtractResult now handles "agent_message" item type (Codex returns
   item.type="agent_message" with text on item.text directly, not the
   "message" type with nested content array from the API docs).

2. MCP servers passed via -c flags instead of temp .codex/config.toml
   (Codex only reads ~/.codex/config.toml, not project-level config).

3. Use --dangerously-bypass-approvals-and-sandbox when MCP servers are
   configured (--full-auto only auto-approves file writes, not MCP tool
   calls). This is safe because MCP servers are explicitly configured
   by the user in atmos.yaml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Phase 3 now shipped for Claude Code AND Codex CLI
- Codex CLI uses -c flag overrides (not temp config.toml)
- --dangerously-bypass-approvals-and-sandbox required for MCP tool calls
- Document Codex output format difference (agent_message vs message)
- Add MCP config delivery summary table per provider
- Update comparison tables and feature descriptions for consistency

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
-c flag overrides do NOT register MCP servers as tools in Codex CLI.
Codex only loads MCP servers from ~/.codex/config.toml. Changed approach
to write MCP servers to the global config with backup/restore:

1. Back up existing ~/.codex/config.toml content
2. Append MCP server TOML sections
3. After Codex exits, restore the original config

Removed unused buildMCPConfigArgs function.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codex CLI MCP servers don't inherit the parent process environment.
Auth-related vars like ATMOS_PROFILE must be explicitly passed in the
server env section of config.toml. Without this, atmos auth exec fails
with "identity not found" because it can't discover the auth config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Codex uses ~/.codex/config.toml with backup/restore (not -c flags)
- ATMOS_* env vars injected into MCP server config for auth discovery
- Documented that Codex MCP servers don't inherit parent process env
- Updated config delivery summary table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PRD v1.6 (final revision):
- Fix Codex JSONL output example to match actual agent_message format
- Update Codex provider code example to match implementation
- Fix Gemini response struct field (response, not result)
- Correct Codex MCP line from "-c flags" to "~/.codex/config.toml"
- Replace generic MCP pass-through description with per-provider details
- Remove outdated "Implementation steps" section (already shipped)
- Add full_auto vs dangerously-bypass note to config example

Example fixes:
- Remove --mcp flag references (not supported for CLI providers)
- Increase timeout from 120s to 300s for MCP server startup
- Add note that tools section is for API providers only
- Add mock vpc component and stack config for self-contained example
- Add MCP Support column to CLI providers table with Gemini note
- Fix flow diagrams: remove smart routing, clarify all servers passed
- Remove .tool-versions reference (not in example)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Updated 8 docs files to mention CLI providers (claude-code, codex-cli, gemini-cli):

- ai/ai.mdx: CLI provider Quick Start, Providers table, MCP pass-through notes
- ai/troubleshooting.mdx: CLI provider troubleshooting section
- cli/configuration/ai/index.mdx: CLI provider Quick Start, provider count
- cli/configuration/ai/providers.mdx: CLI Providers section with settings and examples
- cli/configuration/mcp/index.mdx: CLI Provider MCP Pass-Through section
- cli/commands/ai/usage.mdx: Updated provider list in intro
- cli/commands/ai/ask.mdx: Smart routing note for CLI providers
- cli/commands/ai/exec.mdx: Provider flag list, smart routing note

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Covers Claude Code, Codex CLI, and Gemini CLI as new Atmos AI providers.
Includes real-world examples showing security posture assessment (Claude Code)
and EC2 billing query (Codex CLI) using MCP pass-through.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 26 new unit tests across CLI provider packages:
- claudecode: parseResponse edge cases, formatMessages, MCP config path
- codexcli: ATMOS_* env var injection, extractTextFromEvent variants,
  collectAtmosEnvVars, injectAtmosEnvVars (overwrite protection, nil env)
- geminicli: filterStderr (deprecation/YOLO/empty/meaningful), parseResponse,
  formatMessages edge cases

Coverage: codexcli 62.4% → 68.2%, geminicli 46.6% → 57.8%.
Remaining uncovered code is Send* methods requiring actual CLI binaries.

Roadmap: add 4 shipped CLI provider milestones to ai-assistant initiative.
Remove planned "Auto-detection" milestone. Progress 96% (22/23).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds three CLI-backed AI providers (Claude Code, Codex CLI, Gemini CLI), MCP pass-through and JSON/TOML config tooling, schema/config fields and sentinel errors for CLI providers, many unit tests, docs/examples/PRD/blog updates, provider registration wiring, and dependency/NOTICE updates.

Changes

Cohort / File(s) Summary
Claude Code CLI Provider
pkg/ai/agent/claudecode/client.go, pkg/ai/agent/claudecode/register.go, pkg/ai/agent/claudecode/client_test.go
New Claude Code CLI client: subprocess invocation, JSON parsing, temp MCP config pass-through, unsupported tool-methods, registration and tests.
Codex CLI Provider
pkg/ai/agent/codexcli/client.go, pkg/ai/agent/codexcli/register.go, pkg/ai/agent/codexcli/client_test.go
New Codex CLI client: JSONL event parsing, global ~/.codex/config.toml backup/restore, ATMOS env injection, MCP handling, unsupported tool-methods, registration and tests.
Gemini CLI Provider
pkg/ai/agent/geminicli/client.go, pkg/ai/agent/geminicli/register.go, pkg/ai/agent/geminicli/client_test.go
New Gemini CLI client: .gemini/settings.json MCP handling with backup/restore, JSON parsing/fallback, stderr filtering, unsupported tool-methods, registration and tests.
Provider Registration & CLI gating
cmd/ai/init.go, cmd/ai/init_test.go, pkg/ai/analyze/providers.go, pkg/ai/factory_test.go, pkg/schema/ai.go
Added CLI-provider detection and test (isCLIProvider), blank imports to register new providers in analyze/test wiring, and schema fields for CLI config (binary, max_turns, max_budget_usd, allowed_tools, full_auto).
MCP Configuration Support
pkg/mcp/client/mcpconfig.go, pkg/mcp/client/mcpconfig_test.go, cmd/mcp/client/export.go
MCP JSON/TOML types and generators, env uppercase normalization, toolchain PATH injection/deduping, identity wrapping via atmos auth exec, temp-file writer; export.go uppercases env keys for exported MCP JSON; tests added.
Base helpers & message formatting
pkg/ai/agent/base/config.go, pkg/ai/agent/base/messages.go, pkg/ai/agent/base/*_test.go
Added ResolveToolchainPATH (reads toolchain PATH via dependencies) and FormatMessagesAsPrompt; unit tests included.
Error Handling
errors/errors.go
Added CLI-provider sentinel errors: ErrCLIProviderBinaryNotFound, ErrCLIProviderExecFailed, ErrCLIProviderParseResponse, ErrCLIProviderToolsNotSupported.
Docs, PRD & Examples
docs/prd/atmos-ai-local-providers.md, examples/ai-claude-code/..., multiple website/docs/*, website/blog/2026-04-01-ai-cli-providers.mdx
New PRD, example project for Claude Code (configs + Terraform sample), website docs/troubleshooting/usage updates, and blog post describing CLI providers and MCP pass-through.
Dependencies & Notices
go.mod, NOTICE
Bumped multiple Go dependencies and updated NOTICE license URLs; added BSD entry for andybalholm/brotli/flate and updated andybalholm/brotli.
Roadmap
website/src/data/roadmap.js
Updated quarter statuses and added CLI-provider / MCP pass-through milestones.
Other test wiring
pkg/ai/factory_test.go, pkg/ai/analyze/providers.go
Blank imports added so new providers are registered during tests and analysis initialization.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Atmos
    participant CLITool as CLI Provider
    participant MCP as MCP Server

    User->>Atmos: atmos ai ask --provider claude-code
    Atmos->>Atmos: Load config, detect CLI provider
    alt MCP servers configured
        Atmos->>Atmos: Generate temp MCP JSON config
        Atmos->>Atmos: Write /tmp/mcp-*.json
    end
    Atmos->>Atmos: Resolve tool binary and build args
    Atmos->>CLITool: Run subprocess with stdin and MCP flags
    CLITool->>CLITool: CLI selects MCP server / executes tools
    alt MCP tool requires auth
        CLITool->>MCP: Request tool run
        MCP->>Atmos: Need auth
        Atmos->>MCP: atmos auth exec -i <id> -- <cmd>
        MCP-->>CLITool: Tool result
    end
    CLITool-->>Atmos: stdout JSON / JSONL
    Atmos->>Atmos: Parse response, cleanup temp files
    Atmos-->>User: Formatted response
Loading
sequenceDiagram
    participant Atmos
    participant APIProvider as API Provider
    participant MCP as MCP Server
    participant CLITool as CLI Provider

    rect rgba(200, 150, 100, 0.5)
    Note over Atmos,APIProvider: API Provider Flow (Smart Routing)
    end

    Atmos->>Atmos: Multiple MCP servers configured
    Atmos->>APIProvider: Lightweight routing call
    APIProvider-->>Atmos: Recommended server
    Atmos->>Atmos: Start only selected MCP server
    Atmos->>APIProvider: Send prompt + tool schemas
    APIProvider->>MCP: tool_use call
    MCP-->>APIProvider: Tool result
    APIProvider-->>Atmos: Final response

    rect rgba(100, 150, 200, 0.5)
    Note over Atmos,CLITool: CLI Provider Flow (Pass-Through)
    end

    Atmos->>Atmos: Multiple MCP servers configured
    Atmos->>Atmos: Write MCP config and pass ALL servers to CLI
    Atmos->>Atmos: Skip smart routing
    Atmos->>CLITool: Invoke CLI with MCP config
    CLITool->>MCP: CLI chooses server(s) internally
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • osterman
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main feature: introducing three CLI-based AI providers that use locally installed tools instead of API tokens.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aknysh/atmos-ai-cli-providers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
NOTICE (1)

1-1809: ⚠️ Potential issue | 🟠 Major

Regenerate NOTICE to fix CI drift.

Dependency Review is failing because this file is not in sync with the generator output. Please run ./scripts/generate-notice.sh and commit the regenerated NOTICE as-is.

Based on learnings, “In the cloudposse/atmos repository, the NOTICE file is programmatically generated and should not be manually edited.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@NOTICE` around lines 1 - 1809, The NOTICE file is out of sync with the
generator and causing CI drift; run the generator script and commit the result:
execute ./scripts/generate-notice.sh to regenerate the NOTICE file, verify the
output replaces the existing NOTICE (do not manually edit NOTICE), then
add/commit the regenerated NOTICE and push the change so Dependency Review
passes.
🧹 Nitpick comments (13)
cmd/ai/init_test.go (1)

530-549: Solid table-driven test.

Covers the primary classification cases well. Consider adding edge cases for input normalization robustness (e.g., "Claude-Code", " claude-code ") if config loading doesn't already normalize provider names upstream.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/ai/init_test.go` around lines 530 - 549, Add edge-case inputs to
TestIsCLIProvider to verify normalization: include variations like "Claude-Code"
(different casing), " claude-code " (leading/trailing whitespace), and other
whitespace/case permutations to ensure isCLIProvider handles normalization
consistently; update the tests slice in TestIsCLIProvider to add these cases
with expected true (and any negative variants as needed) so the test exercises
isCLIProvider's robustness.
pkg/schema/ai.go (1)

32-38: Comment missing terminal period.

Line 33 comment should end with a period per the godot linter requirement.

-	// CLI provider fields (for claude-code, codex-cli, gemini-cli).
+	// CLI provider fields (for claude-code, codex-cli, gemini-cli providers).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/schema/ai.go` around lines 32 - 38, The comment "CLI provider fields (for
claude-code, codex-cli, gemini-cli)" above the struct fields in pkg/schema/ai.go
is missing a terminal period; update that top-line comment so it ends with a
period to satisfy the godot linter (edit the comment that precedes the Binary,
MaxTurns, MaxBudgetUSD, AllowedTools, and FullAuto fields).
pkg/ai/factory_test.go (1)

13-16: Consider adding test cases for CLI providers.

The blank imports register the new providers, but TestNewClient lacks test cases for claude-code, codex-cli, and gemini-cli. These would exercise the factory routing for CLI providers.

CLI providers may need binary detection or special skip logic similar to the existing API key handling on lines 163-169.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/factory_test.go` around lines 13 - 16, Add test cases in TestNewClient
to cover the CLI providers "claude-code", "codex-cli", and "gemini-cli" so the
factory routing for CLI agents is exercised; for each new test case, follow the
pattern used for existing providers in TestNewClient (create the client via
NewClient and assert the expected provider type) and include binary-detection or
skip logic similar to the existing API-key checks—i.e., probe for the required
CLI binary (or environment) and call t.Skipf when the binary is not present so
the test suite remains stable on systems without the CLI installed.
cmd/mcp/client/export.go (1)

114-125: Make uppercaseEnvKeys consistently return a copy.

Right now empty maps are returned as-is, which contradicts the function contract and creates inconsistent aliasing behavior.

Suggested patch
 func uppercaseEnvKeys(env map[string]string) map[string]string {
-	if len(env) == 0 {
-		return env
-	}
+	if env == nil {
+		return nil
+	}
 	result := make(map[string]string, len(env))
 	for k, v := range env {
 		result[strings.ToUpper(k)] = v
 	}
 	return result
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/mcp/client/export.go` around lines 114 - 125, The function
uppercaseEnvKeys currently returns the original env map when it's empty, causing
inconsistent aliasing; change it so it always returns a new map copy: allocate
result := make(map[string]string, len(env)) unconditionally, copy each key/value
with result[strings.ToUpper(k)] = v (the loop will be skipped for len==0) and
return result; update the function uppercaseEnvKeys to never return the input
map directly so callers always receive a fresh map.
pkg/mcp/client/mcpconfig_test.go (1)

76-85: Consider adding an empty servers edge case.

GenerateMCPConfig with an empty map is a valid input. A quick test ensures it returns an empty MCPServers map without panicking.

Optional test case
func TestGenerateMCPConfig_EmptyServers(t *testing.T) {
	servers := map[string]schema.MCPServerConfig{}
	config := GenerateMCPConfig(servers, "")
	assert.NotNil(t, config.MCPServers)
	assert.Empty(t, config.MCPServers)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/mcp/client/mcpconfig_test.go` around lines 76 - 85, Add a unit test
covering the empty-servers edge case for GenerateMCPConfig: create an empty
map[string]schema.MCPServerConfig, call GenerateMCPConfig with it, and assert
that the returned config.MCPServers is non-nil and empty (no panic). Place the
new test (e.g. TestGenerateMCPConfig_EmptyServers) alongside
TestGenerateMCPConfig to ensure GenerateMCPConfig handles empty input
gracefully.
website/docs/cli/configuration/ai/providers.mdx (1)

104-119: Consider simplifying the binary description.

The reference to exec.LookPath is Go implementation detail. Users just need to know it finds the binary on PATH.

Suggested wording
   <dt>`binary`</dt>
-  <dd>Path to the CLI binary. Optional — defaults to `exec.LookPath` (finds it on PATH).</dd>
+  <dd>Path to the CLI binary. Optional — defaults to finding it on PATH.</dd>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@website/docs/cli/configuration/ai/providers.mdx` around lines 104 - 119, The
`binary` field description includes an implementation detail (`exec.LookPath`)
that should be removed; update the `<dd>` text for `binary` to a simpler
user-facing sentence like "Path to the CLI binary. Optional — defaults to
locating the binary on the PATH." Replace the existing phrase that mentions
`exec.LookPath` with this simpler wording and keep the rest of the list
unchanged; target the `binary` entry in the providers.mdx content (currently
mentioning `exec.LookPath`) when making the edit.
pkg/ai/agent/codexcli/client.go (1)

331-340: Consider logging restore failures.

restoreGlobalConfig silently ignores write/remove errors. While this is acceptable for cleanup code, logging at debug level could help troubleshoot issues where user config remains corrupted.

Optional: add debug logging for restore failures
 func (c *Client) restoreGlobalConfig() {
 	configPath := codexConfigPath()
 	if c.configBackedUp {
-		_ = os.WriteFile(configPath, c.originalConfig, configFilePerms)
+		if err := os.WriteFile(configPath, c.originalConfig, configFilePerms); err != nil {
+			// Log at debug level — cleanup failures shouldn't crash the command.
+			ui.Debug(fmt.Sprintf("Failed to restore %s: %s", configPath, err))
+		}
 	} else {
 		// No original config existed — remove the file we created.
-		_ = os.Remove(configPath)
+		if err := os.Remove(configPath); err != nil && !os.IsNotExist(err) {
+			ui.Debug(fmt.Sprintf("Failed to remove %s: %s", configPath, err))
+		}
 	}
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/agent/codexcli/client.go` around lines 331 - 340, The
restoreGlobalConfig method currently ignores errors from os.WriteFile and
os.Remove; update restoreGlobalConfig (in Client.restoreGlobalConfig) to capture
the error returned by os.WriteFile and os.Remove and emit a debug-level log when
those operations fail (e.g., use the client logger on c if available, otherwise
the standard log package) including configPath and the error; preserve the
existing behavior (do not return the error) so this remains cleanup code but
adds diagnostic logging to help troubleshoot corrupted user configs.
pkg/ai/agent/geminicli/client.go (4)

246-250: Path construction: redundant ".".

filepath.Join(".", ".gemini") returns .gemini — the leading "." is unnecessary noise.

-	geminiDir := filepath.Join(".", ".gemini")
+	geminiDir := ".gemini"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/agent/geminicli/client.go` around lines 246 - 250, The code constructs
geminiDir using filepath.Join(".", ".gemini") which is redundant; change the
construction to simply use ".gemini" (e.g., set geminiDir := ".gemini") and keep
the existing os.MkdirAll(geminiDir, settingsDirPerms) and error wrapping
(ErrMCPConfigWriteFailed) so the behavior and error handling in this block
(filepath.Join usage, geminiDir variable, os.MkdirAll call, and the fmt.Errorf
wrapping) remain the same but without the unnecessary "." join.

119-126: Non-deterministic MCP server ordering.

Iterating over c.mcpServers map produces non-deterministic order of server names joined into --allowed-mcp-server-names. This isn't a functional bug, but it can make debugging harder and causes non-reproducible CLI invocations.

♻️ Sort server names for deterministic output
+import "slices"
+
 	// Explicitly allow configured MCP servers by name.
 	if len(c.mcpServers) > 0 {
 		var serverNames []string
 		for name := range c.mcpServers {
 			serverNames = append(serverNames, name)
 		}
+		slices.Sort(serverNames)
 		args = append(args, "--allowed-mcp-server-names", strings.Join(serverNames, ","))
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/agent/geminicli/client.go` around lines 119 - 126, Iterating the map
c.mcpServers to build serverNames yields non-deterministic ordering; sort the
collected names before joining so the args slice gets a stable value for
"--allowed-mcp-server-names". In the code that gathers names from c.mcpServers
(the block that appends to args), collect names into serverNames as now, then
sort.Strings(serverNames) and finally use strings.Join(serverNames, ",") when
appending to args (refer to c.mcpServers, serverNames, args).

279-302: Consider filtering more stderr noise patterns.

The filter is thoughtful, but Gemini CLI may emit other warning patterns (e.g., ExperimentalWarning, punycode). You might want to make this extensible or add a catch-all for common Node.js warnings.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/agent/geminicli/client.go` around lines 279 - 302, The filterStderr
function currently skips only a few hardcoded patterns; update it to cover
additional common Node/CLI noise by introducing a reusable list or regex slice
of warning patterns (e.g., include "ExperimentalWarning", "punycode", "Warning",
"deprecated" and "--trace-warnings") and use that list to skip lines, or make
the patterns configurable/extendable so future patterns can be added without
changing filterStderr; locate the function filterStderr in
pkg/ai/agent/geminicli/client.go and replace the individual if checks with a
loop over the pattern list (or compiled regexes) to filter out matches and keep
the rest.

36-44: Dead field: mcpSettingsDir is never read.

The mcpSettingsDir field is assigned at line 96 but never used elsewhere in the code. If it's not needed (since settings are written to cwd), remove it from the struct to avoid confusion.

 type Client struct {
 	binaryPath     string
 	model          string
 	mcpServers     map[string]schema.MCPServerConfig
 	toolchainPATH  string
-	mcpSettingsDir string // Temp dir containing .gemini/settings.json for MCP config.
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/agent/geminicli/client.go` around lines 36 - 44, The Client struct
contains a dead field mcpSettingsDir that is assigned but never read; remove the
mcpSettingsDir field from the Client struct declaration and also remove its
assignment in the client constructor/initializer (wherever mcpSettingsDir is
set) and any associated comment; ensure no other code references mcpSettingsDir
and update related comments to reflect that MCP settings are written to the
current working directory.
pkg/ai/agent/claudecode/client.go (2)

278-289: Duplicate code: formatMessages is identical in both providers.

This function is also duplicated from geminicli/client.go. Consider extracting to a shared location.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/agent/claudecode/client.go` around lines 278 - 289, The formatMessages
function is duplicated across providers (found in this file as formatMessages
and also in geminicli/client.go); extract it into a shared package (e.g.,
pkg/ai/agent/shared or pkg/ai/agent/util) and replace both local implementations
with a single exported helper like FormatMessages(messages []types.Message) that
returns the joined prompt; update callers in claudecode/client.go and
geminicli/client.go to import and use the shared helper and remove the duplicate
function definitions.

100-117: Extract resolveToolchainPATH to pkg/ai/agent/base/ to eliminate triplication across providers.

The function is duplicated across three providers: claudecode, geminicli, and codexcli. Moving it to pkg/ai/agent/base/ keeps shared toolchain logic in one place and prevents drift. The base/ package already exists for common agent functionality.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/ai/agent/claudecode/client.go` around lines 100 - 117, Extract the
duplicated resolveToolchainPATH function into the shared package
pkg/ai/agent/base by moving the implementation (the logic that calls
dependencies.LoadToolVersionsDependencies, dependencies.NewEnvironmentFromDeps
and scans tenv.EnvVars() for "PATH=") into a new function
base.ResolveToolchainPATH, update callers in claudecode, geminicli, and codexcli
to call base.ResolveToolchainPATH(atmosConfig) instead of their local copies,
and remove the duplicated implementations so there is a single canonical
implementation in base.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/ai/init.go`:
- Around line 51-52: The comment above the mcpMgr declaration is inaccurate
about how MCP is passed; update the comment in cmd/ai/init.go (the comment that
currently mentions “--mcp-config”) to say that CLI providers skip MCP handling
because MCP is passed through by provider-specific mechanisms (e.g., different
flags or delivery methods for Codex/Gemini), and make the same wording change
for the other instance referenced around the mcp-related block at lines 300-302
so both comments accurately state “provider-specific pass-through” rather than
the concrete “--mcp-config”.

In `@pkg/ai/agent/claudecode/client.go`:
- Around line 83-95: NewClient calls mcpclient.WriteMCPConfigToTempFile and
stores the resulting path in client.mcpConfigPath but neither documents nor
cleans up the temp file; implement a Close() method on the client (e.g., func (c
*Client) Close() error) that checks c.mcpConfigPath and removes the file if
present, update NewClient/Client docs to mention caller must call Close(), and
ensure tests (client_test.go) can call client.Close() to remove the generated
MCP config; reference WriteMCPConfigToTempFile, NewClient, client.mcpConfigPath,
and Close().

In `@pkg/ai/agent/codexcli/client_test.go`:
- Around line 155-179: TestWriteAndRestoreGlobalConfig currently writes to the
real user config via writeMCPToGlobalConfig/restoreGlobalConfig; fix it by
making the test operate on a temporary config location: create a temp dir
(os.MkdirTemp), set the HOME (or other env used by codexConfigPath) for the test
using t.Setenv so codexConfigPath resolves inside that temp dir, call
writeMCPToGlobalConfig and defer restoreGlobalConfig immediately after creating
the client so restore runs even on failure, and remove the temp dir at the end;
this keeps all operations (writeMCPToGlobalConfig, restoreGlobalConfig, and the
configBackedUp checks) confined to the temp environment.
- Around line 74-98: The test is writing to the real Codex config
(~/.codex/config.toml); modify NewClient (or codexConfigPath) to honor an
override (e.g., ATMOS_CODEX_CONFIG_PATH env var) or inject a configPath
parameter so tests can point to a temp file, update
TestNewClient_MCPServers_Captured_WhenConfigured to set that env var (or pass
the temp path) and assert against the temp file, and fix restoreGlobalConfig to
return or log errors instead of silently discarding them (reference NewClient,
codexConfigPath, restoreGlobalConfig, and the
TestNewClient_MCPServers_Captured_WhenConfigured test to locate the code to
change).

In `@pkg/ai/agent/geminicli/client_test.go`:
- Around line 67-69: Remove the duplicate assertion in the unit test: in the
test in client_test.go where the variable client is asserted, there are two
consecutive identical lines calling assert.Nil(t, client.mcpServers); delete one
of them so only a single assert.Nil(t, client.mcpServers) remains (locate the
duplicate near the test that constructs the client and checks
client.mcpServers).

In `@pkg/ai/agent/geminicli/client.go`:
- Around line 138-141: The two error returns in client.go are inconsistent: the
branch that uses errMsg currently formats the underlying error as plain text
(losing the original error type), so change both returns to consistently wrap
the underlying error using %w; specifically update the return that references
ProviderName and errMsg to include errMsg as context but still wrap the original
error variable (err) with %w, and ensure the other return (which already wraps
err) uses the same ordering/formatting around errUtils.ErrCLIProviderExecFailed,
ProviderName, errMsg and err so callers can use errors.Is/errors.As reliably.
- Around line 89-99: The code currently calls writeMCPSettingsInCwd(...) and
silently overwrites .gemini/settings.json; update writeMCPSettingsInCwd (or the
call site in the block that sets client.mcpServers and client.toolchainPATH) to
detect an existing .gemini/settings.json in cwd and either (a) create a backup
(e.g., rename to settings.json.bak with a timestamp) before writing the new
file, or (b) if backup fails, log a clear warning via ui.Warn or log.Debug
including the existing file path and skip overwriting; ensure any error from the
backup or write is returned and surfaced so the caller can log it (preserve
current behavior of setting client.mcpSettingsDir when appropriate) and
reference writeMCPSettingsInCwd, resolveToolchainPATH, client.mcpSettingsDir,
and atmosConfig.MCP.Servers when making the change.

In `@website/docs/ai/ai.mdx`:
- Around line 54-65: Update the Gemini CLI authentication command in the
Terminal block (the line currently using "gemini auth login") to the official
flow: replace that invocation with a direct "gemini" call so the
install-and-auth sequence uses "npm install -g `@google/gemini-cli` && gemini";
locate the Terminal section in ai.mdx that contains the three tool install/auth
lines (Claude, OpenAI Codex, Gemini) and change the Gemini portion accordingly.

In `@website/docs/cli/commands/ai/ask.mdx`:
- Line 74: The help text describing routing for MCP servers omits `gemini-cli`;
update the sentence that lists CLI providers (currently mentioning `claude-code`
and `codex-cli`) to include `gemini-cli` as well so it reads that `claude-code`,
`codex-cli`, and `gemini-cli` skip routing and pass all servers to the CLI tool
for internal selection.

In `@website/docs/cli/commands/ai/exec.mdx`:
- Line 92: Update the sentence that lists CLI providers to also include
`gemini-cli` so the routing note matches the provider list and implementation;
specifically, modify the sentence mentioning "CLI providers (`claude-code`,
`codex-cli`) skip routing" to read something like "CLI providers (`claude-code`,
`codex-cli`, `gemini-cli`) skip routing" ensuring the documentation text in
exec.mdx reflects that `gemini-cli` is passed to the CLI tool and handled
internally.

In `@website/src/data/roadmap.js`:
- Around line 524-527: The four shipped milestone objects for 'Claude Code CLI
provider', 'OpenAI Codex CLI provider', 'Gemini CLI provider', and 'MCP
pass-through for CLI providers' are missing the required pr metadata; update
each object in website/src/data/roadmap.js to include a pr property (e.g., pr:
'https://github.com/your-repo/...') alongside the existing changelog property so
they follow the repo rule that shipped milestones with changelog must also
include pr links; ensure you add the pr field to the objects where label matches
those four entries.

---

Outside diff comments:
In `@NOTICE`:
- Around line 1-1809: The NOTICE file is out of sync with the generator and
causing CI drift; run the generator script and commit the result: execute
./scripts/generate-notice.sh to regenerate the NOTICE file, verify the output
replaces the existing NOTICE (do not manually edit NOTICE), then add/commit the
regenerated NOTICE and push the change so Dependency Review passes.

---

Nitpick comments:
In `@cmd/ai/init_test.go`:
- Around line 530-549: Add edge-case inputs to TestIsCLIProvider to verify
normalization: include variations like "Claude-Code" (different casing), "
claude-code " (leading/trailing whitespace), and other whitespace/case
permutations to ensure isCLIProvider handles normalization consistently; update
the tests slice in TestIsCLIProvider to add these cases with expected true (and
any negative variants as needed) so the test exercises isCLIProvider's
robustness.

In `@cmd/mcp/client/export.go`:
- Around line 114-125: The function uppercaseEnvKeys currently returns the
original env map when it's empty, causing inconsistent aliasing; change it so it
always returns a new map copy: allocate result := make(map[string]string,
len(env)) unconditionally, copy each key/value with result[strings.ToUpper(k)] =
v (the loop will be skipped for len==0) and return result; update the function
uppercaseEnvKeys to never return the input map directly so callers always
receive a fresh map.

In `@pkg/ai/agent/claudecode/client.go`:
- Around line 278-289: The formatMessages function is duplicated across
providers (found in this file as formatMessages and also in
geminicli/client.go); extract it into a shared package (e.g.,
pkg/ai/agent/shared or pkg/ai/agent/util) and replace both local implementations
with a single exported helper like FormatMessages(messages []types.Message) that
returns the joined prompt; update callers in claudecode/client.go and
geminicli/client.go to import and use the shared helper and remove the duplicate
function definitions.
- Around line 100-117: Extract the duplicated resolveToolchainPATH function into
the shared package pkg/ai/agent/base by moving the implementation (the logic
that calls dependencies.LoadToolVersionsDependencies,
dependencies.NewEnvironmentFromDeps and scans tenv.EnvVars() for "PATH=") into a
new function base.ResolveToolchainPATH, update callers in claudecode, geminicli,
and codexcli to call base.ResolveToolchainPATH(atmosConfig) instead of their
local copies, and remove the duplicated implementations so there is a single
canonical implementation in base.

In `@pkg/ai/agent/codexcli/client.go`:
- Around line 331-340: The restoreGlobalConfig method currently ignores errors
from os.WriteFile and os.Remove; update restoreGlobalConfig (in
Client.restoreGlobalConfig) to capture the error returned by os.WriteFile and
os.Remove and emit a debug-level log when those operations fail (e.g., use the
client logger on c if available, otherwise the standard log package) including
configPath and the error; preserve the existing behavior (do not return the
error) so this remains cleanup code but adds diagnostic logging to help
troubleshoot corrupted user configs.

In `@pkg/ai/agent/geminicli/client.go`:
- Around line 246-250: The code constructs geminiDir using filepath.Join(".",
".gemini") which is redundant; change the construction to simply use ".gemini"
(e.g., set geminiDir := ".gemini") and keep the existing os.MkdirAll(geminiDir,
settingsDirPerms) and error wrapping (ErrMCPConfigWriteFailed) so the behavior
and error handling in this block (filepath.Join usage, geminiDir variable,
os.MkdirAll call, and the fmt.Errorf wrapping) remain the same but without the
unnecessary "." join.
- Around line 119-126: Iterating the map c.mcpServers to build serverNames
yields non-deterministic ordering; sort the collected names before joining so
the args slice gets a stable value for "--allowed-mcp-server-names". In the code
that gathers names from c.mcpServers (the block that appends to args), collect
names into serverNames as now, then sort.Strings(serverNames) and finally use
strings.Join(serverNames, ",") when appending to args (refer to c.mcpServers,
serverNames, args).
- Around line 279-302: The filterStderr function currently skips only a few
hardcoded patterns; update it to cover additional common Node/CLI noise by
introducing a reusable list or regex slice of warning patterns (e.g., include
"ExperimentalWarning", "punycode", "Warning", "deprecated" and
"--trace-warnings") and use that list to skip lines, or make the patterns
configurable/extendable so future patterns can be added without changing
filterStderr; locate the function filterStderr in
pkg/ai/agent/geminicli/client.go and replace the individual if checks with a
loop over the pattern list (or compiled regexes) to filter out matches and keep
the rest.
- Around line 36-44: The Client struct contains a dead field mcpSettingsDir that
is assigned but never read; remove the mcpSettingsDir field from the Client
struct declaration and also remove its assignment in the client
constructor/initializer (wherever mcpSettingsDir is set) and any associated
comment; ensure no other code references mcpSettingsDir and update related
comments to reflect that MCP settings are written to the current working
directory.

In `@pkg/ai/factory_test.go`:
- Around line 13-16: Add test cases in TestNewClient to cover the CLI providers
"claude-code", "codex-cli", and "gemini-cli" so the factory routing for CLI
agents is exercised; for each new test case, follow the pattern used for
existing providers in TestNewClient (create the client via NewClient and assert
the expected provider type) and include binary-detection or skip logic similar
to the existing API-key checks—i.e., probe for the required CLI binary (or
environment) and call t.Skipf when the binary is not present so the test suite
remains stable on systems without the CLI installed.

In `@pkg/mcp/client/mcpconfig_test.go`:
- Around line 76-85: Add a unit test covering the empty-servers edge case for
GenerateMCPConfig: create an empty map[string]schema.MCPServerConfig, call
GenerateMCPConfig with it, and assert that the returned config.MCPServers is
non-nil and empty (no panic). Place the new test (e.g.
TestGenerateMCPConfig_EmptyServers) alongside TestGenerateMCPConfig to ensure
GenerateMCPConfig handles empty input gracefully.

In `@pkg/schema/ai.go`:
- Around line 32-38: The comment "CLI provider fields (for claude-code,
codex-cli, gemini-cli)" above the struct fields in pkg/schema/ai.go is missing a
terminal period; update that top-line comment so it ends with a period to
satisfy the godot linter (edit the comment that precedes the Binary, MaxTurns,
MaxBudgetUSD, AllowedTools, and FullAuto fields).

In `@website/docs/cli/configuration/ai/providers.mdx`:
- Around line 104-119: The `binary` field description includes an implementation
detail (`exec.LookPath`) that should be removed; update the `<dd>` text for
`binary` to a simpler user-facing sentence like "Path to the CLI binary.
Optional — defaults to locating the binary on the PATH." Replace the existing
phrase that mentions `exec.LookPath` with this simpler wording and keep the rest
of the list unchanged; target the `binary` entry in the providers.mdx content
(currently mentioning `exec.LookPath`) when making the edit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c1bb2fc1-e4e4-4c99-a288-c4ed088a3e03

📥 Commits

Reviewing files that changed from the base of the PR and between 39a9be1 and 0b96d75.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (39)
  • NOTICE
  • cmd/ai/init.go
  • cmd/ai/init_test.go
  • cmd/mcp/client/export.go
  • docs/prd/atmos-ai-global-flag.md
  • docs/prd/atmos-ai-local-providers.md
  • errors/errors.go
  • examples/ai-claude-code/README.md
  • examples/ai-claude-code/atmos.yaml
  • examples/ai-claude-code/components/terraform/vpc/main.tf
  • examples/ai-claude-code/components/terraform/vpc/variables.tf
  • examples/ai-claude-code/stacks/example.yaml
  • examples/ai/README.md
  • examples/mcp/README.md
  • go.mod
  • pkg/ai/agent/claudecode/client.go
  • pkg/ai/agent/claudecode/client_test.go
  • pkg/ai/agent/claudecode/register.go
  • pkg/ai/agent/codexcli/client.go
  • pkg/ai/agent/codexcli/client_test.go
  • pkg/ai/agent/codexcli/register.go
  • pkg/ai/agent/geminicli/client.go
  • pkg/ai/agent/geminicli/client_test.go
  • pkg/ai/agent/geminicli/register.go
  • pkg/ai/analyze/providers.go
  • pkg/ai/factory_test.go
  • pkg/mcp/client/mcpconfig.go
  • pkg/mcp/client/mcpconfig_test.go
  • pkg/schema/ai.go
  • website/blog/2026-04-01-ai-cli-providers.mdx
  • website/docs/ai/ai.mdx
  • website/docs/ai/troubleshooting.mdx
  • website/docs/cli/commands/ai/ask.mdx
  • website/docs/cli/commands/ai/exec.mdx
  • website/docs/cli/commands/ai/usage.mdx
  • website/docs/cli/configuration/ai/index.mdx
  • website/docs/cli/configuration/ai/providers.mdx
  • website/docs/cli/configuration/mcp/index.mdx
  • website/src/data/roadmap.js

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 67.97020% with 172 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.18%. Comparing base (4b80474) to head (cd722e8).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/ai/agent/codexcli/client.go 66.84% 50 Missing and 12 partials ⚠️
pkg/ai/agent/geminicli/client.go 62.77% 43 Missing and 8 partials ⚠️
pkg/ai/agent/claudecode/client.go 64.54% 37 Missing and 2 partials ⚠️
pkg/ai/agent/base/config.go 36.36% 7 Missing ⚠️
pkg/mcp/client/mcpconfig.go 92.72% 2 Missing and 2 partials ⚠️
pkg/ai/agent/claudecode/register.go 25.00% 2 Missing and 1 partial ⚠️
pkg/ai/agent/codexcli/register.go 25.00% 2 Missing and 1 partial ⚠️
pkg/ai/agent/geminicli/register.go 25.00% 2 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2280      +/-   ##
==========================================
- Coverage   77.21%   77.18%   -0.03%     
==========================================
  Files        1038     1045       +7     
  Lines       97833    98367     +534     
==========================================
+ Hits        75541    75929     +388     
- Misses      18069    18187     +118     
- Partials     4223     4251      +28     
Flag Coverage Δ
unittests 77.18% <67.97%> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
cmd/ai/init.go 48.36% <100.00%> (+1.74%) ⬆️
cmd/mcp/client/export.go 51.11% <100.00%> (+10.57%) ⬆️
errors/errors.go 100.00% <ø> (ø)
pkg/ai/agent/base/messages.go 100.00% <100.00%> (ø)
pkg/ai/agent/claudecode/register.go 25.00% <25.00%> (ø)
pkg/ai/agent/codexcli/register.go 25.00% <25.00%> (ø)
pkg/ai/agent/geminicli/register.go 25.00% <25.00%> (ø)
pkg/mcp/client/mcpconfig.go 92.72% <92.72%> (ø)
pkg/ai/agent/base/config.go 82.50% <36.36%> (-17.50%) ⬇️
pkg/ai/agent/claudecode/client.go 64.54% <64.54%> (ø)
... and 2 more

... and 4 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

CodeRabbit fixes:
- Add gemini-cli to routing skip notes (ask.mdx, exec.mdx)
- Fix Gemini CLI auth command (gemini, not gemini auth login)
- Wrap original error in stderr error paths (all 3 providers)
- Add .gemini/settings.json backup/restore (like Codex)
- Fix uppercaseEnvKeys nil vs empty (export.go)
- Simplify exec.LookPath wording in docs
- Add debug logging to restore failures
- Sort MCP server names for deterministic output
- Remove dead mcpSettingsDir field
- Fix install hint: brew install --cask claude-code
- Fix MCP pass-through comment wording (init.go)
- Add pr: 2280 to roadmap milestones
- Isolate codexcli tests with temp HOME

Code deduplication:
- Extract formatMessages to base.FormatMessagesAsPrompt
- Extract resolveToolchainPATH to base.ResolveToolchainPATH
- Extract buildArgs from execClaude/SendMessage for testability

Test improvements:
- Add buildArgs tests for all 3 providers (arg construction logic)
- Add FormatMessagesAsPrompt tests in base package
- Add ResolveToolchainPATH test in base package
- Add GenerateMCPConfig_EmptyServers test
- Fix Windows CI: use os.PathListSeparator, skip Unix permission checks
- Coverage: claudecode 53->63%, codexcli 68->72%, geminicli 57->66%

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions bot added size/xl Extra large size PR and removed size/l Large size PR labels Apr 1, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cmd/mcp/client/export.go (1)

114-125: Consider consolidating uppercaseEnvKeys with the identical copyEnv function.

uppercaseEnvKeys and copyEnv in pkg/mcp/client/mcpconfig.go do the same work but differ in nil handling: uppercaseEnvKeys returns nil, while copyEnv returns an empty map. With the json:"env,omitempty" tag on the struct field, this creates inconsistent JSON output (nil omits the field, empty map includes "env": {}). Both are private functions serving identical purposes — extract the shared logic into a single helper to reduce duplication and ensure consistent behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/mcp/client/export.go` around lines 114 - 125, There are two duplicate
helpers (uppercaseEnvKeys and copyEnv) with inconsistent nil/empty-map behavior
causing different JSON outputs; replace them with a single private helper (e.g.,
normalizeEnv or copyEnv) used by both call sites, implement it to canonicalize
keys to UPPERCASE and choose a consistent nil/empty behavior (pick one and apply
everywhere — e.g., return nil when input is nil to keep json:"env,omitempty"
omitting the field, or return an empty map if you want the field present), and
update all references to use that single function (replace uppercaseEnvKeys and
the previous copyEnv usage in pkg/mcp/client/mcpconfig.go and
cmd/mcp/client/export.go).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cmd/mcp/client/export.go`:
- Around line 114-125: There are two duplicate helpers (uppercaseEnvKeys and
copyEnv) with inconsistent nil/empty-map behavior causing different JSON
outputs; replace them with a single private helper (e.g., normalizeEnv or
copyEnv) used by both call sites, implement it to canonicalize keys to UPPERCASE
and choose a consistent nil/empty behavior (pick one and apply everywhere —
e.g., return nil when input is nil to keep json:"env,omitempty" omitting the
field, or return an empty map if you want the field present), and update all
references to use that single function (replace uppercaseEnvKeys and the
previous copyEnv usage in pkg/mcp/client/mcpconfig.go and
cmd/mcp/client/export.go).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cf2d76bd-781f-456c-8c14-a9922e65a552

📥 Commits

Reviewing files that changed from the base of the PR and between 0b96d75 and 0da0ceb.

📒 Files selected for processing (21)
  • cmd/ai/init.go
  • cmd/mcp/client/export.go
  • docs/prd/atmos-ai-local-providers.md
  • pkg/ai/agent/base/config.go
  • pkg/ai/agent/base/config_test.go
  • pkg/ai/agent/base/messages.go
  • pkg/ai/agent/base/messages_tools_test.go
  • pkg/ai/agent/claudecode/client.go
  • pkg/ai/agent/claudecode/client_test.go
  • pkg/ai/agent/codexcli/client.go
  • pkg/ai/agent/codexcli/client_test.go
  • pkg/ai/agent/geminicli/client.go
  • pkg/ai/agent/geminicli/client_test.go
  • pkg/mcp/client/mcpconfig_test.go
  • website/docs/ai/ai.mdx
  • website/docs/ai/troubleshooting.mdx
  • website/docs/cli/commands/ai/ask.mdx
  • website/docs/cli/commands/ai/exec.mdx
  • website/docs/cli/configuration/ai/providers.mdx
  • website/docs/cli/configuration/mcp/index.mdx
  • website/src/data/roadmap.js
✅ Files skipped from review due to trivial changes (8)
  • pkg/ai/agent/base/config_test.go
  • website/docs/cli/commands/ai/ask.mdx
  • pkg/ai/agent/base/messages_tools_test.go
  • website/docs/cli/commands/ai/exec.mdx
  • cmd/ai/init.go
  • website/docs/cli/configuration/mcp/index.mdx
  • pkg/ai/agent/geminicli/client_test.go
  • pkg/ai/agent/codexcli/client_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/mcp/client/mcpconfig_test.go
  • website/src/data/roadmap.js
  • website/docs/cli/configuration/ai/providers.mdx
  • pkg/ai/agent/claudecode/client.go

coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 1, 2026
@aknysh aknysh requested a review from osterman April 1, 2026 20:07
Examples simplified per team feedback:
- ai/README.md: 66 → 46 lines, focus on AI functionality not mock details
- ai-claude-code/README.md: 390 → 56 lines, follows quick-start pattern
- ai/atmos.yaml: remove auto-compact, workflows, excess providers/comments
- ai-claude-code/atmos.yaml: reduce from 8 MCP servers to 2 (aws-docs, aws-billing)
- Remove examples/ai/workflows/ (not relevant to AI concept)
- Add Atmos Auth prerequisite note with setup link

Fix brew install claude → brew install --cask claude-code everywhere:
- Blog post, ai.mdx, troubleshooting.mdx, config/ai/index.mdx, PRD (6 files)

PRD: add detailed execution flow diagrams for both CLI and API providers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 2, 2026
Remove CLI command reference, config reference, YAML functions, auth/toolchain
sections, individual server descriptions, and flow diagram (all in docs).
Keep: server table, prerequisites with doc links, Try It, smart routing,
IDE integration, and 4 best See It in Action examples.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/mcp/README.md`:
- Line 6: The README references documentation pages that are missing from the
website and causing broken-link checks; add the missing docs under website/docs
with the exact paths mentioned in the README: create cli/configuration/mcp.md
(include the smart-routing section and an anchor matching the README anchor),
cli/configuration/auth.md, and cli/configuration/ai/providers.md with content
that covers the referenced configuration details, or alternatively update the
README links to point to existing docs if these topics are intentionally located
elsewhere; ensure filenames and the smart-routing anchor exactly match the
README references so website builds no longer report broken links.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ef8ebe4-a129-4628-8a48-a278bd035c85

📥 Commits

Reviewing files that changed from the base of the PR and between 5ec2f77 and cd722e8.

📒 Files selected for processing (1)
  • examples/mcp/README.md

@aknysh aknysh merged commit 1c18f61 into main Apr 2, 2026
60 checks passed
@aknysh aknysh deleted the aknysh/atmos-ai-cli-providers branch April 2, 2026 17:30
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 2, 2026

These changes were released in v1.214.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New features that do not break anything size/xl Extra large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants