# focus An LLM agent framework in Rust, structured as a Cargo workspace. Built to learn the architecture of [pi](https://github.com/earendil-works/pi) by reimplementing its core layers with a minimal dependency surface (tokio + rustls only). This milestone focuses on calling **Z.ai / 智谱 GLM coding models** (`glm-5.2`, domestic endpoint) end-to-end. ## Quick start ### 1. Create a config file Copy the example (it lists providers but **no API keys** — those go in `auth.json` via `/login`): ```bash mkdir -p ~/.focus cp examples/config.example.json ~/.focus/config.json ``` The config (`~/.focus/config.json`) selects a default provider and lists available ones (host, model). Override the config dir with `FOCUS_CONFIG_DIR`. ### 2. Log in (save your API key) Run `focus` with no arguments to enter the interactive REPL, then use the `/login` slash command: ```bash ./target/debug/focus > /login zai-coding-cn Enter API key for zai-coding-cn: Credentials saved to ~/.focus/auth.json > /quit ``` Your key is stored in `~/.focus/auth.json` (mode `0600`). Log out with `/logout [provider]`. ### 3. Run ```bash cargo build --workspace # Interactive REPL (multi-turn conversation): ./target/debug/focus > 你好 你好!有什么可以帮你的? > /quit # Single prompt (one-shot): ./target/debug/focus "用一句话解释什么是 Rust" # Or pipe via stdin: echo "写一个快排" | ./target/debug/focus # Override the model: ./target/debug/focus --model glm-5.2 "hello" # One-off API key (in-memory only, not saved): ./target/debug/focus --api-key YOUR_KEY "hello" # Print the resolved config: ./target/debug/focus --config ``` `focus --help` shows all options. ### REPL slash commands Inside the REPL (`focus` with no prompt, on a TTY): | Command | Action | |---|---| | `/login [provider]` | store an API key in `~/.focus/auth.json` | | `/logout [provider]` | remove a stored credential | | `/provider [name]` | show or switch the active provider | | `/model [name]` | show or switch the active model | | `/system [text]` | show or set the system prompt | | `/clear` / `/new` | clear the conversation transcript | | `/config` | print the resolved provider config | | `/help` | list commands | | `/quit` | exit focus | Plain text is sent to the model as a prompt; the conversation is multi-turn (the transcript persists across prompts). ### Credential resolution order When running a prompt, the API key is resolved in this order (highest first): 1. `--api-key` CLI flag (in-memory only, never persisted) 2. `~/.focus/auth.json` entry (written by `focus login`) 3. `config.json` provider `apiKey` field (if present) ### 3. Run the real-call smoke test (optional) ```bash ZAI_API_KEY= cargo test -p focus-providers --test zai_smoke -- --ignored --nocapture ``` ## Architecture See [`docs/architecture/`](./docs/architecture/) for the pi reference analysis. The workspace mirrors pi's layering: ``` focus-json minimal JSON parser/serializer (zero deps) focus-core domain types, agent loop, Tool/StreamProvider traits focus-transport HTTP/1.1, TLS (rustls), SSE parser focus-providers Z.ai / OpenAI-compat + Anthropic providers focus-cli config loading + single-prompt runner focus-tools (placeholder) file/shell tools focus-harness (placeholder) session persistence + compaction ``` Dependency flow is strictly one-way: `core` never depends on transport, providers, or tools. All I/O crosses trait boundaries. ## Verification ```bash cargo fmt --all --check cargo clippy --workspace --all-targets -- -D warnings cargo test --workspace ``` All 95 unit/integration tests pass with zero clippy warnings. See [`AGENTS.md`](./AGENTS.md) for the full engineering spec.