diff --git a/.gitignore b/.gitignore index 16751f8..b04e58c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,4 @@ reference/** .env *.env -AGENTS.md -CLAUDE.md Cargo.lock diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d5ec9a9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,73 @@ +# PicoBot + +## Maintenance + +- **Update this file on any architectural change** — module boundaries, data flow, key constraints, or build/test commands must be reflected here + +## Build & Run + +- `cargo build` — build the binary +- `cargo run -- gateway` — start gateway server (binds `127.0.0.1:19876` by default) +- `cargo run -- chat` — connect to gateway as CLI client (default `ws://127.0.0.1:19876/ws`) + +## Config + +- Config file: `~/.picobot/config.json` or `./config.json` (fallback order) +- `.env` is loaded and env var placeholders `` are substituted into config +- Config example: `config.example.json` + +## Tests + +- `cargo test --lib` — run unit tests (FAILS: `src/session/session.rs:657` missing `workspace_dir` field in test helper) +- `cargo test --test test_integration -- --ignored` — run integration tests (requires `tests/test.env` with API keys) + +## Reference + +- `reference/` — third-party reference implementations (nanobot, Mini-Agent, zeroclaw); not part of this project; use for similar functionality patterns + +## Architecture + +### Modes + +- **Gateway mode** (`cargo run -- gateway`): HTTP/WebSocket server; owns `GatewayState` which holds all services +- **Client mode** (`cargo run -- chat`): TUI chat client; connects to gateway via WebSocket, purely for user interaction + +### Core Data Flow + +``` +Channel → MessageBus → SessionManager → AgentLoop → (tools) → SessionManager → MessageBus → OutboundDispatcher → Channel + ↑ + ControlChannel ──→ SessionManager (dialog ops: create/switch/archive/delete) +``` + +### Modules + +| Module | Responsibility | Key Types | +|--------|---------------|-----------| +| `gateway` | Server lifecycle, HTTP/WS endpoints, owns `GatewayState` | `GatewayState`, `run()` | +| `client` | TUI rendering, WebSocket client for CLI chat | `App`, `run()` | +| `channels` | External integrations (Feishu, CLI chat) | `ChannelManager`, `Channel` trait | +| `bus` | Async message queue (inbound/outbound/control channels) | `MessageBus`, `InboundMessage`, `OutboundMessage`, `ControlMessage` | +| `session` | Conversation session lifecycle, dialog operations | `SessionManager`, `Session` | +| `agent` | LLM call loop, tool execution, context compression | `AgentLoop` | +| `providers` | LLM API clients (OpenAI-compatible, Anthropic) | `LLMProvider` trait, factory `create_provider()` | +| `tools` | Agent tools (bash, file operations, http, web) | `ToolRegistry`, `Tool` trait | + +### Functional Boundaries + +- **Channels** only send/receive messages via `MessageBus`; they know nothing about sessions or LLM +- **MessageBus** is a pure async queue; it routes nothing, just passes messages +- **SessionManager** owns session state and dialog operations; it does NOT call LLM directly +- **AgentLoop** receives dialog events from `SessionManager`, calls LLM via `providers`, executes tools, returns text responses +- **Providers** are pure HTTP clients; no bus/session/channel awareness +- **Tools** are executed by `AgentLoop`; they receive raw arguments and return string results + +### Key Constraints + +- Gateway **changes working directory** to workspace on startup (`src/gateway/mod.rs:31`) +- `ChannelManager` owns the `MessageBus` and all channel instances +- `OutboundDispatcher` routes outbound messages to the correct channel via `ChannelManager` + +## Known Issues + +- `src/session/session.rs:657` — `LLMProviderConfig` struct requires `workspace_dir` but test helper at line 656-669 doesn't provide it; test code needs `workspace_dir: PathBuf::new()` added diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7457da4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# Claude Code + +Read `AGENTS.md` for project context, build commands, architecture, and conventions.