Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .claude/commands/commit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
allowed-tools: Bash(git add:*), Bash(git status:*), Bash(git commit:*)
description: Create a git commit
---

## Context

- Current git status: !`git status`
- Current git diff (staged and unstaged changes): !`git diff HEAD`
- Current branch: !`git branch --show-current`
- Recent commits: !`git log --oneline -10`

## Your task

Based on the above changes, create a single git commit.

You have the capability to call multiple tools in a single response. Stage and create the commit using a single message. Do not use any other tools or do anything else. Do not send any other text or messages besides these tool calls.
124 changes: 124 additions & 0 deletions e2e-tests/test_agents_and_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""End-to-end tests for agents and setting sources with real Claude API calls."""

import tempfile
from pathlib import Path

import pytest

from claude_code_sdk import (
AgentDefinition,
ClaudeCodeOptions,
ClaudeSDKClient,
SystemMessage,
)


@pytest.mark.e2e
@pytest.mark.asyncio
async def test_agent_definition():
"""Test that custom agent definitions work."""
options = ClaudeCodeOptions(
agents={
"test-agent": AgentDefinition(
description="A test agent for verification",
prompt="You are a test agent. Always respond with 'Test agent activated'",
tools=["Read"],
model="sonnet",
)
},
max_turns=1,
)

async with ClaudeSDKClient(options=options) as client:
await client.query("What is 2 + 2?")

# Check that agent is available in init message
async for message in client.receive_response():
if isinstance(message, SystemMessage) and message.subtype == "init":
agents = message.data.get("agents", {})
assert (
"test-agent" in agents
), f"test-agent should be available, got: {agents}"
agent_data = agents["test-agent"]
assert agent_data["description"] == "A test agent for verification"
break


@pytest.mark.e2e
@pytest.mark.asyncio
async def test_setting_sources_user_only():
"""Test that setting_sources=['user'] excludes project settings."""
with tempfile.TemporaryDirectory() as tmpdir:
# Create a temporary project with a slash command
project_dir = Path(tmpdir)
commands_dir = project_dir / ".claude" / "commands"
commands_dir.mkdir(parents=True)

test_command = commands_dir / "testcmd.md"
test_command.write_text(
"""---
description: Test command
---

This is a test command.
"""
)

# Use setting_sources=["user"] to exclude project settings
options = ClaudeCodeOptions(
setting_sources=["user"],
cwd=project_dir,
max_turns=1,
)

async with ClaudeSDKClient(options=options) as client:
await client.query("What is 2 + 2?")

# Check that project command is NOT available
async for message in client.receive_response():
if isinstance(message, SystemMessage) and message.subtype == "init":
commands = message.data.get("slash_commands", [])
assert (
"testcmd" not in commands
), f"testcmd should NOT be available with user-only sources, got: {commands}"
break


@pytest.mark.e2e
@pytest.mark.asyncio
async def test_setting_sources_project_included():
"""Test that setting_sources=['user', 'project'] includes project settings."""
with tempfile.TemporaryDirectory() as tmpdir:
# Create a temporary project with a slash command
project_dir = Path(tmpdir)
commands_dir = project_dir / ".claude" / "commands"
commands_dir.mkdir(parents=True)

test_command = commands_dir / "testcmd.md"
test_command.write_text(
"""---
description: Test command
---

This is a test command.
"""
)

# Use setting_sources=["user", "project"] to include project settings
options = ClaudeCodeOptions(
setting_sources=["user", "project"],
cwd=project_dir,
max_turns=1,
)

async with ClaudeSDKClient(options=options) as client:
await client.query("What is 2 + 2?")

# Check that project command IS available
async for message in client.receive_response():
if isinstance(message, SystemMessage) and message.subtype == "init":
commands = message.data.get("slash_commands", [])
assert (
"testcmd" in commands
), f"testcmd should be available with project sources, got: {commands}"
break
124 changes: 124 additions & 0 deletions examples/agents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env python3
"""Example of using custom agents with Claude Code SDK.

This example demonstrates how to define and use custom agents with specific
tools, prompts, and models.

Usage:
./examples/agents.py - Run the example
"""

import anyio

from claude_code_sdk import (
AgentDefinition,
AssistantMessage,
ClaudeCodeOptions,
ResultMessage,
TextBlock,
query,
)


async def code_reviewer_example():
"""Example using a custom code reviewer agent."""
print("=== Code Reviewer Agent Example ===")

options = ClaudeCodeOptions(
agents={
"code-reviewer": AgentDefinition(
description="Reviews code for best practices and potential issues",
prompt="You are a code reviewer. Analyze code for bugs, performance issues, "
"security vulnerabilities, and adherence to best practices. "
"Provide constructive feedback.",
tools=["Read", "Grep"],
model="sonnet",
),
},
)

async for message in query(
prompt="Use the code-reviewer agent to review the code in src/claude_code_sdk/types.py",
options=options,
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
elif isinstance(message, ResultMessage) and message.total_cost_usd and message.total_cost_usd > 0:
print(f"\nCost: ${message.total_cost_usd:.4f}")
print()


async def documentation_writer_example():
"""Example using a documentation writer agent."""
print("=== Documentation Writer Agent Example ===")

options = ClaudeCodeOptions(
agents={
"doc-writer": AgentDefinition(
description="Writes comprehensive documentation",
prompt="You are a technical documentation expert. Write clear, comprehensive "
"documentation with examples. Focus on clarity and completeness.",
tools=["Read", "Write", "Edit"],
model="sonnet",
),
},
)

async for message in query(
prompt="Use the doc-writer agent to explain what AgentDefinition is used for",
options=options,
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
elif isinstance(message, ResultMessage) and message.total_cost_usd and message.total_cost_usd > 0:
print(f"\nCost: ${message.total_cost_usd:.4f}")
print()


async def multiple_agents_example():
"""Example with multiple custom agents."""
print("=== Multiple Agents Example ===")

options = ClaudeCodeOptions(
agents={
"analyzer": AgentDefinition(
description="Analyzes code structure and patterns",
prompt="You are a code analyzer. Examine code structure, patterns, and architecture.",
tools=["Read", "Grep", "Glob"],
),
"tester": AgentDefinition(
description="Creates and runs tests",
prompt="You are a testing expert. Write comprehensive tests and ensure code quality.",
tools=["Read", "Write", "Bash"],
model="sonnet",
),
},
setting_sources=["user", "project"],
)

async for message in query(
prompt="Use the analyzer agent to find all Python files in the examples/ directory",
options=options,
):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(f"Claude: {block.text}")
elif isinstance(message, ResultMessage) and message.total_cost_usd and message.total_cost_usd > 0:
print(f"\nCost: ${message.total_cost_usd:.4f}")
print()


async def main():
"""Run all agent examples."""
await code_reviewer_example()
await documentation_writer_example()
await multiple_agents_example()


if __name__ == "__main__":
anyio.run(main)
Loading
Loading