Claude Code Integration

Integrate Anthropic's Claude Code CLI for AI-powered coding tasks

Claude Code Integration

PraisonAI provides seamless integration with Anthropic’s Claude Code CLI, supporting both subprocess-based execution and the official Python SDK.

Installation

CLI Installation

bash
# Install Claude Code CLI
curl -fsSL https://claude.ai/install.sh | bash

# Verify installation
claude --version

SDK Installation (Optional)

bash
# Install the Python SDK for enhanced features
pip install claude-agent-sdk

SDK availability

use_sdk=True is a best-effort opt-in. If claude-agent-sdk is not installed, the integration falls back to the claude CLI subprocess and logs a WARNING from the praisonai.integrations.claude_code logger:

use_sdk=True but claude-agent-sdk is not installed; falling back to the 'claude' CLI subprocess. Install with: pip install claude-agent-sdk

To require the SDK, check sdk_available before calling:

python
from praisonai.integrations import ClaudeCodeIntegration

claude = ClaudeCodeIntegration(workspace="/project", use_sdk=True)

if not claude.sdk_available:
    raise RuntimeError("SDK path required. Run: pip install claude-agent-sdk")

result = await claude.execute("Refactor the auth module")

Quick Start

python
from praisonai.integrations import ClaudeCodeIntegration

# Create integration
claude = ClaudeCodeIntegration(
    workspace="/path/to/project",
    output_format="json",
    skip_permissions=True
)

# Execute a coding task
result = await claude.execute("Refactor the auth module")
print(result)

Use as Agent Backend

Delegate an Agent’s LLM turns to the claude CLI instead of the Anthropic API — the flagship CLI backend, works with Claude Pro / Max subscriptions.

python
from praisonaiagents import Agent

agent = Agent(name="assistant", cli_backend="claude-code")
agent.start("Hello")

Configuration Options

OptionTypeDefaultDescription
workspacestr“.”Working directory for CLI execution
timeoutint300Timeout in seconds
output_formatstr“json”Output format: “json”, “text”, “stream-json”
skip_permissionsboolTrueSkip permission prompts
system_promptstrNoneCustom system prompt to append
allowed_toolslistNoneList of allowed tools
disallowed_toolslistNoneList of disallowed tools
use_sdkboolFalseUse SDK instead of subprocess. If claude-agent-sdk is not installed, a WARNING is logged and the integration falls back to the subprocess path. See SDK availability.
modelstrNoneModel to use (e.g., “sonnet”, “opus”)

Runtime-only kwargs (passed to execute(), not to the constructor):

OptionTypeDefaultDescription
on_event (alias on_progress)Callable[[dict], Any] \AwaitableNoneCalled with every event (system, stream_event, result) as the run streams. Sync or async. Exceptions are swallowed so a broken sink can’t fail the run.
continue_sessionboolFalseContinue the previous session.

The callback receives one event dict at a time:

event["type"]MeaningUseful fields
system (subtype init)Session startedsession_id, model
stream_eventPartial content — token/tool-use deltasevent.content_block.type == "tool_use", or event.delta.type == "text_delta" with event.delta.text
result (subtype success)Terminal event with final answerresult (final text), total_cost_usd

Examples

Basic Execution

python
from praisonai.integrations import ClaudeCodeIntegration

claude = ClaudeCodeIntegration(workspace="/project")

# Simple task
result = await claude.execute("Add error handling to main.py")
print(result)

With System Prompt

python
claude = ClaudeCodeIntegration(
    workspace="/project",
    system_prompt="You are a Python expert. Follow PEP 8 guidelines."
)

result = await claude.execute("Refactor the utils module")

Tool Restrictions

python
claude = ClaudeCodeIntegration(
    workspace="/project",
    allowed_tools=["Read", "Write"],  # Only allow file operations
    disallowed_tools=["Bash"]  # Disable shell commands
)

result = await claude.execute("Update the config file")

Session Continuation

python
claude = ClaudeCodeIntegration(workspace="/project")

# First request
result1 = await claude.execute("Read main.py and understand the structure")

# Continue the session
result2 = await claude.execute(
    "Now refactor the function we discussed",
    continue_session=True
)

Using the SDK

python
claude = ClaudeCodeIntegration(
    workspace="/project",
    use_sdk=True  # Use claude-agent-sdk if available
)

# Check if SDK is available (captured at import time; see SDK availability)
print(f"SDK available: {claude.sdk_available}")

result = await claude.execute("Complex refactoring task")

Live Progress with on_event

Pass an on_event callback to execute() to see live progress while the run is still in flight — execute() still returns the final text.

Diagram

Claude Code Live Progress

execute prompt
on_event=cb

system: init

stream_event: tool_use

stream_event: text_delta

result

final text

šŸ‘¤ Caller

šŸ¤– Claude Code CLI

šŸ“” on_event callback

An Agent using the Claude Code tool surfaces progress with the same on_event kwarg — nothing extra on the Agent side.

python
from praisonaiagents import Agent
from praisonai.integrations import ClaudeCodeIntegration

progress_log = []
claude = ClaudeCodeIntegration(workspace=".")

# The agent hands its progress sink to the tool at call time via on_event.
tool = claude.as_tool()

agent = Agent(
    name="Coder",
    instructions="Refactor code and keep the user informed as you go.",
    tools=[tool],
)
agent.start("Refactor the payment module")

The raw one-liner most callers reach for first:

python
from praisonai.integrations import ClaudeCodeIntegration

claude = ClaudeCodeIntegration(workspace="/project")

def show_progress(event):
    t = event.get("type")
    if t == "system":
        print(f"[started on {event.get('model')}]")
    elif t == "stream_event":
        block = (event.get("event") or {}).get("content_block", {})
        if block.get("type") == "tool_use":
            print(f"→ {block.get('name')}")

# execute() still returns the final text — you just also see progress.
final = await claude.execute(
    "Refactor the auth module and add tests",
    on_event=show_progress,
)
print(final)

on_event may be sync or async — awaitable results are awaited:

python
async def stream_to_ui(event):
    await websocket.send_json(event)

final = await claude.execute("Big refactor", on_event=stream_to_ui)

Streaming Output

Iterate stream() directly for full control over each event.

python
claude = ClaudeCodeIntegration(workspace="/project")

async for event in claude.stream("Add comprehensive tests"):
    if event.get("type") == "system":
        print(f"[session {event.get('session_id')} started on {event.get('model')}]")
    elif event.get("type") == "stream_event":
        inner = event.get("event", {})
        # Tool-use start
        block = inner.get("content_block", {})
        if block.get("type") == "tool_use":
            print(f"→ using tool: {block.get('name')}")
        # Streaming text
        delta = inner.get("delta", {})
        if delta.get("type") == "text_delta":
            print(delta.get("text", ""), end="", flush=True)
    elif event.get("type") == "result":
        print(f"\nāœ… done. cost=${event.get('total_cost_usd', 0):.4f}")

As Agent Tool

python
from praisonai import Agent
from praisonai.integrations import ClaudeCodeIntegration

claude = ClaudeCodeIntegration(
    workspace="/project",
    skip_permissions=True
)

# Create tool
tool = claude.as_tool()

# Use with agent
agent = Agent(
    name="Code Assistant",
    role="Software Developer",
    goal="Help with coding tasks",
    tools=[tool]
)

result = agent.start("Refactor the authentication module")

As a native async agent tool

Preferred when the agent runs on agent.astart(...) (or any async entrypoint) — the tool is awaited directly on the running loop with no thread hop.

python
from praisonaiagents import Agent
from praisonai.integrations import ClaudeCodeIntegration

claude = ClaudeCodeIntegration(workspace="/project")

agent = Agent(
    name="Code Assistant",
    role="Software Developer",
    goal="Help with coding tasks",
    tools=[claude.as_async_tool()],   # registers as `claude_atool`
)

await agent.astart("Refactor the authentication module")

as_async_tool() was added in PraisonAI PR #4022; as_tool() still works and is now also safe to call from inside an async agent runtime.

Environment Variables

bash
# API Key (required)
export ANTHROPIC_API_KEY=your-key
# or
export CLAUDE_API_KEY=your-key

# Optional: Set default workspace
export PRAISONAI_CODE_REPO_PATH=/path/to/project

CLI Flags Used

The integration uses the following Claude Code CLI flags:

FlagDescription
-pPrint mode (headless)
--output-format jsonJSON output for parsing
--continueContinue previous session
--dangerously-skip-permissionsSkip permission prompts
--append-system-promptAdd custom system prompt
--allowedToolsRestrict available tools
--disallowedToolsDisable specific tools
--modelSelect model (sonnet, opus)
--verboseForced automatically when output_format="stream-json" (the CLI rejects stream-json in print mode without it).
--include-partial-messagesForced automatically when output_format="stream-json" — emits the token/tool-use deltas that make live progress visible.

Error Handling

python
from praisonai.integrations import ClaudeCodeIntegration

claude = ClaudeCodeIntegration(timeout=60)

try:
    result = await claude.execute("Complex task")
except TimeoutError:
    print("Task timed out after 60 seconds")
except Exception as e:
    print(f"Error: {e}")

Robustness (PR #4111)

  • A subprocess TimeoutError is now returned as CliBackendResult(error=...) instead of escaping as an exception.
  • --model (-m) and cwd are threaded through, so scheduled runs can pin a model and run in a workspace.
  • CliSessionBinding.is_resume is now set on the second turn of a session, so the resume branch runs instead of re-sending the system prompt every turn.

Best Practices

  1. Use JSON output for programmatic processing
  2. Set appropriate timeouts for complex tasks
  3. Use tool restrictions for security
  4. Enable SDK for enhanced features when available
  5. Use session continuation for multi-step tasks