|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## What This Is |
| 6 | + |
| 7 | +A monorepo containing three parallel implementations of a **Starter Agent** for Slack built with Bolt for Python. All implementations are functionally identical from the Slack user's perspective but use different AI agent frameworks: |
| 8 | + |
| 9 | +- `claude-agent-sdk/` -- Built with **Claude Agent SDK** |
| 10 | +- `openai-agents-sdk/` -- Built with **OpenAI Agents SDK** |
| 11 | +- `pydantic-ai/` -- Built with **Pydantic AI** |
| 12 | + |
| 13 | +This is a minimal starter template. It includes one example tool (emoji reactions) and optional Slack MCP Server integration. |
| 14 | + |
| 15 | +## Commands |
| 16 | + |
| 17 | +All commands must be run from within the respective project directory (`claude-agent-sdk/`, `openai-agents-sdk/`, or `pydantic-ai/`). |
| 18 | + |
| 19 | +```sh |
| 20 | +# Run the app (requires .env with OPENAI_API_KEY or ANTHROPIC_API_KEY; Slack tokens optional with CLI) |
| 21 | +slack run # via Slack CLI |
| 22 | +python3 app.py # directly |
| 23 | + |
| 24 | +# Lint and format (CI runs these on push to main and all PRs) |
| 25 | +ruff check . |
| 26 | +ruff format --check . |
| 27 | + |
| 28 | +# Run tests |
| 29 | +pytest |
| 30 | +``` |
| 31 | + |
| 32 | +## Monorepo Structure |
| 33 | + |
| 34 | +``` |
| 35 | +.github/ # Shared CI workflows and dependabot config |
| 36 | +claude-agent-sdk/ # Claude Agent SDK implementation |
| 37 | +openai-agents-sdk/ # OpenAI Agents SDK implementation |
| 38 | +pydantic-ai/ # Pydantic AI implementation |
| 39 | +``` |
| 40 | + |
| 41 | +CI runs ruff lint/format checks against all directories via a matrix strategy in `.github/workflows/ruff.yml`. Dependabot monitors `requirements.txt` in all directories independently. |
| 42 | + |
| 43 | +## Architecture (shared across all implementations) |
| 44 | + |
| 45 | +Three-layer design: **app.py** -> **listeners/** -> **agent/** |
| 46 | + |
| 47 | +**Entry point (`app.py`)** initializes Bolt with Socket Mode and calls `register_listeners(app)`. |
| 48 | + |
| 49 | +**Listeners** are organized by Slack platform feature: |
| 50 | +- `listeners/events/` -- `app_home_opened`, `app_mentioned`, `message` |
| 51 | +- `listeners/actions/` -- `feedback_buttons` |
| 52 | + |
| 53 | +Each sub-package has a `register(app)` function called from `listeners/__init__.py`. |
| 54 | + |
| 55 | +**AgentDeps** (`agent/deps.py`) is a dataclass carrying `client`, `user_id`, `channel_id`, `thread_ts`. Constructed in each listener handler and passed to the agent at runtime. |
| 56 | + |
| 57 | +**Conversation history** (`thread_context/store.py`) is a thread-safe in-memory dict keyed by `(channel_id, thread_ts)` with TTL-based cleanup. This enables multi-turn context. |
| 58 | + |
| 59 | +**Handler flow** (DM, mention): get history from store -> run agent -> post response in thread with feedback blocks -> store updated messages. |
| 60 | + |
| 61 | +## Key Differences Between Implementations |
| 62 | + |
| 63 | +| Aspect | Claude Agent SDK | OpenAI Agents SDK | Pydantic AI | |
| 64 | +|--------|-----------------|-------------------|-------------| |
| 65 | +| Agent file | `agent/agent.py` | `agent/agent.py` | `agent/agent.py` | |
| 66 | +| App type | `AsyncApp` (fully async) | `App` (sync) | `App` (sync) | |
| 67 | +| Agent definition | `ClaudeSDKClient` with `ClaudeAgentOptions` | `Agent[AgentDeps](model="gpt-4.1-mini")` | `Agent(deps_type=AgentDeps)` | |
| 68 | +| Model config | Managed by SDK (Claude models) | Set directly on agent constructor | `get_model()` selects provider at runtime (Anthropic preferred) | |
| 69 | +| Tool definition | `@tool` decorated functions via MCP server | `@function_tool` decorated functions | Plain async functions | |
| 70 | +| Tool context param | `args` dict (no context param) | `RunContextWrapper[AgentDeps]` | `RunContext[AgentDeps]` | |
| 71 | +| Execution | `await run_agent(text, session_id=...)` | `Runner.run_sync(agent, input=..., context=...)` | `agent.run_sync(text, model=..., deps=..., message_history=...)` | |
| 72 | +| Result output | `response_text` from collected `TextBlock.text` | `result.final_output` | `result.output` | |
| 73 | +| Conversation history | Session-based via `resume` (server-side) | `list` stored locally | `list[ModelMessage]` stored locally | |
| 74 | +| API key env var | `ANTHROPIC_API_KEY` | `OPENAI_API_KEY` | `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` | |
| 75 | +| Feedback blocks | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` | Native `FeedbackButtonsElement` | |
0 commit comments