User guide
Working with rufous
This guide comes in two parts. The first is a reference for the rufous commands and their flags. The second is about the part that matters more, which is how one sets up a project so that a 2B–12B model can do useful development work in it.
Getting started
Before rufous can do anything, it needs a model to talk to. We will be assuming Ollama throughout this guide, since that is what rufous starts by default. Let’s install rufous, pull a model and check that the two can find each other:
$ curl -fsSL https://rufous.chat/install.sh | sh $ ollama pull qwen3.5:4b $ rufous doctor
The install script downloads the release archive for your machine, verifies its SHA-256 checksum and puts rufous in ~/.local/bin. You can pin a release with RUFOUS_VERSION=v0.3.0, or install somewhere else with RUFOUS_INSTALL_DIR.
rufous doctor reads your configuration, checks that Ollama is reachable, lists your models and Skills, and probes every MCP server you have configured. If something is wrong, this is where you will find out. Once it is happy, we can give the model something to do:
$ rufous chat qwen3.5:4b ~/code/billing
rufous reads its settings from~/.ollama_agent, the directory Mingbird used before it. If you have run Mingbird, rufous will pick up yourconfig.json,mcp.json, Skills and sessions as they are.
The commands
rufous is a single binary with a handful of subcommands. Three of them put the model to work (chat, run and acp), one serves the web UI, and the rest are there to help you look at what rufous sees. rufous --help and rufous <command> --help print the same information as this section.
rufous chat
$ rufous chat <model> <workdir> [--session NAME] [--time-budget DURATION]
An interactive conversation at the terminal, working in <workdir>. Each message you send is a Turn, and the Turn ends when the model replies or calls finish. This is the same Turn policy the web UI uses.
| Flag | What it does |
|---|---|
--session NAME | Names the session, so that it is saved under ~/.ollama_agent/sessions and can be picked up again later. |
--time-budget | How long rufous may work, in minutes or with a unit: 40, 90m, 1.5h, or even half an hour. |
Ctrl-C ends the run after the current step, with the session saved. A running command’s process tree is terminated along with it. A second Ctrl-C quits at once.
rufous run
$ rufous run <model> <taskfile> <workdir> [flags]
Where chat is a conversation, run takes a task file and works on it unattended until it is done. This is the batch harness, and it is a little stricter than a chat. Before it accepts that a task is finished, it checks for the things the task asked for (the files, the passing tests, the items on the plan). Since nobody is at the keyboard, anything the Safety Gate would normally ask you about is refused.
| Flag | What it does |
|---|---|
--session NAME | The session name under ~/.ollama_agent/sessions. |
--new | Starts fresh. By default a run resumes the checkpoint it finds in the working directory, and this discards it, along with the time already spent. |
--append | Continues the conversation, adding this task to the saved session rather than starting a new one. |
--time-budget | As for chat. |
--policy | unattended (the default) is the batch harness described above. general and coding run the task file as a single web-chat or editor Turn, which is mostly useful for benchmarking. |
Do note that a run resumes by default. If you edit a task file and run it again in the same folder, rufous will carry on from where the last run stopped. Pass --new when that is not what you want.
rufous serve
$ rufous serve [--port 8765] [--workdir PATH]
Serves the local web UI on 127.0.0.1, on port 8765 unless you say otherwise. The UI shows your sessions, every Tool Call with its step time, permission cards inline in the conversation, the live plan and the files the model touched. --workdir is the folder the agent works in, and it defaults to ~/agent_tasks/work.
rufous acp
$ rufous acp --model <model> --ctx <tokens>
This is how rufous runs inside your editor. It speaks the Agent Client Protocol over stdio, so the editor starts rufous as its agent and shows you streamed replies, each Tool Call with a diff, the plan, and the Safety Gate as the editor’s own permission dialog.
| Flag | What it does |
|---|---|
--model | A model tag, or a display name from config.json. Without it rufous falls back to RUFOUS_MODEL. |
--ctx | The context window in tokens, for every editor session. |
Always pass--ctxunder an editor. An editor starts rufous with no environment, soAGENT_CTXis never seen and the window falls back to 131,072 tokens. Ollama then sizes the model’s cache for that window, and a qwen3.5:9b ends up taking 17 GB. The editor Turns we have measured peak at 6,000 to 13,000 tokens, so--ctx 32768is plenty.
In Zed, this goes under agent_servers in settings.json:
{
"agent_servers": {
"rufous": {
"type": "custom",
"command": "/Users/you/.local/bin/rufous",
"args": ["acp", "--model", "qwen3.5:4b", "--ctx", "32768"],
"env": {}
}
}
}
Then start a new external agent thread from the Agent Panel and pick rufous. rufous keeps its own files (session, backups, trash, the todo plan) under ~/.ollama_agent/sessions rather than in your project, so your git status stays clean. Other ACP clients should work too, but at the time of writing Zed is the one we test against. The editor notes in the repository go into more detail.
rufous mcp
MCP servers give the model extra tools. The rufous mcp commands are shaped after claude mcp, so if you have used that, there is nothing new here.
$ rufous mcp add framit -- node /path/to/framit/dist/cli.js serve $ rufous mcp add -s project -t http -H "Authorization: Bearer …" remote https://host/mcp $ rufous mcp list $ rufous mcp get framit $ rufous mcp remove framit
Flag on add | What it does |
|---|---|
-s, --scope | user (the default) writes to ~/.ollama_agent/mcp.json and applies to every session. project writes to ./.mcp.json. |
-t, --transport | stdio (the default) spawns a command. http talks streamable HTTP to a URL. |
-e, --env KEY=VALUE | Environment for a stdio server. Repeat it for more than one. |
-H, --header "Name: value" | A header for an HTTP server. Also repeatable. |
--force | Replaces a server of the same name. |
Everything after -- is the server’s command, flags included. remove takes --scope as well, which it needs only when both files have a server of that name.
Looking around
The remaining commands don’t run the model at all. They let you see what rufous sees, which turns out to be rather useful when a Turn has gone sideways.
| Command | What it does |
|---|---|
rufous doctor | Checks the configuration, Ollama, your models, Skills and MCP servers. |
rufous models | Lists the models installed in Ollama. |
rufous skills [workdir] | Lists the Skills visible from a folder. |
rufous tools [--enable NAME] | Prints the tool schemas the model receives. --enable web_fetch --enable mcp_call adds advanced tools to the listing. |
rufous tool <name> '<json>' [--workdir DIR] | Runs a single Tool Call by hand, e.g. rufous tool web_search '{"query":"rust"}'. MCP tools are named server.tool. |
rufous plan | Shows how many sandboxed sub-agents this machine could run in parallel. Nothing is started. |
rufous voice <wav> [--lang en] [--model TAG] | Transcribes a WAV file. This only works in a build with the voice feature, which the alpha release does not include. |
rufous decide [--file F] [--model TAG] [--timeout-ms 5000] | Classifies messages as a question, a task or a follow-up. This one is for working on rufous itself. |
Files and settings
rufous keeps everything of its own under ~/.ollama_agent (or RUFOUS_HOME). The files you are most likely to touch are these:
config.jsonholds the engine (Ollama, an OpenAI-compatible server such as LM Studio or llama-server, or a cloud provider), the per-model profiles, Offline mode and the scope gate.mcp.jsonholds your MCP servers, as written byrufous mcp add.skills/holds your own Skills, for every project.hooks.jsonholds commands rufous runs at events in the loop, in the same shape Claude Code, Codex and Cursor use.sessions/holds saved sessions, and a.logper session with every Tool Call in it.
A project can bring its own as well. rufous reads these from the working directory:
AGENTS.mdandSYSTEM.mdare added to the system prompt for every Turn..agents/skills/,.claude/skills/andskills/hold the project’s Skills..mcp.jsonadds the project’s MCP servers to yours..rufous/hooks.json, and thehookskey of.claude/settings.json, add the project’s hooks.
Please note that a project’s.mcp.jsonand hooks run commands the project chose. Read them before running rufous in a repository you didn’t write.AGENT_PROJECT_MCP=0andAGENT_HOOKS=0switch them off.
A few environment variables are worth knowing about. Most of them come from Mingbird and behave the same way.
| Variable | What it does |
|---|---|
OLLAMA_HOST | Where to find Ollama. |
AGENT_CTX | The context window for chat and run. |
AGENT_TEMP, AGENT_THINK, AGENT_NUMPREDICT | Sampling temperature, thinking on or off, and the cap on tokens per reply. |
AGENT_OFFLINE | Offline mode, which switches off the cloud provider, the web tools and HTTP MCP servers. |
AGENT_SLIM_OUTPUT | Trims long command output. It is on by default at 32K and below. |
AGENT_SYSTEM_FILE | A file to use as the system prompt. |
AGENT_SESSION_LOG=0 | Stops writing the per-session log. |
RUFOUS_HOME | Uses another directory in place of ~/.ollama_agent. |
The switches accept 1/0, on/off, yes/no or true/false. A value rufous cannot read is logged and ignored, rather than stopping the agent.
Do note that Offline mode does not stop a command run through run_bash from reaching the network. It switches off rufous’ own network tools, not your shell’s.
Development work with small models
Everything up to now has been about rufous. This part is mostly about your project, and it is the part that will make the larger difference. Most of it would hold for any harness running a small model.
The idea behind it
A small model is good at doing one named thing with the facts in front of it. It is bad at deciding what the thing should be, and bad at guessing the facts. Give a 4B model “add a --verbose flag to src/cli.rs and print the config path” and it will usually get there. Give it “make the CLI nicer” and it will wander.
So the work of preparing a project comes down to one thing: we move the deciding and the guessing out of the model, and into files the model can read. In practice that means four things.
- Project knowledge goes into context. An
AGENTS.mdstates the layout and conventions, so the model doesn’t have to guess them. - Procedures arrive as Skills. The things the model never learned, such as how your tracker works or how your tests are run, are written down once and loaded when they are needed.
- Open-ended writing becomes filling in a template. Small models complete a structure far better than they compose one from nothing. A spec framework gives them that structure.
- Decomposition happens before the model starts. A task already split into small, named items turns every Turn into “do the named thing”.
There is a fifth point that ties these together. Whatever lives only in the conversation is lost at the next compaction or the next session. A spec, a task list and a tracker survive both.
It should be noted how much of this is measured. The guide in the repository, Effective Agentic Coding with Smaller Models, tags every claim as measured, observed or a hypothesis. At the time of writing, the case for preparing a project rests on a handful of live sessions, the best of them a qwen3.5:9b that produced a usable first spec from one prompt once Skills, OpenSpec and beads were in place. The bench run that would measure each piece separately has not been done yet.
Context is the budget
Before we start adding files, there is a catch we need to be honest about. Every one of them is paid for out of the same context window. rufous starts with a Prefill of about 1,250 tokens precisely so that a small model has room to work. A 2,000-token spec, a 600-token Skill and a 1,000-token file read already take up more than a tenth of a 32K window before the model has done anything. A model that was told the layout, but can no longer see the file it is editing, is worse off than one that was told nothing.
The short of it, is that more preparation is not always better. A few habits help:
- Set the window on purpose with
--ctxorAGENT_CTX, and check the model’s memory footprint once. A window the machine cannot hold is a slower model, not a bigger one. - Keep
AGENTS.mdshort. rufous cuts it at 6,000 characters in any case. - Give Skills
triggers:, so that they are only loaded on the Turns that need them. - Measure a
SessionStarthook before you keep it.bd primeprints about 3,500 characters, which is roughly a thousand tokens on a 1,250-token Prefill.
An AGENTS.md
This is the cheapest thing on the list, and the one we would do first. An AGENTS.md in the root of the project is added to the system prompt for every Turn. Other harnesses read the same file, so you may already have one.
It should state facts the model can act on: the layout, the build and test commands, the naming conventions, and what not to touch. Leave out advice the model has no way of acting on. Here is the kind of thing we mean:
# billing Rust workspace. Library code in crates/core/src, the CLI in crates/cli/src. Build: cargo build. Test: cargo test -p core. Run one test: cargo test -p core <name>. Config keys are snake_case and live in crates/core/src/config.rs. Specs are in openspec/. Tasks are tracked with beads (bd). See the beads Skill. Do not edit anything under migrations/ or vendor/.
Every word in that file is an instruction, as far as a small model is concerned. It turns out that a 4B model acts on a passing remark as readily as on a rule, so if something is in AGENTS.md, expect the model to do something about it.
A SYSTEM.md works the same way, and is added before AGENTS.md. Keep it for what is rufous’ alone, such as a persona or house rules. If its first line is <!-- rufous: replace -->, it replaces the built-in prompt entirely, which you should only do if you are prepared to take over everything that prompt does.
Project Skills
A Skill is an operating manual for the model, written as a Markdown file. Normally the model only sees each Skill’s name and a one-line description, and loads the full text when it decides it needs it. That costs almost nothing until it is used.
For a small model, the useful Skills are the project-specific ones. They describe how this project does things: how a change is proposed, how a task is claimed and closed, how the tests are run and what a passing run looks like. A Skill is either a flat file, <name>.md, or a folder with a SKILL.md in it. rufous looks for them in this order, and the first one with a given name wins:
<workdir>/skills/<workdir>/.agents/skills/and<workdir>/.claude/skills/~/.ollama_agent/skills/~/.agents/skills/and~/.claude/skills/- the Skills built into rufous
Since rufous reads .agents/skills and .claude/skills, a Skill written for Claude Code or Codex works here as it is. There is one rufous addition worth knowing about. A small model often won’t think to load a Skill by itself, so a triggers: line in the frontmatter places the Skill’s full text in the prompt whenever one of its words appears in your instruction:
--- name: beads description: How this project tracks work with bd (beads) triggers: bead, beads, bd, issue, ready, claim, close --- Work items are beads. Use run_bash for every bd command. - See what is ready: bd ready - Read one item: bd show <id> - Claim it: bd update <id> --claim - Close it when done: bd close <id> - File a new one: bd create "<title>" Never invent a bd command. If a command fails, run bd --help and read it. Do not start implementing when you were asked to file or close beads.
At most three Skills are placed in the prompt like this per task, and the log shows which ones with a [skills in effect: …] line. autoload: true does the same for every task, which is rarely what we want on a small model.
The example above is not hypothetical. In one of our editor sessions, qwen3.5:9b was told “I think we already did that one. It can be closed.” It didn’t know thebdverb, triedbd set done,bd sqlandbd dolt commit, concluded the item was not done, and then implemented the feature instead. A Skill that names the five commands is a few hundred tokens. That is a far cheaper fix than any rule we could put in the harness.
A task tracker
A small model is poor at choosing what to do next from an open brief. A tracker takes that choice away from it. Each item names its file or files and what “done” means, and each Turn becomes “do this item”. We use beads (bd), but a plain tasks.md works on the same principle.
Three things make a tracker work well with rufous:
- Items small enough for one Turn. If an item needs the model to plan, it is too big. Split it.
- A Skill that teaches the commands, as above. Do not count on the model knowing your tracker.
- Separate Turns for bookkeeping and for code. Ask for “break the spec into beads” in one Turn, and “work on the next ready bead” in another.
That last point is something rufous helps with. A small model asked to “break the specs into beads” will quite happily run bd ready, claim the first item, and start writing code. rufous’ scope gate classifies each instruction as tracker, docs or code work. When a tracker or docs Turn is about to write a source file or run a build, it asks you first. Allow it and the Turn carries on. Deny it and the model is told to report what is done and stop. If you would rather not be asked, "scope_gate": "shadow" in config.json only logs it, and "off" switches it off.
If your project already has a Claude Code hook that runsbd primeat session start, rufous will run it too, from.claude/settings.json. Do measure what that costs on your window (see above) before you leave it on.
Spec-driven development
The third piece is a spec. Writing a design from nothing is exactly the kind of open-ended task a small model does badly. Filling in a fixed structure is something it does rather well. A spec framework such as OpenSpec provides that structure. Each change gets a folder under openspec/changes/ with a proposal, the affected specs, and a tasks.md.
$ openspec init $ bd init
With the structure in place, we can ask the model to write the proposal for a change and fill in the spec. Then, and this part matters, review the spec yourself before any code is written. A small model will implement a wrong spec as faithfully as a right one, and it is far cheaper to fix a paragraph than a module.
Do note that rufous checks file names against your instruction. If you say “tick the first task in tasks.md” and the model writesopenspec/tasks.mdwhile a./tasks.mdexists, the reply is held with both paths in the note. Name the file with its folder when there is more than one of them.
Putting it together
Let’s put this together into the flow we use day to day. Each step is its own Turn, and often its own session.
- Prepare the project once. Write the
AGENTS.md, runopenspec initandbd init, and add the project Skills for your spec framework, your tracker and your tests. - Write the spec. “Propose a change for <feature> in openspec.” Read what comes back, and fix it.
- Split it into beads. “Break the tasks in openspec/changes/<name>/tasks.md into beads.” Each bead should name its file and its finish condition. The scope gate will stop the model from wandering into code.
- Work one bead per Turn. “Work on the next ready bead.” The model runs
bd ready, claims it, does the work, runs the tests and closes it. - Start a new session when the window fills up. Nothing is lost, since the state is in the spec and the tracker rather than in the conversation.
This is more ceremony than one would put up with for a frontier model. With a 4B model on a laptop, it is the difference between work that lands and work that doesn’t.