/architecture

One event loop, no render races

The agent runs in a spawned task and reaches the interface only through events. Nothing that renders mutates state, and nothing that thinks touches the screen.

62k

lines of Rust across 207 files

~900

tests, none of which touch the network

51

slash commands, one file each

16

built-in tools before a single MCP server

── the shape

Everything hangs off one select!

Conversations own their state, the runtime owns the turns, and the renderer owns nothing at all.

one tokio::select! loop

mainApp (thin coordinator on a single select! loop)
        ├─ AppState
        │    └─ Conversations ── focused + background chats
         each owns: messages · forked session · agent task
        ├─ AgentRuntime.spawn(TurnSpec) ── the one place a turn starts
        │    └─ run_agent_loop ── LLM ↔ tools, events tagged by id
        │         ├─ semantic hint ── skills + MCP tools per prompt
        │         └─ task tool ⇒ sub-agent (fg) | detached (bg)
        ├─ SemanticService ── e5-small ONNX + TF-IDF + RRF
        ├─ ToolRegistry ── bash · edit · write · task · todo …
        ├─ MCPManager ── stdio / http / sse, OAuth in the keyring
        ├─ providers ── DeepSeek web · OpenAI · Anthropic · Gemini
        └─ TUI render ── reads the focused chat; never mutates

CPU-heavy work — ONNX embeddings, the SHA-3 proof-of-work — runs on blocking threads, so a long hash never costs you a frame.

── a turn

What happens between Enter and the answer

Six steps, and you can stop it at any of them.

  1. 01

    Match

    The prompt is scored against skills and MCP tools. The top hits become an ephemeral hint that never persists into the conversation history.

  2. 02

    Send

    The provider forks a session for this chat. On DeepSeek that means solving its SHA-3 proof-of-work locally, then streaming the reply over SSE.

  3. 03

    Call

    Tool calls come out of the stream — native ones where a provider emits them, parsed ones everywhere else, with the provider’s own call id.

  4. 04

    Ask

    Anything with side effects stops at an approval modal with a readable preview: a real diff for edit, the actual text for write, a warning for paths outside the workspace.

  5. 05

    Checkpoint

    Before a write lands, the old content goes into an append-only journal. /undo puts it back — and refuses when the file changed after the edit, because then someone else was editing too.

  6. 06

    Judge

    In GOAL mode a second agent reads the result against your success criteria and either signs off or sends concrete fixes back into the loop.

── src/

Where everything lives

Layering is enforced by the module graph: tools know nothing about the app, the renderer knows nothing about providers.

app/
The coordinator: conversations, the event loop, the agent runtime, keys, GOAL, the search screen, the providers panel.
provider/
DeepSeek web (auth, PoW, SSE, sessions) plus OpenAI-compatible, Anthropic Messages and Gemini clients.
agent/
The agent loop, the sub-agent runner, the tool-call parser.
semantic/
The local RAG layer: embedder, hybrid index, corpora, history store, eval harness.
tools/
Shell and PTY, edit and write, todo, timers, task, and the semantic builtins.
mcp/
Clients, transports, config discovery across eight sources, the manager, OAuth.
server/
The OpenAI-compatible gateway: catalog routing, hyper transport, the dialect itself.
tui/
ratatui views, widgets, markdown rendering — and the theme table this website borrows.
update/
Channels, manifest verification, the staged binary swap.
acp/
Agent Client Protocol server mode: JSON-RPC over stdio, for IDEs that speak it.

── rails

The parts that say no

An agent with a shell is a security surface. These are the places where it is deliberately less convenient.

scoped approvals
A rule is a tool plus a scope: bash · cargo test allows that prefix, edit · <folder> allows writes there. A compound command gets no scope at all — git status && rm -rf / starts with git status.
checkpoints
Every edit and write is journaled first. Files that look like secrets (.env, *.pem, id_rsa) and anything over 8 MiB are never copied anywhere — such an edit is honestly marked irreversible instead.
the keyring
The DeepSeek session and MCP OAuth tokens live encrypted in the OS keyring, never in a plaintext config file.
untrusted text
Project instructions (AGENTS.md, CLAUDE.md…) come from someone else’s repository: symlinks aren’t followed, the text arrives inside an envelope with a one-time marker, and the absolute rules are repeated after it.
off-limits files
The edit tool refuses to write the agent’s own config or any MCP config, and aborts when a file changed since it was read.

── evidence

Measured, not asserted

Claims on this page are checkable in the repository, and most of them are checked on every push.

~900 tests
Unit tests with no network, run on Windows, Linux and macOS.
MRR evals
Retrieval quality is a number, not a feeling: 0.927 for skills, 0.836 for MCP tools.
field ladder
A scenario harness runs the agent against real tasks — and fails a run where every tool call errored but the summary sounded fine.
gates
fmt, clippy -D warnings and the suite block the commit locally and the merge in CI.
--locked
Cargo.lock is committed and CI builds from it: a release is the exact dependency set the tests passed on.

ci

 cargo fmt --check
 cargo clippy -- -D warnings
 cargo test --bin pooprusteek        ~900 tests, no network
 cargo test semantic::eval -- --ignored
    skills    MRR 0.927
    mcp tools MRR 0.836
 build: windows x64/arm64 · linux x64/arm64 · macos arm64
 package: pooprusteek-setup.exe + install.sh + manifest

Go get it

deepseek · deepseek-chatctx:0% · | [streaming]