# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Prerequisites

- **ripgrep (rg)** - Required for:
  - Code search via `grep_search` tool (`src/tools/impl/grep_search.py`)
  - Directory tree visualization via `get_directory_structure` tool (`src/tools/impl/terminal.py`)
- **fd** - Required for:
  - `@:file:`, `@:dir:`, and `@:image:` reference tab completion (`src/cli/resolvers/file.py`, `src/cli/resolvers/image.py`)
  - Fallback when not in a Git repository

## Development Commands

### Setup
```bash
make install      # Install dependencies + pre-commit hooks (runs: uv sync --all-groups && uv run pre-commit install)
```

### Testing
```bash
make test                    # Run all tests (uv run pytest tests)
uv run pytest tests/path/to/test_file.py              # Run specific test file
uv run pytest tests/path/to/test_file.py::test_name   # Run specific test
uv run pytest -v                                       # Verbose output
uv run pytest -k "test_pattern"                        # Run tests matching pattern
```

### Linting & Formatting
```bash
make lint-fix     # Format and lint code (pyupgrade, autoflake, isort, black, mypy)
make pre-commit   # Run pre-commit hooks on all files
```

### Running the CLI
```bash
# From within repository (development mode):
uv run langrepl              # Start interactive session
uv run langrepl -a general   # Use specific agent
uv run langrepl -r           # Resume last conversation
uv run langrepl -w /path     # Set working directory
uv run langrepl "message"    # CLI mode (one-shot)
uv run langrepl -r "message" # CLI mode with resume
uv run langrepl -s -a general  # Start LangGraph server mode

# After global install (uv tool install --editable .):
langrepl              # or: lg
```

### Cleanup
```bash
make clean        # Remove cache/build artifacts (__pycache__, *.egg-info, etc.)
```

### Release & Publishing
The project uses automated releases with Release Please and PyPI publishing:

1. **Conventional Commits**: Use conventional commit messages (`feat:`, `fix:`, `chore:`, etc.)
2. **Release Please**: Automatically creates release PRs with version bumps and changelog updates
3. **PyPI Publishing**: On release publication, GitHub Actions automatically:
   - Builds distributions (`uv build`)
   - Publishes to TestPyPI first (https://test.pypi.org/project/langrepl/)
   - Publishes to PyPI (https://pypi.org/project/langrepl/)

Manual publishing (if needed):
```bash
uv build          # Build distributions
uv publish        # Publish to PyPI (requires authentication)
```

## Architecture Overview

Langrepl is an interactive terminal CLI for building and running LLM agents. It uses LangChain/LangGraph for agent orchestration, Prompt Toolkit for the interactive interface, and Rich for rendering.

### Component Hierarchy

```
┌─────────────────────────────────────────────────────────────┐
│ CLI Layer (src/cli/)                                        │
│  ├── bootstrap/    - App initialization & session lifecycle │
│  ├── core/         - Context & session management           │
│  ├── handlers/     - Command handlers (/resume, /compress)  │
│  ├── dispatchers/  - Message & command routing              │
│  ├── ui/           - Interactive prompt & rendering         │
│  ├── completers/   - Tab completion (slash cmds, @refs)     │
│  └── resolvers/    - Reference resolution (@:file:path)     │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Agent Layer (src/agents/)                                   │
│  ├── factory.py     - AgentFactory & GraphFactory           │
│  ├── deep_agent.py  - Creates agents with planning tools    │
│  └── react_agent.py - Base ReAct agent implementation       │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Tool Layer (src/tools/)                                     │
│  ├── factory.py      - Tool registration & lookup           │
│  ├── impl/           - Built-in tools (file, web, terminal) │
│  ├── internal/       - System tools (todo, memory)          │
│  └── subagents/      - Task delegation tool                 │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Middleware Layer (src/middleware/)                          │
│  └── approval.py     - Tool approval middleware             │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ Core Layer (src/core/)                                      │
│  ├── config.py   - AgentConfig, LLMConfig, MCPConfig        │
│  ├── settings.py - Pydantic settings from .env              │
│  └── logging.py  - Centralized logging setup                │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│ State Layer (src/state/)                                    │
│  └── base.py - BaseState with todos, files, token tracking  │
└─────────────────────────────────────────────────────────────┘
```

### Key Design Patterns

#### 1. Configuration System
- **Directory-based YAML configs** located in `.langrepl/` (or custom working directory)
  - `agents/*.yml` - Agent definitions (filename must match agent name)
  - `llms/*.yml` - LLM configs organized by provider (flexible filenames)
  - `checkpointers/*.yml` - Checkpoint configs (filename must match type)
  - `subagents/*.yml` - Subagent definitions (filename must match name)
  - `config.mcp.json` - MCP server configurations
  - `config.approval.json` - Tool approval rules (allow/deny patterns)

- **Backward compatibility**: Legacy single-file format (`config.agents.yml`) still supported
- **Validation**: No duplicates allowed; filename must match config key (except LLMs)
- **String references resolve at load time:**
  ```yaml
  # agents/my-agent.yml
  name: my-agent
  llm: haiku-4.5           # References llms/*.yml
  checkpointer: sqlite     # References checkpointers/*.yml
  subagents: [coder]       # References subagents/*.yml
  ```

- **Prompt loading supports multiple files:**
  ```yaml
  prompt: prompts/agent.md              # Single file
  prompt: [prompts/base.md, prompts/tools.md]  # Multiple files (concatenated)
  ```

#### 2. Tool System
- **Three-part naming:** `<category>:<module>:<function>`
  - `impl:file_system:read_file` - Built-in tools
  - `internal:todo:add_todo` - System tools
  - `mcp:context7:resolve-library-id` - MCP server tools

- **Wildcard support:** `impl:*:*`, `impl:file_system:read_*`, `mcp:server:*`

- **Tool approval flow (via ApprovalMiddleware):**
  1. Check `approval_mode` (SEMI_ACTIVE, ACTIVE, AGGRESSIVE)
  2. Check `config.approval.json` rules (always_allow/always_deny with regex)
  3. If no rule matches, interrupt graph and prompt user
  4. User responses: "allow", "always allow", "deny", "always deny"
  5. Approval metadata can be configured per-tool via `tool.metadata["approval_config"]`

- **Custom tools:** Add to `src/tools/impl/`, define as LangChain tools, register in `factory.py`. Approval is handled automatically by middleware.

#### 3. Agent Architecture
- **Deep Agent = ReAct Agent + Planning Tools + Subagents**
  - `create_deep_agent()` wraps `create_react_agent()` with additional capabilities
  - Adds `task` tool for delegating to subagents
  - Internal tools (todo, memory) injected automatically
  - Supports virtual filesystem via `files` state field

- **Subagents are full agents:**
  - Each has own prompt, tools, and LLM configuration
  - Referenced in parent's `subagents` list
  - Parent calls via `task` tool with natural language instructions

#### 4. Session & Context Management
- **Context** (`src/cli/core/context.py`):
  - Holds: agent name, model, thread_id, working_dir, approval_mode
  - Created once per CLI invocation
  - Updated via `update_context()` when switching agents/models

- **CLISession** (`src/cli/core/session.py`):
  - Main REPL loop with graph execution
  - Manages graph lifecycle (async context manager)
  - Handles reload flag for agent/model switches
  - Coordinates message/command dispatchers

- **Initializer** (`src/cli/bootstrap/initializer.py`):
  - Singleton pattern for config loading
  - Creates `.langrepl/` directory on first run
  - Copies template configs from `resources/configs/default/`
  - Loads and resolves all configurations (agents, llms, checkpointers, mcp)

#### 5. Checkpoint & Conversation Management
- **SQLite checkpoint storage** (LangGraph's SqliteSaver)
  - Each conversation = unique `thread_id` (UUID)
  - Checkpoints saved after each graph step
  - Stored in `.langrepl/.db/checkpoints.db`

- **Resume** (`/resume` command):
  - Lists all threads with timestamps + first human message
  - Loads selected thread's history
  - Restores reference mappings

- **Replay** (`/replay` command):
  - Shows all human messages in current thread
  - Branches from selected message (creates new checkpoint)
  - Deletes checkpoints after selected point
  - Preserves original thread

- **Compress** (`/compress` command):
  - Summarizes message history using LLM
  - Creates new thread with compressed history
  - Auto-triggered when context usage exceeds threshold (configurable in agent config)

#### 6. Reference System
- **Syntax:**
  - Files: `@:file:path/to/file`
  - Directories: `@:dir:path/to/directory`
  - Images: `@:image:path/to/image.png`
- **Tab completion:** Triggers file/directory/image suggestions based on reference type
- **Resolution:** Expands to absolute paths before sending to LLM
- **Mapping storage:** Session stores mapping for rendering in UI
- **Image Support:**
  - Supported formats: PNG, JPEG, GIF, WebP
  - Images are base64-encoded and sent as multimodal content blocks
  - Can use `@:image:` references with tab completion or paste absolute paths directly
  - Images automatically detected and processed in user messages
  - Tab completion filters by image file extensions

#### 7. Message Dispatching
- **MessageDispatcher** (`src/cli/dispatchers/messages.py`):
  - Streams graph execution with `astream_events()`
  - Handles interrupts (tool approval, user prompts)
  - Triggers auto-compression when threshold exceeded
  - Deduplicates rendered messages by ID
  - Resolves and stores reference mappings

- **CommandDispatcher** (`src/cli/dispatchers/commands.py`):
  - Parses slash commands with `shlex.split()`
  - Routes to appropriate handler (agents, resume, compress, etc.)
  - Validates arguments before execution

#### 8. MCP Integration
- **MCP servers as tool providers:**
  - Defined in `config.mcp.json` with command/args/transport
  - Tools imported via `langchain-mcp-adapters`
  - Support include/exclude filters per server
  - Can be toggled on/off via `/mcp` command

- **Repair commands:**
  - If server fails, runs `repair_command` before retry
  - Useful for clearing caches or resetting state

## Important Implementation Details

### Virtual Filesystem (`files` state field)
- Tools can store large outputs in state instead of returning directly
- Reduces token usage by keeping data out of message history
- Uses custom reducer: `file_reducer` merges dicts across updates
- Access pattern: Write with `files={"/path": "content"}`, read from state

### Token Tracking & Cost Calculation
- State fields: `current_input_tokens`, `current_output_tokens`, `total_cost`
- Updated by agent after each LLM call
- Displayed in status line during execution
- Cost formula: `(input_tokens / 1M * input_cost_per_mtok) + (output_tokens / 1M * output_cost_per_mtok)`

### User Memory System
- Stored in `.langrepl/memory.md`
- Content injected into agent prompts automatically
- Edit via `/memory` command (opens in $EDITOR)
- Use `{user_memory}` placeholder in custom prompts for explicit placement

### Approval Mode Behavior
- **SEMI_ACTIVE** (default): Ask unless `always_allow` matches
- **ACTIVE**: Auto-approve unless `always_deny` matches
- **AGGRESSIVE**: Bypass all approval (never ask)
- Toggle via `/approval` command (cycles through modes)

### Thinking/Reasoning Support
- Automatically extracts thinking from:
  1. Metadata (`additional_kwargs.thinking` for Bedrock)
  2. Content blocks (`{"type": "thinking"}`, `{"type": "reasoning"}`)
  3. XML tags (`<think>...</think>` at start of content)
- Rendered as italic/dimmed text before main content
- Configure via `extended_reasoning` in LLM config

### Code Block Malformation Fixes
- Renderer auto-fixes escaped backticks: `\`\`\`` → ` ``` `
- Handles mixed escaping and incomplete blocks
- Three-pass regex cleanup + line-by-line state machine
- Located in `renderer.py:_fix_malformed_code_blocks()`

### Pre-commit Hooks
- Runs: pyupgrade (py313+), autoflake, isort, black, mypy
- Auto-installs via `make install`
- Manually trigger: `make pre-commit`
- Skips on merge commits

## Testing Guidelines

### Test Structure
- Mirror source structure: `tests/cli/handlers/test_resume.py` ↔ `src/cli/handlers/resume.py`
- Use `pytest-asyncio` for async tests
- Fixtures in `tests/conftest.py`:
  - `temp_dir` - Temporary directory for file operations
  - `create_test_graph` - Factory for creating test graphs with tools

### What to Test
- Focus on complex logic, edge cases, and error handling
- Prioritize:
  1. State management (session, context, graph lifecycle)
  2. User input validation (malformed commands, references)
  3. Async operations and race conditions
  4. File I/O and checkpoint integrity
  5. Interactive UI state (key bindings, selection)

### What NOT to Test
- Simple property getters/setters
- Pure delegation methods
- Configuration dataclasses without logic
- Theme/styling code

## Directory Reference

```
langrepl/
├── src/
│   ├── agents/          - Agent implementations (deep_agent, react_agent)
│   ├── cli/             - CLI interface (REPL, commands, UI)
│   ├── core/            - Config, settings, logging
│   ├── llms/            - LLM provider factories
│   ├── mcp/             - MCP server integration
│   ├── state/           - LangGraph state schemas
│   ├── tools/           - Tool implementations and wrappers
│   └── utils/           - Utilities (templating, rendering)
├── tests/               - Test suite (mirrors src/ structure)
├── resources/
│   └── configs/default/ - Template configs (copied to .langrepl/ on first run)
├── pyproject.toml       - Project metadata and dependencies
├── Makefile             - Development commands
└── .langrepl/           - User config directory (created at runtime)
    ├── agents/          - Agent configs (my-agent.yml)
    ├── llms/            - LLM configs by provider (anthropic.yml, openai.yml)
    ├── checkpointers/   - Checkpointer configs (sqlite.yml, memory.yml)
    ├── subagents/       - Subagent configs (explorer.yml)
    ├── config.mcp.json
    ├── memory.md
    └── .db/checkpoints.db
```
