# Meow - Agent Notes Rust terminal AI chat client (TUI). Single crate, no workspace. ## Build & Run ```bash cargo build --release # binary: target/release/meow ``` No tests, no lint/typecheck tooling configured. Just `cargo build` / `cargo run`. ## Architecture (Single Crate) | File | Responsibility | |------|---------------| | `src/main.rs` | `tokio::main`, event loop (100ms tick), dispatches to `App` | | `src/app.rs` | State machine: `Chat` / `ModelSelect` / `ProviderConfig`; draws all UI; spawns stream task | | `src/config.rs` | `Config` / `ProviderConfig`; loads/saves `~/.meow/config.toml` via `dirs` | | `src/message.rs` | `Role`, `Message`, `ChatSession` with `uuid` + `chrono` | | `src/storage.rs` | JSON Lines persistence at `~/.meow/data/sessions/.jsonl` | | `src/providers/mod.rs` | `Provider` trait (`async_trait`), `Model`, `ProviderError` | | `src/providers/openai.rs` | `OpenAICompatibleProvider`: `/v1/models`, `/v1/chat/completions` SSE | | `src/tui/mod.rs` | `Tui`: raw mode, alternate screen, mouse capture, panic hook restore | | `src/tui/input.rs` | Multi-line `InputState` with cursor movement; `Shift+Enter` newline, `Enter` submit | | `src/tui/markdown.rs` | `pulldown-cmark` → `ratatui::Line` + `syntect` highlighting (`base16-ocean.dark`) | ## Key Conventions - **Streaming protocol**: `Provider::chat_stream` returns `BoxStream>`. The spawned tokio task sends chunks through an `mpsc::unbounded_channel`. Sentinel `__STREAM_END__` marks completion; `App::poll_stream()` drains the channel on each tick. - **Provider rebuild**: After saving config, `App::rebuild_providers()` reconstructs the `Vec>` from `config.providers`. Always call this after mutating providers. - **First-run behavior**: If `config.providers` is empty, app boots into `ProviderConfig` state. `Esc` in that state quits the app instead of returning to chat. - **Ctrl+C while streaming**: Stops stream and calls `finish_stream_message()` to persist partial response. Do NOT treat as app quit during streaming. - **Session persistence**: `Storage::append_message` is append-only JSON Lines. `save_current_session()` iterates messages and appends each (safe to call multiple times; duplicates are harmless in current design). - **Syntax highlighting**: `syntect` uses `regex-fancy` backend (not `onig`). Theme hardcoded to `base16-ocean.dark`. ## Runtime Data Paths - Config: `~/.meow/config.toml` - Sessions: `~/.meow/data/sessions/.jsonl` ## Dependencies to Know - `reqwest` with `rustls-tls`, `stream` — SSE via `bytes_stream()` - `ratatui` 0.29 + `crossterm` 0.28 — all TUI rendering - `syntect` with `default-themes`, `default-syntaxes`, `regex-fancy` - `pulldown-cmark` 0.12 — markdown parser ## Adding a New Provider Only `OpenAICompatibleProvider` exists. To add a non-OpenAI provider: 1. Implement `Provider` trait in `src/providers/.rs` 2. Register in `src/providers/mod.rs` 3. Wire instantiation in `App::new()` and `App::rebuild_providers()`