Skip to main content

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 enable you to drive external AI coding tools from PraisonAI agents, with Claude Code as the flagship example.

Quick Start

1

One-shot call to Claude Code

import asyncio
from praisonai.integrations import ClaudeCodeIntegration

integration = ClaudeCodeIntegration()
result = asyncio.run(integration.execute("Summarise the README in this repo"))
print(result)
2

Continue a previous Claude Code session

# 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)
3

Stream events live

async for event in integration.stream("Refactor utils.py"):
    print(event)   # parsed JSON events from the CLI
4

Per-call output format

await integration.execute("prompt", output_format="json")
await integration.execute("prompt", output_format="text")

How It Works

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

Stateless Session Management

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.
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
Because state is no longer shared on the instance, a single ClaudeCodeIntegration is safe to call concurrently from multiple tasks / threads.
Before (stateful):
# ❌ 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):
# ✅ 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.
ModeFlagBehaviourBest for
Manager delegation (default)--external-agent XManager Agent wraps the CLI as a tool, reasons, then calls itMulti-step tasks, planning, aggregation
Direct proxy--external-agent X --external-agent-directPass-through — CLI runs the prompt verbatimFast single-shot calls, scripting, when you don’t want a manager LLM
Usage Examples:
# 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

OptionTypeDefaultWhereDescription
workspacestr"."constructorWorking directory passed to the CLI
timeoutint300constructorPer-call timeout (seconds)
output_formatstr"json"constructor or per-call"text" | "json" | "stream-json"
use_sdkboolFalse (True if SDK available)constructorUse the Claude Code SDK when installed, otherwise fall back to subprocess
modelstr | NoneNoneconstructorPassed through to the CLI via --model
continue_sessionboolFalseper-callAdds --continue to the CLI invocation
skip_permissionsboolTrueconstructorSkip permission prompts with --dangerously-skip-permissions
system_promptstr | NoneNoneconstructorCustom system prompt to append
allowed_toolsList[str] | NoneNoneconstructorList of allowed tools (e.g., [“Read”, “Write”, “Bash”])
disallowed_toolsList[str] | NoneNoneconstructorList of disallowed tools

Registry

The ExternalAgentRegistry manages all available CLI integrations using a thread-safe singleton pattern:
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().
# Install Claude Code CLI (option 1)
# Follow Claude Code installation instructions

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

Decision Flow


Best Practices

Don’t rely on instance state for session continuation. Always be explicit about when you want to continue a previous session.
# ✅ 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
Do not mutate the integration.output_format attribute between calls. Use the per-call parameter or set it once during initialization.
# ✅ 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
The reset_session() method is now a no-op. Remove these calls from your code as they serve no purpose.
# ❌ Bad - calling deprecated no-op
integration.reset_session()

# ✅ Good - just omit the call
# Session state is automatically stateless
A single ClaudeCodeIntegration instance can be shared across concurrent tasks safely since no instance state is mutated during calls.
# ✅ 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())

Using from PraisonAI UI

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 for complete documentation.

Persistence & Concurrency

Learn about thread-safe persistence features

Agent Tools

Using integrations as agent tools