A personal Python CLI coding agent — an AI-powered assistant that reads, searches, edits files, runs shell commands, and interacts with git from your terminal.
python -m venv .venv
.venv/bin/pip install -e ".[dev]"Set your API key and optional base URL via environment variables (or a .env file):
export DEEPSEEK_API_KEY="your-key" # defaults to DeepSeek
export DEEPSEEK_BASE_URL="https://api.deepseek.com"
export TODDLER_DEFAULT_MODEL="deepseek-flash"OpenAI-compatible providers work too — use the OPENAI_* env prefix as a fallback.
Models: three slots — default, pro, flash — are selected with
--model, or switched mid-session with /model, and valued from
TODDLER_DEFAULT_MODEL, TODDLER_PRO_MODEL and TODDLER_FLASH_MODEL (all
three ship as deepseek-flash). A slot value may carry a [1m] suffix
(deepseek-v4-pro[1m]) naming a 1,000K-token context window; without one the
model is accounted for at 200K. The suffix is Toddler's own notation and is
stripped from the request.
Every slot must name a deepseek- family model. The reasoning echo-back is
DeepSeek-specific, so a foreign model is refused at startup rather than left to
fail mid-turn.
Thinking effort is selected with --reasoning-effort, or switched with
/effort (TODDLER_EFFORT_LEVEL, default high), and it sets the output
budget: 4K with thinking off, 64K at max or above, 32K otherwise.
A conversation remembers both, so resuming restores the model and effort it was running with.
Troubleshooting: thinking is paid for out of the same budget as the answer,
so a thinking-heavy turn can exhaust max_tokens, ending with
finish_reason="length" and empty output — raise the effort. The budget
follows it. reasoning_tokens in the recorded usage shows the reasoning/output
split.
tod # Interactive REPL with slash commands
tod "refactor auth.py" # One-shot: run a single task
tod --plan "add tests" # One-shot in plan mode (research → propose → execute)
tod --session <id> # Resume a previous session
tod --list-sessions # List saved sessions
tod serve # Browser-based web UI (see Web UI below)Sessions are bound to the directory tod starts in. With no --session,
the most recently used session that directory has (CLI or web) is resumed;
a fresh one is created only when there is none. --new-session forces a
fresh session, and --session <id> resumes a specific one whatever
directory it came from.
A session is held by one live process at a time: a claim file under
~/.toddler/sessions/ (an OS file lock) stops a second tod or
tod serve in the same project from appending to the same transcript —
it gets a fresh session instead. --session <id> naming a session
another process holds exits with an error rather than silently opening a
different one. The claim file is removed on exit and swept up on the
next start if a process was killed before it could release.
| Flag | Purpose |
|---|---|
--plan |
Force plan mode — agent researches before making changes |
--session <id> |
Resume a previous session by ID |
--new-session |
Start fresh instead of resuming this directory's last session |
--list-sessions |
List saved sessions and exit |
--model <name> |
Override the LLM model |
--base-url <url> |
Override the API base URL |
--api-key <key> |
Override the API key |
--max-iterations <n> |
Cap agent loop iterations |
--no-stream |
Disable streaming output |
--verbose, -v |
Enable debug logging |
tod serve runs the browser frontend (FastAPI + Vue) against the same
agent backend and SQLite session database as the CLI:
tod serve # serve at http://127.0.0.1:8000, open browser
tod serve --port 9000 # custom port
tod serve --no-open # don't auto-open the browserIt accepts the shared flags (--model, --base-url, --api-key,
--no-stream, --max-iterations, --new-session); --dev enables CORS
for the Vite dev server. Like the CLI, the server resumes the last session
used in the directory it was started from.
Slash commands work in the input bar too (/help, /mode, /clear,
/session, …) — they dispatch server-side and never reach the LLM.
/quit and /view are not available in the browser.
The model and the thinking effort are also switchable from the pill beside
the input box: it reads deepseek-flash high and opens onto the three
slots and an eight-stop effort slider — the same tiers /effort and
TODDLER_EFFORT_LEVEL take, in the same order. Note that the endpoint
collapses some of them (minimal/low are both low to DeepSeek,
medium/high/xhigh are all high, max/ultra are both max), so
moving between those pairs changes the label and not the request.
Development workflow:
cd website && npm run dev # Vite :5173, proxies /api and /ws to :8000
# keep `tod serve` running in another terminal while editing frontend code
cd website && npm run build # production bundle into website/dist
tod serve # serves the built bundle directlytoddler/
main.py CLI entry point, arg parsing, component wiring
agent/ Agent loop, state machine, event handling, stop conditions
cli/ Terminal app, commands, display, input handler, renderer
config/ Settings (env + CLI overrides), defaults
context/ Context window, compaction, conversation context,
persistent memory, project map, system prompt
llm/ LLM provider abstraction (OpenAI-compatible), types,
token counting
storage/ SQLite-backed session persistence, store, models
tools/ Tool registry, base protocol, executor, filesystem, git,
search, shell subprocess
checkpoint/ Snapshot/checkpoint management
- LLM-agnostic — OpenAI-compatible provider works with DeepSeek, OpenAI, and any compatible endpoint.
- Permission model — Tools declare a tier (
READ,WRITE,SHELL_SAFE,SHELL_DANGEROUS) and the scope of the files a call reaches (the workspace, outside it, or a restricted path); the agent pauses before destructive work or anything out of the workspace, and refuses restricted paths such as.envoutright. - Plan mode — State machine that detects complex tasks, researches first, proposes a plan, then executes after user approval.
- Context window — Automatic token counting with LLM-powered compaction to handle long sessions.
- Session persistence — SQLite-backed history at
~/.toddler/sessions.dbwith multi-conversation support. Sessions are bound to the directory they were started in and that project's last one resumes automatically — one live process per session, claimed with a file lock. - Streaming — Real-time token output via Rich with animated tool-call status.
- Web frontend — browser-based GUI with a split-pane layout: file
viewer/editor on the left, agent console with scrollback on the right.
Coexists with the CLI via a
tod servecommand and shares the same agent backend and session database.
Always use the project virtual environment:
.venv/bin/python
.venv/bin/pip
.venv/bin/pytest
.venv/bin/ruff check .