Skip to main content

Overview

CLI providers (geminicli:, claudecli:, codex:, vibe:, opencode:) support file and folder attachments through the extra_body.cli parameter. This enables the AI to read, analyze, and modify local files.
CLI attachments are only supported with CLI providers. API providers (gemini:, claude:, etc.) do not support this feature.

Syntax

Add attachments via the extra_body.cli.attachments array:
{
  "model": "geminicli:gemini-2.5-pro",
  "messages": [{"role": "user", "content": "Analyze this code"}],
  "extra_body": {
    "cli": {
      "attachments": [
        {"type": "file", "path": "/path/to/file.py"},
        {"type": "folder", "path": "./src/"}
      ]
    }
  }
}

Attachment Types

File Attachments

Attach a single file:
{
  "type": "file",
  "path": "/absolute/or/relative/path/to/file.py"
}
Supported Path Formats:
  • Absolute: /home/user/project/main.py
  • Relative: ./src/main.py (relative to server working directory)
  • Home directory: ~/projects/file.py

Folder Attachments

Attach an entire directory:
{
  "type": "folder",
  "path": "./src/"
}
All files in the folder and subdirectories are included.

Examples

Single File

curl http://localhost:18080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-test-123" \
  -d '{
    "model": "geminicli:gemini-2.5-pro",
    "messages": [
      {"role": "user", "content": "Fix the bugs in this code"}
    ],
    "extra_body": {
      "cli": {
        "attachments": [
          {"type": "file", "path": "./main.py"}
        ]
      }
    }
  }'

Multiple Files

response = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {"role": "user", "content": "Review this pull request"}
    ],
    extra_body={
        "cli": {
            "attachments": [
                {"type": "file", "path": "./src/main.py"},
                {"type": "file", "path": "./src/utils.py"},
                {"type": "file", "path": "./tests/test_main.py"}
            ]
        }
    }
)

Folder Attachment

response = client.chat.completions.create(
    model="claudecli:claude-sonnet-4",
    messages=[
        {"role": "user", "content": "Refactor this codebase to use TypeScript"}
    ],
    extra_body={
        "cli": {
            "attachments": [
                {"type": "folder", "path": "./src/"}
            ]
        }
    }
)

Mixed Attachments

Combine files and folders:
response = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {"role": "user", "content": "Add tests for the new features"}
    ],
    extra_body={
        "cli": {
            "attachments": [
                {"type": "folder", "path": "./src/"},       # Source code
                {"type": "folder", "path": "./tests/"},     # Existing tests
                {"type": "file", "path": "./package.json"}  # Dependencies
            ]
        }
    }
)

CLI Flags

Control CLI behavior with flags:
{
  "extra_body": {
    "cli": {
      "attachments": [...],
      "flags": {
        "sandbox": true,
        "auto_approve": true,
        "yolo": false
      }
    }
  }
}

Available Flags

sandbox
boolean
default:false
Run in restricted execution mode. Prevents file modifications.CLI Mapping:
  • geminicli: -s or --sandbox
  • codex: --sandbox
  • claudecli: Not supported
  • vibe: Not supported
auto_approve
boolean
default:false
Skip confirmation prompts for file operations.CLI Mapping:
  • geminicli: -y or --yes
  • vibe: --auto-approve
  • claudecli: --dangerously-skip-permissions
  • codex: --full-auto
yolo
boolean
default:false
Maximum autonomy mode. Combines auto-approve with other permissive flags.CLI Mapping:
  • geminicli: --yolo
  • vibe: --auto-approve
  • claudecli: --dangerously-skip-permissions
  • codex: --full-auto

Flag Examples

# Read-only mode - AI can view but not modify files
response = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {"role": "user", "content": "Analyze code quality"}
    ],
    extra_body={
        "cli": {
            "attachments": [{"type": "folder", "path": "./src/"}],
            "flags": {
                "sandbox": True  # Read-only
            }
        }
    }
)

Session Management

Resume or name CLI sessions:
response = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {"role": "user", "content": "Continue working on the feature"}
    ],
    extra_body={
        "cli": {
            "session_id": "my-project-session",
            "attachments": [{"type": "folder", "path": "./"}]
        }
    }
)

Session ID Values

ValueBehavior
"latest"Resume most recent session
"new"Force new session
"my-session-name"Named session (resumable)

Session Example

# First request: Start named session
response1 = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {"role": "user", "content": "Create a user authentication system"}
    ],
    extra_body={
        "cli": {
            "session_id": "auth-feature",
            "attachments": [{"type": "folder", "path": "./src/"}]
        }
    }
)

# Later request: Resume same session
response2 = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {"role": "user", "content": "Add password reset functionality"}
    ],
    extra_body={
        "cli": {
            "session_id": "auth-feature",  # Same session
            "attachments": [{"type": "folder", "path": "./src/"}]
        }
    }
)

Use Cases

Code Review

response = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {
            "role": "user",
            "content": "Review this code for security vulnerabilities and suggest improvements"
        }
    ],
    extra_body={
        "cli": {
            "attachments": [
                {"type": "folder", "path": "./src/"},
                {"type": "file", "path": "./package.json"}
            ],
            "flags": {"sandbox": True}  # Read-only review
        }
    }
)

Automated Refactoring

response = client.chat.completions.create(
    model="claudecli:claude-sonnet-4",
    messages=[
        {
            "role": "user",
            "content": "Refactor to use async/await throughout"
        }
    ],
    extra_body={
        "cli": {
            "attachments": [{"type": "folder", "path": "./src/"}],
            "flags": {"auto_approve": True}  # Auto-apply changes
        }
    }
)

Test Generation

response = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {
            "role": "user",
            "content": "Generate comprehensive unit tests for all functions"
        }
    ],
    extra_body={
        "cli": {
            "attachments": [
                {"type": "folder", "path": "./src/"},
                {"type": "folder", "path": "./tests/"}  # Existing tests for reference
            ]
        }
    }
)

Documentation Generation

response = client.chat.completions.create(
    model="codex:gpt-5",
    messages=[
        {
            "role": "user",
            "content": "Add comprehensive JSDoc comments to all functions"
        }
    ],
    extra_body={
        "cli": {
            "attachments": [{"type": "folder", "path": "./src/"}],
            "flags": {"auto_approve": True}
        }
    }
)

Codebase Migration

response = client.chat.completions.create(
    model="geminicli:gemini-2.5-pro",
    messages=[
        {
            "role": "user",
            "content": "Migrate from JavaScript to TypeScript. Add type definitions to all files."
        }
    ],
    extra_body={
        "cli": {
            "attachments": [
                {"type": "folder", "path": "./src/"},
                {"type": "file", "path": "./tsconfig.json"}
            ],
            "session_id": "ts-migration",
            "flags": {"auto_approve": True}
        }
    }
)

Provider Support

ProviderFilesFoldersFlagsSessions
geminicli
claudecli⚠️
codex
vibe⚠️
opencode
⚠️ = Partial support (limited flags)

Security Considerations

switchAILocal validates all file paths to prevent directory traversal attacks:
# Safe: Relative paths are resolved securely
{"type": "file", "path": "./src/main.py"}

# Unsafe: Absolute paths outside workspace are rejected
{"type": "file", "path": "/etc/passwd"}  # Blocked
Use sandbox mode for untrusted operations:
# Safe: Read-only analysis
extra_body={
    "cli": {
        "attachments": [...],
        "flags": {"sandbox": True}
    }
}
Large files may exceed CLI limits:
config.yaml
cli:
  max_attachment_size: 10485760  # 10MB
  max_total_size: 52428800       # 50MB
Avoid attaching sensitive files:
# Don't attach:
# - .env files
# - credentials.json
# - private keys
# - API tokens

Error Handling

from openai import OpenAI, APIError

client = OpenAI(
    base_url="http://localhost:18080/v1",
    api_key="sk-test-123"
)

try:
    response = client.chat.completions.create(
        model="geminicli:gemini-2.5-pro",
        messages=[
            {"role": "user", "content": "Analyze this file"}
        ],
        extra_body={
            "cli": {
                "attachments": [
                    {"type": "file", "path": "./nonexistent.py"}
                ]
            }
        }
    )
except APIError as e:
    if "file not found" in str(e).lower():
        print("File does not exist")
    elif "permission denied" in str(e).lower():
        print("Cannot access file")
    else:
        print(f"Error: {e.message}")

Limitations

LimitationValueNotes
Max file size10 MBConfigurable in config.yaml
Max total size50 MBSum of all attachments
Max files100Per request
Supported formatsAll textBinary files may fail

Best Practices

  1. Start with Sandbox: Test with read-only mode first
  2. Use Relative Paths: More portable and secure
  3. Minimize Attachments: Only include relevant files
  4. Name Sessions: Use descriptive session IDs for continuity
  5. Validate Outputs: Review AI changes before committing

Next Steps