Files
meow/AGENTS.md
MangoFanFanw ebc20e0013 fix: SSE line buffering, provider trait plumbing, streaming safety, session resume, code block boxes, markdown improvements
Bug fixes:
- Fix SSE chunk parsing: buffer partial lines across network boundaries (openai.rs)
- Use Arc<dyn Provider> so send_message calls through the trait and reuses the HTTP connection pool, instead of hardcoding OpenAICompatibleProvider per request (app.rs)
- Fix Ctrl+Q during streaming: persist partial response before quitting (app.rs)
- Fix Ctrl+N duplicate JSONL messages: remove redundant save_current_session call and guard against new session while streaming (app.rs)
- Fix code block content never rendered: text was accumulated into the wrong variable (markdown.rs)
- Fix status bar stuck on 'Loading models...' after closing model selector (app.rs)
- Fix CJK character panic in session title truncation: use char-level slicing (storage.rs)

New features:
- Session resume with Ctrl+O: list, browse, and load past chat sessions (app.rs, storage.rs)
- Code block boxes: full-border rendering (top/left/right/bottom) with solid background for acrylic/transparent terminals (markdown.rs)
- Code blocks fill chat area width with CJK-aware display width padding (markdown.rs, app.rs)
- Markdown: add heading support (H1-H6), strikethrough, fix bold+italic by combining style stack layers (markdown.rs)

Docs:
- Replace CLAUDE.md with AGENTS.md containing repo-specific agent guidance
2026-05-12 23:25:17 +08:00

3.0 KiB

Meow - Agent Notes

Rust terminal AI chat client (TUI). Single crate, no workspace.

Build & Run

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/<uuid>.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-cmarkratatui::Line + syntect highlighting (base16-ocean.dark)

Key Conventions

  • Streaming protocol: Provider::chat_stream returns BoxStream<Result<String>>. 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<Box<dyn Provider>> 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/<uuid>.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/<name>.rs
  2. Register in src/providers/mod.rs
  3. Wire instantiation in App::new() and App::rebuild_providers()