🏛️ Architecture

Understanding Code Puppy's internal architecture helps you extend and customize it effectively.

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                     Main CLI Loop                        │
│                      (main.py)                           │
├─────────────────────────────────────────────────────────┤
│                   Agent Manager                           │
│    (Discovery, Loading, Switching, Session Tracking)      │
├──────────────┬──────────────┬──────────────┬────────────┤
│   BaseAgent  │  JSONAgent   │  Specialized │   Custom   │
│  (Abstract)  │ (From JSON)  │   Agents     │   Agents   │
├──────────────┴──────────────┴──────────────┴────────────┤
│                     Tool System                           │
│    (File Ops, Shell, Browser, Agent Invocation)          │
├─────────────────────────────────────────────────────────┤
│                   Model Factory                           │
│    (OpenAI, Anthropic, Google, Mistral, etc.)            │
├─────────────────────────────────────────────────────────┤
│                    MCP Manager                            │
│    (External Tool Integration via MCP Protocol)           │
└─────────────────────────────────────────────────────────┘

Package Structure

Core Package: code_puppy/

FilePurpose
main.pyCLI loop and main application logic
config.pyGlobal configuration manager
model_factory.pyConstructs LLM models from configuration
models.jsonAvailable models and metadata registry
callbacks.pyPlugin callback system
session_storage.pySession persistence

Agents Package: code_puppy/agents/

FilePurpose
base_agent.pyAbstract base class for all agents
agent_manager.pyAgent discovery, loading, switching
json_agent.pyJSON-based agent configuration system
agent_code_puppy.pyDefault code generation agent
agent_*.pySpecialized agents (planning, review, etc.)

Tools Package: code_puppy/tools/

FilePurpose
file_operations.pyList, read, grep filesystem operations
file_modifications.pyFile editing with diffs
command_runner.pyShell command execution
agent_tools.pyAgent invocation and reasoning tools
browser/Browser automation tools (30+)

Command Line: code_puppy/command_line/

FilePurpose
command_handler.pyCommand dispatch and routing
command_registry.pyCommand registration system
core_commands.pyCore CLI commands
config_commands.pyConfiguration commands
session_commands.pySession management commands
mcp/MCP subcommands

MCP System: code_puppy/mcp_/

FilePurpose
manager.pyMCP server lifecycle management
health_monitor.pyServer health monitoring
circuit_breaker.pyCircuit breaker pattern
retry_manager.pyRetry logic

Agent System

The agent system uses inheritance and composition:

BaseAgent (Abstract)
    ├── CodePuppyAgent (Built-in Python agent)
    ├── PlanningAgent (Built-in Python agent)
    ├── CodeReviewerAgent (Built-in Python agent)
    ├── ...
    └── JSONAgent (Loads from JSON config)
            ├── user-agent-1.json
            ├── user-agent-2.json
            └── ...

Tool Registration

Tools are registered using decorators:

from code_puppy.tools import register_tool

@register_tool("my_custom_tool")
async def my_tool(param1: str, param2: int = 10) -> dict:
    """Tool description for the LLM.
    
    Args:
        param1: Description of param1
        param2: Description of param2 (default: 10)
    
    Returns:
        Result dictionary
    """
    return {"success": True, "result": "..."}

Message Flow

User Input
    ↓
[Command Handler] ───────── /command? ───▶ Execute Command
    ↓ (chat message)
[Plugin Callbacks: before_message]
    ↓
[Current Agent] ───▶ Prepares context + system prompt
    ↓
[Model Factory] ───▶ Routes to appropriate provider
    ↓
[LLM Provider] ───▶ API call (OpenAI, Anthropic, etc.)
    ↓
[Response Stream] ──▶ Streaming output to console
    ↓
[Tool Calls?] ────────── Yes ───▶ Execute Tools ───▶ Loop back
    ↓ No
[Plugin Callbacks: after_response]
    ↓
Display to User

File Size Guidelines

🧘 Zen Puppy Approves

Keep files under 600 lines! If a file grows beyond that, split it into smaller, composable files.