CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Development Commands
Core Development Tasks
- Install dependencies:
make install(requires uv and pre-commit) - Run all checks:
make allorpre-commit run --all-files - Run tests:
make test - Build docs:
make docsormake docs-serve(local development)
Single Test Commands
- Run specific test:
uv run pytest tests/test_agent.py::test_function_name -v - Run test file:
uv run pytest tests/test_agent.py -v - Run with debug:
uv run pytest tests/test_agent.py -v -s
Project Architecture
Core Components
Agent Factory (pydantic_deep/agent.py)
create_deep_agent(): Main factory function for creating configured agentscreate_default_deps(): Helper for creating DeepAgentDeps with sensible defaults- Built on top of pydantic-ai's Agent class
Dependencies (pydantic_deep/deps.py)
DeepAgentDeps: Dataclass holding agent dependencies (backend, working_dir, skills_dirs, subagents)- Passed to agent.run() for runtime configuration
Backends (from pydantic-ai-backend)
BackendProtocol: Interface for file storage backendsStateBackend: In-memory file storage (for testing, ephemeral use)LocalBackend: Real filesystem operationsDockerSandbox: Isolated Docker container executionCompositeBackend: Combines multiple backends with routing
Toolsets (pydantic_deep/toolsets/)
TodoToolset: Task planning and tracking tools (read_todos, write_todos) - from pydantic-ai-todocreate_console_toolset: File operations (ls, read, write, edit, glob, grep, execute) - from pydantic-ai-backendSubAgentToolset: Spawn and delegate to subagents - from subagents-pydantic-aiSkillsToolset: Load and use skill definitions from markdown files
Subagents (from subagents-pydantic-ai)
create_subagent_toolset(): Factory function to create subagent toolsetsget_subagent_system_prompt(): Generate system prompt for subagent tools- Dual-mode execution: sync (blocking) or async (background)
- Task management: check_task, list_active_tasks, soft_cancel_task, hard_cancel_task
- Types:
SubAgentConfig,CompiledSubAgent,TaskHandle,TaskStatus,TaskPriority
Processors (from summarization-pydantic-ai)
SummarizationProcessor: LLM-based conversation summarization for token managementSlidingWindowProcessor: Zero-cost message trimming without LLM callscreate_summarization_processor(): Factory function for summarization processorscreate_sliding_window_processor(): Factory function for sliding window processors
Types (pydantic_deep/types.py)
- Pydantic models for all data structures
FileData,FileInfo,WriteResult,EditResult,GrepMatchTodo,SubAgentConfig,CompiledSubAgentSkill,SkillDirectory,SkillFrontmatterResponseFormat: Alias for structured output specification
Key Design Patterns
Backend Abstraction
from pydantic_ai_backends import StateBackend, LocalBackend, CompositeBackend
# In-memory for testing
backend = StateBackend()
# Real filesystem
backend = LocalBackend(root_dir="/path/to/workspace")
# Combined backends with routing
backend = CompositeBackend(
default=StateBackend(),
routes={
"/project/": LocalBackend(root_dir="/home/user/project"),
},
)
Toolset Registration
from pydantic_deep import create_deep_agent, DeepAgentDeps
from pydantic_ai_backends import create_console_toolset
from pydantic_ai_todo import create_todo_toolset
agent = create_deep_agent(
model="openai:gpt-4.1",
toolsets=[create_todo_toolset(), create_console_toolset()],
)
Skills System
# Skills are markdown files with YAML frontmatter
# Located in skills_dirs specified in DeepAgentDeps
deps = DeepAgentDeps(
backend=StateBackend(),
skills_dirs=["/path/to/skills"],
)
Structured Output
from pydantic import BaseModel
from pydantic_deep import create_deep_agent
class TaskResult(BaseModel):
status: str
details: str
# Agent returns TaskResult instead of str
agent = create_deep_agent(output_type=TaskResult)
Context Management / Summarization
from pydantic_deep import (
create_deep_agent,
create_summarization_processor,
create_sliding_window_processor,
)
# Automatically summarize when reaching token limits
processor = create_summarization_processor(
trigger=("tokens", 100000), # or ("messages", 50) or ("fraction", 0.8)
keep=("messages", 20), # Keep last N messages after summarization
)
# Or use sliding window for zero-cost trimming
window = create_sliding_window_processor(
trigger=("messages", 100),
keep=("messages", 50),
)
agent = create_deep_agent(history_processors=[processor])
Testing Strategy
- Unit tests:
tests/directory with comprehensive coverage - Test models: Use
TestModelfrom pydantic-ai for deterministic testing - Async testing: pytest-asyncio with
asyncio_mode = "auto" - Coverage requirement: 100% coverage is required for all PRs
Key Configuration Files
pyproject.toml: Main configuration (dependencies, tools, coverage)Makefile: Development task automationmkdocs.yml: Documentation configuration.pre-commit-config.yaml: Pre-commit hook configuration
Important Implementation Notes
- Backend Protocol: All backends implement
BackendProtocolfor consistent file operations - Async-First: Most operations are async, use
awaitappropriately - Type Safety: Full type annotations with Pyright strict mode
- Sandbox Support: DockerSandbox requires
dockeroptional dependency
Documentation Development
- Local docs:
make docs-serve(serves at http://localhost:8000) - Docs source:
docs/directory (MkDocs with Material theme) - API reference: Auto-generated from docstrings using mkdocstrings
Dependencies Management
- Package manager: uv (fast Python package manager)
- Lock file:
uv.lock(commit this file) - Sync command:
make syncto update dependencies - Optional extras: sandbox, cli, dev
Best Practices
Coverage
Every pull request MUST have 100% coverage. You can check the coverage by running make test.
Use # pragma: no cover for legitimately untestable code (e.g., platform-specific branches).
Type Annotations
All code must pass both Pyright and MyPy strict checking:
make typecheckfor Pyrightmake typecheck-mypyfor MyPy
Writing Documentation
Always reference Python objects with backticks and link to API reference:
The [`create_deep_agent`][pydantic_deep.agent.create_deep_agent] function creates a configured agent.
Rename a Class
When renaming a class, add deprecation warning:
from typing_extensions import deprecated
class NewClass: ...
@deprecated("Use `NewClass` instead.")
class OldClass(NewClass): ...