coder-leo
A coding agent for the terminal that runs on local models. It reads, writes and edits code, runs commands, searches a project and keeps a task list, driven by a model that runs on your own machine through Ollama or any server with the OpenAI chat API (LM Studio, llama-server, vLLM, mlx). It works the way Claude Code does, and it is built for small context windows: it measures the window, keeps every tool result in budget, clears old results, summarizes the conversation in parts when it must, and gives the model tools that find things without reading everything.
Install
macOS, with Homebrew, two commands, once:
brew tap leonardoaguiar/coder-leo https://leonardoaguiar.com/apps/coder-leo/tap.git
brew install leonardoaguiar/coder-leo/coder-leo
brew upgrade brings new versions, and so does coder-leo update.
Linux (any distribution, x86_64 or ARM), Android inside Termux, and Windows inside WSL:
curl -fsSL https://leonardoaguiar.com/apps/coder-leo/install.sh | sh
It picks the build for the machine, checks its published SHA-256 and installs the binary, the man page and the shell completions in /usr/local (asking for sudo when needed), ~/.local or Termux's own folder. Running it again updates; sh -s -- --uninstall removes it.
You also need a model server with a model that supports tools:
- Ollama (default):
ollama pull qwen2.5-coder:14borqwen3,devstral,gpt-oss,llama3.1,mistral. coder-leo talks to127.0.0.1:11434, or toOLLAMA_HOST. - LM Studio, llama-server, vLLM, mlx-lm or any OpenAI-compatible server:
coder-leo --api openai --host http://127.0.0.1:1234.
Start
Open a terminal in a project and run coder-leo. The first time, pick a model from the list; it is remembered. Type what you want; /help lists the commands.
coder-leo a session in this directory
coder-leo "add tests for parser" a session with a first prompt
coder-leo -p "explain main.go" one answer, then exit (also reads stdin)
coder-leo -c continue the latest session here
coder-leo --sessions pick an earlier session
coder-leo --list the installed models
The welcome box shows the model, the context window, the permission mode and the directory. The line under the prompt shows the mode, the model, how full the context is and the git branch.
Working with it
- Ask for changes, not advice. It edits the files and runs the build or the tests itself; it never pastes code for you to apply.
- @path attaches a file or a directory to the message (Tab completes, fuzzy). A file too big for the window goes in as its outline, and the model reads the parts it needs.
- !command runs a shell command and keeps the output in the conversation.
- #text saves a note to the project's
CLAUDE.md. - Esc or Ctrl+C stops the reply. What you type meanwhile waits at the next prompt.
- Shift+Tab cycles the permission mode; Ctrl+O toggles full tool output; Ctrl+T shows the task list; Esc Esc rewinds; Ctrl+R searches the history; Ctrl+J inserts a new line; a big paste is folded into a placeholder.
- The words think, think hard and ultrathink in a prompt raise the reasoning of a thinking model for that turn.
Tools
The model works through these tools, named as in Claude Code (it also understands the names other agents use, like read_file or run_command):
| Tool | What it does |
|---|---|
| Read | A file, numbered, with offset and limit; images for vision models; PDFs through pdftotext; notebooks as cells. A big file read whole returns its outline and the start |
| Write, Edit | Create a file; replace an exact snippet (tolerant of copied line numbers and of indentation differences) |
| LS, Glob, Grep | List, find files by pattern, search contents with a regular expression (content, file names or counts, with context) |
| Search | The files and lines most relevant to a plain-language question, ranked by the words of identifiers |
| Outline, RepoMap | The functions and types of a file; the map of the repository, ranked by how much the code refers to each file |
| Bash, BashOutput, KillShell | Run a command with a timeout; long ones in the background |
| WebFetch, WebSearch | A page as readable text; a web search |
| TodoWrite | The task list, shown as it changes |
| Task | A sub-agent with a fresh context window (explore, plan, general, review, or your own) that returns only its report |
| Recall | Search the whole conversation, including what was summarized away |
| AskUserQuestion, ExitPlanMode, Skill | Ask you something; present a plan; load a skill |
| mcp__server__tool | The tools of your MCP servers |
The context window
Local models have small windows (8k to 32k tokens is common on a 16 GB machine), and re-reading a long prompt costs seconds per turn. coder-leo keeps the window working:
- Size: chosen from the model's limit and the machine's memory (
/ctxshows it,/ctx 32kor/ctx autochanges it). The count is an estimate corrected by what the server reports. - Stable prefix: the system prompt does not change during a session, so the server reuses its cache instead of re-evaluating everything.
- Results in budget: every tool result is cut to what fits; the complete output is saved to a file whose path the model gets, to Read in parts or Grep.
- Clearing at 70%: old tool results become one-line stubs (the last ones stay whole).
- Summary at 85%, or
/compact [what to keep]: a structured handover summary replaces the conversation, keeping the latest turns verbatim; a conversation too long to summarize at once is summarized in parts and merged. The transcript stays on disk, and Recall searches it. - Repository map: the most referenced files and their symbols go into the system prompt when there is room, so the model knows where things are without reading.
- /context shows what is using the window: system prompt, tool schemas, messages, results, cleared results, free space.
Permissions
Four modes: default asks before editing and running commands; acceptEdits lets edits inside the project through and asks for commands; plan only reads, and the model presents a plan for approval; yolo runs everything without asking, the way the first versions did, and it is the default unless your settings say otherwise. Destructive commands (removing wide directories, discarding git work, force pushes, sudo, dropping tables) still ask in yolo mode; "guard": false in the settings turns that off.
Rules in the settings decide before the mode: "allow": ["Bash(git *)", "Read"], "deny": ["Bash(rm *)"], "ask": ["Write"]. Answering a permission prompt with a allows that tool for the session; answering with words sends them to the model as instructions instead.
Sessions
Every session is saved as it happens in ~/.config/coder-leo/sessions/<project>/. coder-leo -c continues the latest one, --resume <id> or /resume picks one, /rename names it, /export writes it as Markdown. Before every edit the file is checkpointed: /rewind (or Esc Esc) goes back to an earlier turn, restoring the files changed since and cutting the conversation there.
Instructions, commands, skills, agents
- CLAUDE.md or AGENTS.md in the project (and in the directories above it, and
~/.config/coder-leo/, and~/.claude/) go into the system prompt: conventions, build commands, what to avoid.@pathlines inside them import other files./initasks the model to write one./memorylists what is loaded. - Custom commands: Markdown files in
.coder-leo/commands/(or.claude/commands/, project or home), run as/name arguments.$ARGUMENTS,$1, `!commandand@fileare expanded; the front matter may setdescription,argument-hint,allowed-toolsandmodel`. - Skills:
.coder-leo/skills/<name>/SKILL.md(or.claude/skills/), listed to the model with their descriptions; it loads one with the Skill tool when the job matches, or you run/name. - Sub-agents:
.coder-leo/agents/<name>.md(or.claude/agents/) withname,description,toolsandmodelin the front matter and the system prompt as the body; the Task tool runs them. Built in: explore, plan, general, review. - Hooks: shell commands around events (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop, SubagentStop, PreCompact, SessionEnd), with the event as JSON on stdin; exit 2 blocks with the reason, and a JSON answer can allow, deny or add context. Same protocol as Claude Code.
- MCP: servers from
.mcp.json(project or home) ormcpServersin the settings, over stdio or HTTP; their tools appear asmcp__server__tooland their prompts as commands./mcplists them.
Settings
~/.config/coder-leo/settings.json, then .coder-leo/settings.json and .coder-leo/settings.local.json in the project, each overriding the one before; then the environment (CODER_MODEL, CODER_API, CODER_HOST or OLLAMA_HOST, CODER_API_KEY).
{
"model": "qwen2.5-coder:14b",
"api": "ollama",
"numCtx": 16384,
"think": "low",
"permissions": { "mode": "default", "allow": ["Bash(git *)"], "guard": true },
"compact": { "micro": 0.7, "full": 0.85, "keepRecent": 6 },
"repoMap": { "enabled": true, "budget": 3000 },
"tools": { "minimal": false, "disabled": ["WebSearch"] },
"hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "./check.sh" }] }] },
"mcpServers": { "fs": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] } },
"statusLine": { "command": "~/.config/coder-leo/status.sh" },
"notify": "bell"
}
/config shows what is in effect and where it came from; /doctor checks the server, the model, the memory and the setup.
Headless
coder-leo -p "prompt" answers once: the reply on stdout, everything else on stderr. --output-format json prints one object with the result, the session id and the token counts; --output-format stream-json prints one JSON event per line as things happen. --max-turns, --allowedTools, --disallowedTools, --system-prompt, --append-system-prompt and --mode shape the run; -c or --resume continue a session.
Privacy
Everything runs on your machine: the model, the files, the conversation. The program reaches the network only when the model calls WebFetch or WebSearch for your task (a permission prompt, outside yolo mode), when you set up an MCP server that reaches out, and when you run coder-leo update. No accounts, no telemetry, nothing sent anywhere.
Updates
coder-leo update checks the site for a newer version and installs it (through Homebrew for a Homebrew copy); coder-leo update --check only tells. The program never checks on its own.
Help and bug reports
Problems and ideas: the support address at the end of the coder-leo page. Include the model, the server, coder-leo version and the output of /doctor.