> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praison.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# External CLI Integrations

> Drive external AI CLIs like Claude Code from a PraisonAI agent

External CLI integrations enable you to drive external AI coding tools from PraisonAI agents, with Claude Code as the flagship example.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    User[👤 User] --> Agent[🤖 Agent]
    Agent --> Integration[🔌 ClaudeCodeIntegration]
    Integration --> CLI[💻 claude CLI]
    CLI --> Result[✅ Result]

    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef agent fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef tool fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class User,Agent input
    class Integration,CLI tool
    class Result output
```

## Quick Start

<Steps>
  <Step title="One-shot call to Claude Code">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import asyncio
    from praisonai.integrations import ClaudeCodeIntegration

    integration = ClaudeCodeIntegration()
    result = asyncio.run(integration.execute("Summarise the README in this repo"))
    print(result)
    ```
  </Step>

  <Step title="Continue a previous Claude Code session">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # First turn
    await integration.execute("List the open bugs")

    # Continue the same session — you MUST pass continue_session=True explicitly
    await integration.execute("Now suggest fixes for the first one", continue_session=True)
    ```
  </Step>

  <Step title="Stream events live">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    async for event in integration.stream("Refactor utils.py"):
        print(event)   # parsed JSON events from the CLI
    ```
  </Step>

  <Step title="Per-call output format">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    await integration.execute("prompt", output_format="json")
    await integration.execute("prompt", output_format="text")
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Integration as ClaudeCodeIntegration
    participant CLI as claude CLI
    participant Result

    User->>Integration: execute(prompt, continue_session=?, output_format=?)
    Integration->>Integration: _build_command() adds --continue only when asked
    Integration->>CLI: subprocess with assembled args
    CLI->>Result: parsed response
    Result-->>Integration: return to caller
    Integration-->>User: final result
```

The integration builds CLI arguments dynamically per call, with no instance state mutated during execution.

***

## Stateless Session Management

<Warning>
  `reset_session()` is a deprecated no-op as of PraisonAI PR #1466. Remove it from your code. Session continuation is now explicit via `continue_session=True`.
</Warning>

| Old (removed / ignored)                        | New explicit parameter                        |
| ---------------------------------------------- | --------------------------------------------- |
| `self._session_active = True` (auto)           | `continue_session=True` per call              |
| `self.output_format` mutated during `stream()` | `output_format="stream-json"` passed per call |
| `integration.reset_session()`                  | **No longer needed — deprecated no-op**       |

<Note>
  Because state is no longer shared on the instance, a single `ClaudeCodeIntegration` is safe to call concurrently from multiple tasks / threads.
</Note>

**Before (stateful):**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# ❌ Old pattern — relied on implicit session state
integration = ClaudeCodeIntegration()
await integration.execute("step 1")  # marks _session_active = True
await integration.execute("step 2")  # implicitly continued previous session
integration.reset_session()           # cleared state
```

**After (stateless):**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# ✅ New pattern — explicit, stateless, safe for concurrent use
integration = ClaudeCodeIntegration()
await integration.execute("step 1")
await integration.execute("step 2", continue_session=True)   # explicit
# reset_session() is no longer needed; it is a deprecated no-op
```

***

## CLI: Manager Delegation (Default)

PraisonAI CLI offers two execution modes for external agents, providing flexibility between automated reasoning and direct execution.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    A[User Command] --> B{Need planning/reasoning?}
    B -->|Yes| C[Manager Delegation]
    B -->|No| D[Direct Proxy]
    C --> E["--external-agent"]
    D --> F["--external-agent-direct"]
    E --> G[Manager Agent + Subagent Tool]
    F --> H[Pass-through CLI]
    
    classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff  
    classDef mode fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef flag fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef execution fill:#10B981,stroke:#7C90A0,color:#fff
    
    class A input
    class B decision
    class C,D mode
    class E,F flag
    class G,H execution
```

| Mode                         | Flag                                         | Behaviour                                                     | Best for                                                             |
| ---------------------------- | -------------------------------------------- | ------------------------------------------------------------- | -------------------------------------------------------------------- |
| Manager delegation (default) | `--external-agent X`                         | Manager Agent wraps the CLI as a tool, reasons, then calls it | Multi-step tasks, planning, aggregation                              |
| Direct proxy                 | `--external-agent X --external-agent-direct` | Pass-through — CLI runs the prompt verbatim                   | Fast single-shot calls, scripting, when you don't want a manager LLM |

**Usage Examples:**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Manager delegation - manager reasons, then calls claude as tool
praisonai "Fix the bug in auth.py" --external-agent claude

# Direct proxy - no manager overhead, straight to claude  
praisonai "Fix the bug in auth.py" --external-agent claude --external-agent-direct
```

***

## Configuration Options

| Option             | Type                | Default                         | Where                       | Description                                                               |
| ------------------ | ------------------- | ------------------------------- | --------------------------- | ------------------------------------------------------------------------- |
| `workspace`        | `str`               | `"."`                           | constructor                 | Working directory passed to the CLI                                       |
| `timeout`          | `int`               | `300`                           | constructor                 | Per-call timeout (seconds)                                                |
| `output_format`    | `str`               | `"json"`                        | constructor **or** per-call | `"text"` \| `"json"` \| `"stream-json"`                                   |
| `use_sdk`          | `bool`              | `False` (True if SDK available) | constructor                 | Use the Claude Code SDK when installed, otherwise fall back to subprocess |
| `model`            | `str \| None`       | `None`                          | constructor                 | Passed through to the CLI via `--model`                                   |
| `continue_session` | `bool`              | `False`                         | per-call                    | Adds `--continue` to the CLI invocation                                   |
| `skip_permissions` | `bool`              | `True`                          | constructor                 | Skip permission prompts with `--dangerously-skip-permissions`             |
| `system_prompt`    | `str \| None`       | `None`                          | constructor                 | Custom system prompt to append                                            |
| `allowed_tools`    | `List[str] \| None` | `None`                          | constructor                 | List of allowed tools (e.g., \["Read", "Write", "Bash"])                  |
| `disallowed_tools` | `List[str] \| None` | `None`                          | constructor                 | List of disallowed tools                                                  |

***

## Registry

The `ExternalAgentRegistry` manages all available CLI integrations using a thread-safe singleton pattern:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.integrations.registry import ExternalAgentRegistry

# Get singleton registry
registry = ExternalAgentRegistry.get_instance()

# List available integrations  
available = await registry.get_available()
print(available)  # {'claude': True, 'gemini': False, ...}

# Create integration
claude = registry.create('claude', workspace="/path/to/project")
```

The registry uses a thread-safe `_availability_cache` lock to ensure `is_available()` checks are safe from concurrent access.

***

## Prerequisites

The `claude` CLI must be available on `PATH` or the Claude Code SDK must be installed if `use_sdk=True`. The integration performs a cached, thread-safe `shutil.which(...)` check via `is_available()`.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Install Claude Code CLI (option 1)
# Follow Claude Code installation instructions

# Or install SDK (option 2)  
pip install claude-agent-sdk
```

***

## Decision Flow

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{How do I use Claude Code?}
    Q -->|One-shot answer| A[await integration.execute prompt]
    Q -->|Stream events live| B[async for event in integration.stream prompt]
    Q -->|Continue prior session| C[pass continue_session=True per call]
    Q -->|Structured JSON back| D[pass output_format=&quot;json&quot; per call]

    classDef choice fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef action fill:#10B981,stroke:#7C90A0,color:#fff

    class Q choice
    class A,B,C,D action
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Always pass continue_session=True explicitly">
    Don't rely on instance state for session continuation. Always be explicit about when you want to continue a previous session.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # ✅ Good - explicit session control
    await integration.execute("step 1")
    await integration.execute("step 2", continue_session=True)

    # ❌ Bad - relying on removed implicit state
    await integration.execute("step 1")
    await integration.execute("step 2")  # No longer continues session
    ```
  </Accordion>

  <Accordion title="Set output_format per call or once in constructor">
    Do not mutate the `integration.output_format` attribute between calls. Use the per-call parameter or set it once during initialization.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # ✅ Good - per-call parameter
    await integration.execute("prompt", output_format="json")
    await integration.execute("prompt", output_format="text")

    # ✅ Good - set once in constructor
    integration = ClaudeCodeIntegration(output_format="json")

    # ❌ Bad - mutating instance attribute
    integration.output_format = "json"  # Don't do this
    ```
  </Accordion>

  <Accordion title="Remove reset_session() calls">
    The `reset_session()` method is now a no-op. Remove these calls from your code as they serve no purpose.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # ❌ Bad - calling deprecated no-op
    integration.reset_session()

    # ✅ Good - just omit the call
    # Session state is automatically stateless
    ```
  </Accordion>

  <Accordion title="Safe for concurrent use">
    A single `ClaudeCodeIntegration` instance can be shared across concurrent tasks safely since no instance state is mutated during calls.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # ✅ Good - concurrent safe
    integration = ClaudeCodeIntegration()

    async def task1():
        return await integration.execute("task 1")

    async def task2(): 
        return await integration.execute("task 2")

    # Both tasks can run concurrently safely
    results = await asyncio.gather(task1(), task2())
    ```
  </Accordion>
</AccordionGroup>

***

## Using from PraisonAI UI

<Tip>
  You can also enable external CLI integrations directly from the PraisonAI user interface without writing code. All PraisonAI UI entry points include toggles for external agents when the corresponding CLIs are installed. See [External Agents in UI](/docs/features/external-agents-ui) for complete documentation.
</Tip>

***

## Related

<CardGroup cols={2}>
  <Card title="Persistence & Concurrency" icon="database" href="/docs/persistence/overview">
    Learn about thread-safe persistence features
  </Card>

  <Card title="Agent Tools" icon="wrench" href="/docs/concepts/tools">
    Using integrations as agent tools
  </Card>
</CardGroup>
