██████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗███████╗████████╗███████╗███████╗██╗ ██╗
██╔══██╗██╔═══██╗██╔═══██╗██╔══██╗██╔══██╗██║ ██║██╔════╝╚══██╔══╝██╔════╝██╔════╝██║ ██╔╝
██████╔╝██║ ██║██║ ██║██████╔╝██████╔╝██║ ██║███████╗ ██║ █████╗ █████╗ █████╔╝
██╔═══╝ ██║ ██║██║ ██║██╔═══╝ ██╔══██╗██║ ██║╚════██║ ██║ ██╔══╝ ██╔══╝ ██╔═██╗
██║ ╚██████╔╝╚██████╔╝██║ ██║ ██║╚██████╔╝███████║ ██║ ███████╗███████╗██║ ██╗
╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝
A free, terminal-native alternative to Claude Code — with parallel chats, sub-agents, an iterative goal loop, MCP with OAuth, multiple LLM providers, and a fully local RAG layer over skills, tools, and your own conversation history.
Pooprusteek is a Rust rewrite of the TypeScript Poopseek. It talks to DeepSeek's web API directly (no paid API key), streams responses token-by-token, runs shell tools, speaks MCP, and lets you drive several conversations and background agents at once — all inside a single, snappy terminal UI. When you outgrow DeepSeek, plug in any OpenAI-compatible, Anthropic, or Gemini endpoint via /providers.
Note
Pooprusteek uses the reverse-engineered DeepSeek web API (chat.deepseek.com), not the official API-key product. It needs a session token and solves DeepSeek's SHA-3 proof-of-work locally. It's unofficial and may break if the upstream API changes.
Each chat is an isolated conversation with its own forked session — turns never collide.
/new+/chats— open and switch between parallel sessions;Tab/Shift+Tabcycles focus./btw <question>— fire a one-shot side-question that answers in the background without disturbing your main turn.- Background chats keep streaming while you work elsewhere; the status bar shows how many are live.
A fully offline semantic layer: multilingual embeddings (e5-small, quantized ONNX via fastembed) + Snowball-stemmed keyword TF-IDF, fused with Reciprocal Rank Fusion. The model (~120 MB) downloads once on first launch into the data directory — zero network after that. Works cross-language: a Russian prompt matches English descriptions and vice versa.
Three corpora, one engine:
- Skills — every outgoing prompt is matched against the whole skill catalog; the top hits ride along as an ephemeral hint ("this skill may apply — load it with the
skilltool"). The model discovers capabilities instead of you enabling them by hand. - MCP tools — same matching, plus deferred schemas: with many tools connected (one Playwright server ≈ 25 tools), the system prompt carries only a server-level summary (
playwright (25 tools)) — individual tools aren't enumerated at all, saving thousands of tokens per request. Full definitions arrive exactly when needed — inlined into the hint for matched tools, or on demand via thetool_searchbuiltin. Never bricks: with embeddings unavailable it degrades to lexical search, and with RAG disabled full schemas return automatically. - Conversation history — every saved session is chunked, embedded, and persisted (
data_dir/semantic/history.json, incremental with per-session watermarks). Search it yourself with/search— a dedicated screen with sorting (relevance / newest / oldest), role filters, and per-session dedup — or let the agent recall past solutions itself via thehistory_searchtool. Enter on a result loads that session.
Control it with /rag (status), /rag on|off, /rag reload (re-verify the model, re-embed everything). Retrieval quality is guarded by an MRR eval harness (cargo test semantic::eval -- --ignored --nocapture): skills 0.927, MCP tools 0.836.
The built-in DeepSeek web client is the default; /providers manages any number of extra endpoints speaking three wire protocols:
- OpenAI Chat Completions — LM Studio, Ollama
/v1, vLLM, OpenRouter, … - Anthropic Messages — the Claude API and compatible proxies (modern no-sampling models handled correctly).
- Google Generative Language — Gemini.
Add via a step-by-step wizard or one line (/providers add <name> <base_url> [model] [api_key]), switch instantly from the panel, pick models with /models (validated against the provider's live list).
Spin off isolated agents for sub-tasks — inspired by Claude Code.
- The model can spawn them itself via the
tasktool; you can via/agent <task>. - Foreground by default (only the conclusion returns into your turn — clean context), with
background:trueopt-in that detaches and notifies on completion. /agentslists running agents and lets you stop them.
/goal arms a two-agent loop: a worker pursues your goal, then a separate evaluator judges whether it's actually met, feeding back concrete fixes until it passes. Sessions auto-swap after repeated failures to escape dead ends.
- Shell (
bash/powershell) — foreground, background, and interactive PTY processes. /jobs·/ps— list, kill, and prune background jobs; persistent jobs (dev servers) survive turns with an idle TTL.- Tool approval with a
/whitelistfor auto-approving trusted tools. tool_search·history_search·skill— the semantic builtins that let the model navigate its own capability space.
- Model Context Protocol over stdio / HTTP / SSE, auto-discovered from 8 config sources (own, workspace, Claude Desktop, VS Code, Claude CLI, Cursor, Opencode…). Browse with
/mcp. /mcp add— paste a JSON config, walk a wizard, or one-line it (/mcp add <name> <command> [args…]).- OAuth authorization (
/mcp auth) — full RFC 9728/8414/7591 + PKCE flow for servers that need it; tokens live encrypted in the OS keyring, never in plaintext config. - Skills — reusable markdown instruction sets (
SKILL.md), discovered from a dozen agent-ecosystem directories (.skills/,.claude/skills/,~/.agents/skills/, Cursor/Windsurf/Cline/Codex dirs, …). Enable manually with/skillsto pin one into the system prompt — or let the semantic matcher suggest them per-prompt automatically.
Markdown + syntax highlighting, streaming with a live thinking indicator, multi-line input with history, @file mentions, session save/load, Markdown export/import, a dedicated search screen, and a Catppuccin Mocha theme — all on an event-driven loop with no render races. Logs go to a file in the data dir, never over your screen.
Windows — grab pooprusteek-setup.exe from the
latest release and click
through: pick a folder, press Install. No admin rights. It adds pooprusteek to
your PATH and the Start menu and registers an uninstaller in Apps. Uninstalling
asks whether to delete your settings and sessions too. The installer isn't
code-signed yet, so SmartScreen may warn: More info → Run anyway.
macOS (Apple Silicon) · Linux (x86_64 / arm64)
curl -fsSL https://github.com/Aver005/pooprusteek/releases/latest/download/install.sh | shInstalls to ~/.local/bin and adds it to PATH. Options go after sh -s --:
--dir <path>, --channel dev, --uninstall. Linux builds need glibc 2.39+
(Ubuntu 24.04 / Debian 13 or newer); Intel Macs aren't supported, since ONNX
Runtime ships no prebuilt library for them.
The folder you install into is also where /update puts new versions, so it
must stay writable by you.
From source — cargo install --path . (Rust edition 2024, MSRV 1.91).
pooprusteek # launch the TUI
pooprusteek --acp # run as an ACP server (JSON-RPC over stdio, for IDEs)
pooprusteek --version # print the version
pooprusteek --debug-log # developer debug log (data dir; .dev/ in debug builds)On first launch, an onboarding flow helps you set your DeepSeek token and model.
Tip
The first launch also downloads the embedding model (~120 MB) in the background and indexes your saved sessions — the status bar reports progress; everything else is usable meanwhile. Every launch after that is fully offline. Don't want it? /rag off or [semantic] enabled = false.
| Key | Action |
|---|---|
Enter |
Send message (trailing \ continues on a new line) |
Shift+Enter |
Insert a newline |
Tab / Shift+Tab |
Switch to next / previous chat |
Esc |
Cancel generation · clear chat · quit (context-dependent) |
Ctrl+C |
Cancel generation / quit |
Ctrl+L |
Clear current chat |
Ctrl+P |
Toggle the stats panel |
Ctrl+A |
Select all input |
↑ / ↓ |
Input history / scroll |
PageUp / PageDown |
Scroll history |
Chat & sessions
| Command | Description |
|---|---|
/new · /chats |
Open a new parallel chat · switch between chats |
/btw <question> |
One-shot background side-question |
/search [query] |
History-search screen: semantic + keyword search over all saved sessions — sort (s), role filter (r), unique-per-session (u), Enter opens the found session |
/clear · /home |
Clear history · return to the landing screen |
/compact |
Summarize history to shrink context |
/reset · /last |
Reset session · open the most recent session |
/load <id> · /sessions · /session |
Load a session · list sessions · current session info |
/delete [id] · /delete-local [id] |
Delete sessions — account + local, or local-only; multi-select picker with an All/Local/Remote filter and a confirm step |
/export [path] · /import <path.md> |
Export / import chat as Markdown |
/cwd (/cd, /move) <path> |
Change working directory |
/attach <path…> |
Attach files to the next message |
Agents & tools
| Command | Description |
|---|---|
/agent <task> · /agents |
Launch a sub-agent · list and stop running agents |
/goal |
Toggle the iterative goal-driven loop |
/jobs [list|kill <id>|prune] · /ps |
Manage background jobs |
/tools · /whitelist |
List tools · manage tool auto-approval |
/mcp [add|auth|ttl <s>|reload] |
MCP view · add a server (JSON / wizard / one-liner) · OAuth-authorize · cache TTL · reload |
/skills [enable|disable <name>] |
Manage skills |
/rag [on|off|reload] |
Semantic matching: live status · full on/off switch · reinitialize (verify/re-download the model, re-embed skills + MCP tools + history) |
Providers & models
| Command | Description |
|---|---|
/providers |
Provider panel: built-in DeepSeek + your OpenAI-compat / Anthropic / Gemini endpoints; Enter activates, a adds, d removes |
/providers add [<name> <base_url> [model] [api_key]] |
Add an endpoint — quick one-liner or step-by-step wizard |
/models [id] |
Pick a model from the active provider's live list · or switch directly (validated) |
Settings & misc
| Command | Description |
|---|---|
/rate <ms>|<N>/min|off |
Rate limit: ms between requests and/or max requests per minute |
/retry <N|on|off|-1> |
Max retries on API failure (-1 = infinite) |
/update [channel stable|dev] · /autoupdate [on|off] |
Self-update now or pick the update channel · toggle the startup auto-check |
/debug [on|off] |
Toggle debug logging to .dev/debug.log (no args = switch) |
/help · /version · /quit |
Help · version · exit |
Config lives at {config_dir}/pooprusteek/config.toml — e.g. ~/.config/pooprusteek/config.toml on Linux, %APPDATA%\pooprusteek\ on Windows, ~/Library/Application Support/pooprusteek/ on macOS. Sessions, history, the semantic index, the embedding model, and mcp.json live under the platform data directory.
[provider]
kind = "deepseek"
token = "your-deepseek-token"
model = "deepseek-chat"
temperature = 0.7
max_tokens = 4096
# Extra endpoints managed via /providers land here as [[providers]] entries
# (protocol = "openai" | "anthropic" | "gemini"); `active_provider` picks one.
[ui]
theme = "default"
show_status_bar = true
[agent]
max_steps_per_turn = 256
max_tools_per_step = 10
max_context_messages = 256
rate_limit_ms = 0
rate_limit_per_minute = 0 # max requests per rolling 60s window (0 = off)
max_retries = 0 # -1 = infinite, 0 = none, N = N+1 attempts
[semantic]
enabled = true # the whole RAG layer; /rag on|off flips this too
top_k = 3 # max suggestions per corpus per turn
min_dense_score = 0.80 # cosine floor for hint candidates without keyword overlap
mcp_schemas = "auto" # "auto" (defer above 12 tools) | "full" | "deferred"/update checks your update channel and installs a newer build. The
binary is downloaded, verified against the release's manifest.json, staged
next to the current one and swapped in. It takes effect on the next launch,
because a running executable is renamed aside rather than overwritten.
/autoupdate on runs the same check in the background on every startup
(off by default, [update] auto).
| Channel | Source | Installs when |
|---|---|---|
stable (default) |
the latest tagged release vX.Y.Z |
its version is newer than yours; never downgrades |
dev |
the rolling dev prerelease, rebuilt on every develop push |
the binary differs from yours |
Switch with /update channel stable|dev ([update] channel). In a
cargo run dev build /update asks for confirmation first.
Releases are cut with scripts/release.sh patch|minor|major (see
Releasing). Integrity is guaranteed, authenticity isn't: trust
rests on TLS + GitHub, and binaries aren't signed yet. The contract between the
app and CI lives in
.memories/reference/AUTO-UPDATE.md.
bash scripts/release.sh minor # or patch | major | 1.4.0; --dry-run to previewThe script checks that you're on a clean, up-to-date develop and that
CHANGELOG.md has something under [Unreleased]. It then bumps Cargo.toml,
turns [Unreleased] into the new version section, commits
chore(release): 🔖 Release vX.Y.Z, tags it and pushes both. Pushing the tag
starts .github/workflows/release.yml, which runs the checks and builds every
platform plus the Windows installer. It publishes the GitHub release that the
stable channel and install.sh read. Details:
.memories/reference/RELEASING.md.
Pooprusteek runs on a single tokio::select! event loop. The agent runs in a spawned task and communicates only through AppEvents and channels — it never touches UI state directly, so there are no render races. All CPU-heavy work (ONNX embeddings, SHA-3 proof-of-work) runs on blocking threads.
main ─ App (thin coordinator on a single select! loop)
├─ AppState
│ └─ Conversations ── focused + background chats
│ each owns: messages · forked provider/session · agent task
├─ AgentRuntime.spawn(TurnSpec) ── the one place a turn launches
│ └─ run_agent_loop ── LLM ↔ tools (events tagged by ConversationId)
│ ├─ semantic hint ── skills + MCP tools matched per prompt
│ └─ task tool ⇒ sub-agent (foreground) | detached (background)
├─ SemanticService ── local hybrid index (e5-small ONNX + TF-IDF + RRF)
│ └─ corpora: skills · MCP tools · message history (persistent)
├─ ToolRegistry ── bash · powershell · shell_* · skill · tool_search
│ history_search · mcp__*
├─ MCPManager ── external tool servers (stdio / http / sse, OAuth)
├─ providers ── DeepSeek web (PoW + SSE) · OpenAI-compat · Anthropic · Gemini
│ fork() ⇒ isolated session per conversation
└─ TUI render ── reads the focused conversation; never mutates
src/
├── main.rs — entry point, CLI flags (--acp, --debug-log, --version), file-based tracing
├── app/ — coordinator: conversations, event loop, runtime, keys, goal,
│ multichat, search screen, providers panel
├── provider/ — DeepSeek web API (auth, PoW, SSE, sessions) + OpenAI-compat,
│ Anthropic Messages, and Gemini protocol clients
├── agent/ — agent loop, sub-agent runner, tool-call parser
├── semantic/ — local RAG: embedder, hybrid index, corpora, history store, evals
├── tools/ — shell tools, PTY process registry, skill / tool_search / history_search
├── mcp/ — MCP clients, transports, config discovery, manager, OAuth
├── tui/ — ratatui UI: views, widgets, theme, markdown
├── commands/ — 35 slash commands (one file each)
├── skills/ — skill discovery + injection
├── acp/ — Agent Client Protocol server mode
└── config/ — configuration schema + storage paths
Want the deep dive? The repo ships a curated knowledge base in
.memories/— start atINDEX.md.
cargo build # debug
cargo build --release # optimized (LTO, single codegen unit, stripped)cargo test --bin pooprusteek # 326 unit tests, no network
cargo test semantic::eval -- --ignored --nocapture # retrieval-quality evals (MRR);
# downloads the embedding model onceA pre-commit hook (.githooks/pre-commit) blocks commits that fail cargo fmt -- --check, cargo clippy -D warnings, or cargo test. Not wired up
by default — enable it once per clone:
git config core.hooksPath .githooksSkip it for one commit (CI runs the same gates, so this only delays the failure, not avoids it):
git commit --no-verify- Poopseek — the original TypeScript project this is a rewrite of.
- ratatui, crossterm, tokio, fastembed-rs, and the broader Rust ecosystem.
MIT