A pip-installable AI agent harness with a Textual TUI, persistent sessions, and MCP tool support.
Install • Quick start • Configuration • TUI controls • MCP servers • Skills • Troubleshooting
DoStuff is a terminal AI agent that runs anywhere pip runs. It gives you a full interactive session with persistent memory, multi-provider LLM support via litellm, and pluggable MCP tool servers. State lives in ~/.dostuff/, so any project you launch dostuff from shares the same memory and sessions.
Tip
If you have used claude-code or aider, this will feel familiar. DoStuff is opinionated, minimal, and easy to extend.
- Textual TUI with per-role colored messages, status bar, and a loading spinner
- Multi-provider LLMs via litellm — OpenAI, Anthropic, Gemini, Groq, Mistral, Ollama, OpenRouter
- Persistent sessions in SQLite; resume with
dostuff --session <id> - Long-term memory (semantic + episodic) backed by ChromaDB
- MCP server support — stdio (npx, uvx) and HTTP transports
- Skills loader — drop a
SKILL.mdin the right folder, restart, done - Per-tool timeout (120s) to prevent hung servers
- Token tracking — per-turn and cumulative totals
- Graceful exit —
Ctrl+Qor/exitsaves, cleans up, and stops background workers
Install from PyPI:
pip install dostuffThis installs the dostuff command on your PATH. Done.
For an isolated install with pipx:
pipx install dostuffFor development (editable mode, from a clone):
git clone https://github.com/<you>/dostuff.git
cd dostuff
pip install -e . # core
pip install -e ".[full]" # + onnxruntime + grpcioVerify:
dostuff --help# 1. Set up config and secrets
mkdir -p ~/.dostuff
cp config.example.yaml ~/.dostuff/config.yaml
cp .env.example ~/.dostuff/.env
nano ~/.dostuff/.env # add OPENAI_API_KEY=sk-...
# 2. Edit ~/.dostuff/config.yaml and set model.name
# (see config.example.yaml)
# 3. Launch the TUI
dostuffIn the TUI:
> hi there
[agent replies]
> /exit
[session saved, goodbye]
Note
No config? No .env? The TUI shows a clear message and lets you quit without crashing. See Troubleshooting.
| Command | Description |
|---|---|
dostuff |
Launch the interactive TUI |
dostuff --session <id> |
Resume a prior session |
dostuff --user <id> |
Override the user ID |
dostuff init |
Create .dostuff/ in the current directory |
dostuff config |
Show resolved config paths and values |
dostuff doctor |
Health check (config, data dir, user ID) |
dostuff session-list |
List all past sessions with their working dirs |
| Key / command | Action |
|---|---|
Enter |
Submit message |
Ctrl+J |
Insert a newline (multi-line input) |
/exit |
Save memories and quit |
Ctrl+Q |
Graceful quit with save |
/help |
Show available in-TUI commands |
Visual elements:
- User messages — blue background
- Agent messages — neutral
- Tool calls and results — teal background, truncated at 600 chars
- Confirmations — yellow background (respond with
yorn) - Errors — red background
- Status bar —
cwd • session-id • new/resumed • ↑total ↓total - Loading —
⏳ agent is thinking...while waiting for the model
Config is layered, with later sources overriding earlier ones:
~/.dostuff/config.yaml— global defaults<cwd>/.dostuff/config.yaml— per-project overrides~/.dostuff/.env/<cwd>/.dostuff/.env— secrets- Environment variables — highest priority
# ~/.dostuff/config.yaml
data:
global_dir: ~/.dostuff/data
mcp:
config_path: ~/.dostuff/mcp_config.json
tracing:
enabled: false # default OFF (no 4317 noise)
exporter: "otlp" # otlp | console | none
model:
name: "openai/gpt-4o-mini" # litellm format: provider/model
api_key_env: "OPENAI_API_KEY" # name of the env var holding the keySee config.example.yaml for a full template.
Never put secrets in config.yaml. Use ~/.dostuff/.env:
# ~/.dostuff/.env (chmod 600 on Unix)
OPENAI_API_KEY=sk-...
GEMINI_API_KEY=AIza...
GROQ_API_KEY=gsk_...
TAVILY_API_KEY=tvly-...MODEL=gemini/gemini-3.1-flash-lite dostuff
OTEL_ENABLED=true OTEL_EXPORTER=console dostuffThe name field uses litellm format: provider/model-name.
| Provider | Model name | API key env |
|---|---|---|
| OpenAI | openai/gpt-4o-mini |
OPENAI_API_KEY |
| Anthropic | anthropic/claude-3-5-sonnet-20240620 |
ANTHROPIC_API_KEY |
| Gemini | gemini/gemini-3.1-flash-lite |
GEMINI_API_KEY |
| Groq | groq/llama-3.1-70b-versatile |
GROQ_API_KEY |
| Ollama | ollama/llama3.1 |
(none) |
If name has no /, the provider field is auto-prepended.
MCP servers extend the agent with new tools. Configure them in ~/.dostuff/mcp_config.json.
Discovery order: mcp.config_path → ~/.dostuff/mcp_config.json → <cwd>/.dostuff/mcp_config.json → <cwd>/mcp_config.json.
Conflict resolution: No merge — the first found config wins. If ~/.dostuff/mcp_config.json exists, the project-level .dostuff/mcp_config.json is silently ignored. To use a custom path, set mcp.config_path in config.yaml — that takes highest priority.
{
"mcpServers": {
"tavily": {
"transport": "stdio",
"command": "npx",
"args": ["-y", "@tavily/mcp-server"],
"env": { "TAVILY_API_KEY": "tvly-..." }
},
"github": {
"transport": "stdio",
"command": "uvx",
"args": ["mcp-server-github"],
"env": { "GITHUB_TOKEN": "ghp_..." }
},
"remote": {
"transport": "http",
"url": "https://mcp.example.com/sse",
"headers": { "Authorization": "Bearer xxx" }
}
}
}| Field | Required for | Notes |
|---|---|---|
transport |
optional | stdio (default) or http |
command |
stdio | Executable (npx, uvx, ...) |
args |
stdio | Argument list |
env |
stdio | Env vars for the child process |
url |
http | Remote MCP server URL |
headers |
http | HTTP headers |
MCP connections start in a background worker, so the TUI never blanks during boot. Each server is announced with a ✓ or ✗ in the message area.
To force a re-registration, delete:
rm ~/.dostuff/data/mcp_client_registrations.jsonSkills are markdown documents that teach the agent new workflows. Each is a directory with a SKILL.md (frontmatter + body).
Discovered from (in order):
<cwd>/.dostuff/skills/<cwd>/.agents/skills/~/.dostuff/skills/~/.agents/skills/<cwd>/skills/
Conflict resolution: No deduplication. If two skills share the same name (from different directories), both are loaded and passed to the model — the agent may see the same skill twice with different location paths. To avoid confusion, keep names unique across the search paths.
Example structure:
~/.agents/skills/
└── my-skill/
├── SKILL.md # required
└── helpers/ # optional
SKILL.md:
---
name: my-skill
description: One-line description of what this skill does
---
# My Skill
Detailed instructions for the agent go here.Run dostuff init to bootstrap a local .dostuff/skills/ folder. Restart the agent after adding new skills.
- DB:
~/.dostuff/data/sessions.db(SQLite) - Resume:
dostuff --session <id> - List:
dostuff session-list - Memory stores:
~/.dostuff/data/chroma/- Semantic (key/value facts)
- Episodic (events with summaries and timestamps)
- Saved on:
/exitorCtrl+Q
Long-term memory survives pip install . --upgrade because it lives in the user's home directory, not in the package.
Tracing is OFF by default to avoid spurious localhost:4317 errors.
# ~/.dostuff/config.yaml
tracing:
enabled: true
exporter: "console" # or "otlp"
endpoint: "localhost:4317"Or via env:
OTEL_ENABLED=true OTEL_EXPORTER=console dostuffWarning
TUI says "No LLM configured"
Set model.name in ~/.dostuff/config.yaml and add the API key to ~/.dostuff/.env. See Quick start.
Warning
Connection closed (MCP tool)
The MCP server disconnected. Check the command and logs. Force a reconnect:
rm ~/.dostuff/data/mcp_client_registrations.json
dostuffWarning
MarkupError when a tool returns a long URL
Fixed in the current version. Update:
pip install --upgrade dostuffWarning
Tracing errors / localhost:4317 retrying
Tracing is OFF by default. If you enabled it and have no collector, set:
OTEL_ENABLED=false dostuffWarning
Session not resuming
Use dostuff session-list to find the session ID. Each session also stores the working directory it was started from.
Warning
pip install . fails
Requires Python 3.10+. Upgrade pip first:
pip install --upgrade pipdostuff/
├── cli_tui.py # Textual App + adapter
├── cli.py # typer CLI entrypoint
├── config.py # layered config (env > project > global > default)
├── agent/
│ ├── loop.py # agent loop, tool dispatch, token tracking
│ ├── call_agent.py # LLM call wrapper
│ └── run_tool.py # tool execution
├── lib/
│ ├── tracing.py # opt-in OpenTelemetry tracing
│ ├── model.py # model resolution
│ ├── memory/ # SQLite + ChromaDB stores
│ └── mcp/ # MCP client and registries
├── helpers/
│ ├── skills/ # skill discovery
│ ├── agent/ # identity, constants, exit handlers
│ ├── mcp/ # config loading
│ └── ui/ # emit shim
└── tools/ # tool definitions
├── bash/
├── files/
└── mcp/
- Author: varun
- Repository: https://github.com/kVarunkk/DoStuff
- Issues: https://github.com/kVarunkk/DoStuff/issues
