Umbra

Documentation for Umbra 0.8.0

Work with Umbra
in your terminal

How to install it, choose a model, give it a task, steer it while it works, and pick a session up later, in the terminal or in your editor. Every picture is the real program, and every answer under a command is what Umbra printed.

01

What Umbra is

Umbra is a coding agent for the terminal, written in Rust. You give it a task, it asks a model what to do, runs the commands the model asks for, shows the model what happened, and repeats until the model replies without a command.

That reply ends the turn, and your next message continues the same conversation. A greeting gets a one line answer, a question about the code gets read and answered, and a change gets made, checked and reported.

Umbra works with any model: Claude through the Anthropic API or your Claude Code login, OpenAI, OpenRouter, or a local model on llama.cpp, Ollama or vLLM. One flag, -m provider/model, switches between them.

It reads the instruction files, skills, slash commands and MCP servers you already set up for Claude Code, Codex, Gemini CLI, opencode, Cursor, Copilot, Windsurf and Cline, so it knows your project on the first run. You copy and convert nothing.

Its loop comes from mini-swe-agent, the agent from the team behind SWE-bench: the model has one tool, bash, the shell keeps its folder and exported variables from one command to the next, and the whole conversation is one list of messages. Umbra adds what an agent needs on a real machine: your setup, MCP, confirm mode, the fullscreen view, sessions, compaction, images, and the Agent Client Protocol for editors.

  • You see every command before it runs. Press Enter to run it, or type why not and the model gets your reason.
  • You can type while it works. The message reaches the model before its next step.
  • Every session is saved after every step and can be picked up later, in the terminal or in Zed, a JetBrains IDE or Unluminous.
  • It is one program with no runtime to install. The Windows x86_64 download is 4,068,811 bytes.
Umbra and Claude Haiku 5.5 find and fix a bug, then add a test for it. Typing is sped up.
02

Install Umbra

One line installs Umbra on macOS, Linux or Windows. Each install script downloads the build for your machine from the latest GitHub release, checks it against the release's SHA256SUMS, and puts it in your home folder, so neither needs administrator rights.

On macOS and Linux, install.sh puts umbra in ~/.local/bin. On Windows, install.ps1 puts umbra.exe in %LOCALAPPDATA%\Programs\umbra and adds that folder to your user PATH. UMBRA_VERSION=0.8.0 installs one version, and UMBRA_INSTALL_DIR another folder.

On Windows, Umbra runs commands with Git Bash, so install Git for Windows too. Umbra never uses C:\Windows\System32\bash.exe, which would run the command inside WSL. With no Git Bash it falls back to cmd /C.

The releases have six builds: macOS on Apple silicon and on Intel, Linux on x86_64 and arm64 as static programs, and Windows on x86_64 and arm64. The size and the SHA-256 of each are in the install section of the Umbra page.

Other ways to install

Any of these gives you the same program.

HowCommand
Homebrew, macOS and Linuxbrew install black-rainbow-labs/umbra/umbra
cargo, from sourcecargo install --git https://github.com/Black-Rainbow-Labs/umbra --locked
A downloadThe .tar.gz or .zip for your machine from the releases page
From a checkoutcargo install --path ., with Rust 1.85 or newer
An editorPick Umbra from the editor's ACP agent list. See Editors

Have another agent install it

Paste these instructions into Claude Code, Codex, opencode or any agent that can run a command. It installs Umbra for your system, checks it is on the PATH, and saves the model.

Install Umbra, the coding agent from https://github.com/Black-Rainbow-Labs/umbra. On macOS or Linux run curl -fsSL https://raw.githubusercontent.com/Black-Rainbow-Labs/umbra/main/install.sh | sh. On Windows run irm https://raw.githubusercontent.com/Black-Rainbow-Labs/umbra/main/install.ps1 | iex in PowerShell, and check that Git for Windows is installed. Then run umbra --version, and umbra setup --model anthropic/claude-haiku-5-5 --no-test (or another provider/model I name) to save the model. To add it to an editor that speaks ACP, the command is umbra with the argument acp.

Updates

In the fullscreen view Umbra asks GitHub for the newest release at most once a day. When there is a newer one, a prompt above the input line offers Install and restart, Remind me tomorrow and Skip this version, once the agent is idle.

Install and restart downloads the release for your machine, checks it against the release's SHA256SUMS, replaces the program, and starts the new version on the same session with --resume. A failure at any step leaves the old program and the session as they were. A Homebrew install runs brew upgrade instead.

umbra update --check says whether a newer release exists, umbra update installs it from any terminal, and /update does it inside a session. updates.check = false or UMBRA_UPDATE_CHECK=0 turns the daily check off.

From the command line

Install on macOS or Linux

Downloads the build for your machine, checks its SHA-256 and puts umbra in ~/.local/bin.

Command
curl -fsSL https://raw.githubusercontent.com/Black-Rainbow-Labs/umbra/main/install.sh | sh

Install on Windows

In PowerShell or Command Prompt. Installs into %LOCALAPPDATA%\Programs\umbra and adds it to your PATH.

Command
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/Black-Rainbow-Labs/umbra/main/install.ps1 | iex"

Check it is on your PATH

Prints the version of the umbra your shell finds first.

Command
umbra --version
Answerobserved
umbra 0.8.0

Check for a newer release

Asks GitHub for the newest release and compares it with this one.

Command
umbra update --check
Answerobserved
umbra 0.8.0 is the newest release
03

Choose a model

`umbra setup` asks which provider and model to use, saves the answer as `model` in `~/.umbra/config.toml`, and sends one short request to check that the model answers.

If you are signed in to Claude Code, Umbra uses that login for Claude and you need no key. Otherwise set ANTHROPIC_API_KEY, OPENAI_API_KEY or OPENROUTER_API_KEY, or paste the key when umbra setup asks for it. A local llama.cpp or Ollama server needs no key.

You can skip umbra setup and name the model on every run with -m provider/model, or in the UMBRA_MODEL environment variable. A bare claude- name, such as -m claude-sonnet-5-5, means the anthropic provider.

A model can also have a short name of your own, in a [models.<name>] table, and -m <name> uses it. Those names fill the model picker in the fullscreen view and in an editor. See Models and providers.

The built in providers

Any other server with the OpenAI chat API, such as vLLM or LM Studio, is one entry in the config.

ProviderExampleWhat it talks toCredential
anthropicanthropic/claude-haiku-5-5The Anthropic Messages APIANTHROPIC_API_KEY, or your Claude Code login
locallocal/qwenhttp://localhost:8080/v1, llama.cpp's llama-servernone
ollamaollama/llama3http://localhost:11434/v1none
openaiopenai/gpt-5OpenAIOPENAI_API_KEY
openrouteropenrouter/anthropic/claude-sonnet-5-5OpenRouterOPENROUTER_API_KEY

From the command line

Choose a model, with questions

Lists the providers, asks for a model name, and saves the choice. It asks for a key when the provider needs one and none is set.

Command
umbra setup

Save a model without questions

The form a script or another agent uses. --no-test skips the test request.

Command
umbra setup --model anthropic/claude-haiku-5-5 --no-test

List the models

The default model, which umbra setup saved, your named models, and the models Umbra knows a label and a price for.

Command
umbra models
Answerobserved
default  (none; run umbra setup)

listed models
  anthropic/claude-haiku-5-5
  local/qwen

known models (use as provider/model, for example anthropic/claude-haiku-5-5)
  Fable 5.2    anthropic  claude-fable-5-2
  Opus 5.5     anthropic  claude-opus-5-5
  Sonnet 5.5   anthropic  claude-sonnet-5-5
  Haiku 5.5    anthropic  claude-haiku-5-5
  GPT-5        openai     gpt-5
  GPT-4.1      openai     gpt-4.1
  GPT-4o       openai     gpt-4o
  o3           openai     o3
04

Your first task

Give Umbra a task in quotes. It opens its fullscreen view, sends the task to the model, and shows each command the model asks for on the input line, waiting for Enter.

At Run? [Enter], Enter runs the command in the session's shell and the model gets its output. Type a reason instead, and the command does not run and the model gets your reason. /y switches to yolo mode, so the rest run without asking.

Consecutive commands fold into one row, Ran 3 shell commands ยท 0:42, with a tick when every one of them succeeded. When the model replies without asking for a command, the turn ends and the input line waits for your next message, which continues the same conversation.

Press Enter on the empty input line, Ctrl+D, or type /quit, to end the session. Your shell comes back as it was, with the path of the session's journal printed under it.

The first command of the task, waiting for Enter on the input line.

The answer

The model's reply is drawn from its Markdown, with code colored by its language. Here Haiku found the bug, fixed it, ran the tests, and said what it did. It did not commit, because the CLAUDE.md in the demo home folder says not to.

The turn ends with a reply and no command. The input line waits for the next message.

From the command line

A task on Claude

With umbra setup done, -m can be left out.

Command
umbra -m anthropic/claude-haiku-5-5 "two tests fail. find out why and fix it"

The same task on a local model

llama.cpp's llama-server on port 8080, started with --jinja so it does tool calling.

Command
umbra -m local/qwen "two tests fail. find out why and fix it"

One question, one answer

Prints only the answer and exits, the way claude -p does. -y is needed because nobody is there to confirm a command.

Command
umbra -q -y --exit-immediately -t "what does cart.py do? one sentence"
05

What Umbra reads from your setup

Umbra looks in the places Claude Code, Codex, Gemini CLI, opencode, Cursor, Copilot, Windsurf and Cline keep their files, and uses what it finds: instruction files in the system prompt, skills listed for the model, slash commands for your tasks, and MCP servers for their tools.

Instruction files go into the system prompt from the most general to the most specific: your own files in your home folder, then the rule folders at the project root, then each folder from the project root down to the working folder. A project's file comes after yours and wins when they disagree. @path imports in Claude Code and Gemini files are followed.

Only each skill's name, description and path go into the prompt. When a task matches, the model reads the SKILL.md with cat and follows it, the way Codex does. A task that starts with /name runs that slash command or puts that skill's instructions into the task.

MCP servers start in the background when Umbra starts, and their tools are offered to the model next to bash as each server becomes ready. When two files define a server of the same name, the first one found is used whole.

--no-source <name> ignores one tool's files for a run, and [sources] <name> = false for every run. The names are claude, codex, gemini, opencode, cursor, copilot, windsurf, cline, agents, vscode, claude_desktop and umbra.

What Umbra finds in a project set up for other agents.

Where it looks

The full list, in order, is on the Where files are found page of the repository.

WhatExamples of where Umbra looksHow it is used
Instruction files~/.claude/CLAUDE.md, AGENTS.md, CLAUDE.md, CLAUDE.local.md, GEMINI.md, UMBRA.md, .cursor/rules/*.mdc, .github/copilot-instructions.md, .windsurfrules, .clinerulesAdded to the system prompt, most general first
Skills~/.claude/skills, installed Claude Code plugins, ~/.codex/skills, ~/.agents/skills, .claude/skills, .agents/skills, .opencode/skillsThe name, description and path go into the system prompt
Slash commands.claude/commands/*.md, ~/.codex/prompts/*.md, .gemini/commands/*.toml, opencode's command/*.mdA task that starts with /name is replaced by the command's prompt
MCP servers~/.claude.json, .mcp.json, Claude Desktop, ~/.codex/config.toml, ~/.gemini/settings.json, opencode.json, .cursor/mcp.json, .vscode/mcp.jsonStarted in the background, their tools offered next to bash

Rules for instruction files

A rule file tied to particular file paths is left out, because Umbra does not track which files the agent is editing. A Cursor rule with alwaysApply: true, a Windsurf rule with trigger: always_on, a Copilot file with applyTo: "**", and a rule with no frontmatter are always included.

  • Each file is loaded once, even when two paths lead to it, and two files with the same content are loaded once.
  • Empty files are skipped.
  • The total is capped at instructions.max_bytes, 256 KB. A file that would go over the cap is skipped with a warning.

From the command line

The instruction files, in prompt order

From the demo project: a CLAUDE.md in the home folder, a Cursor rule and an AGENTS.md in the project.

Command
umbra instructions
Answerobserved
4 instruction files, 548 bytes

claude     User      160 bytes  C:\Users\jason\home\.claude\CLAUDE.md
cursor     Project       94 bytes  C:\Users\jason\shop\.cursor\rules\money.mdc
agents     Project      187 bytes  C:\Users\jason\shop\AGENTS.md
gemini     Project      107 bytes  C:\Users\jason\shop\GEMINI.md

Every skill and where it came from

Three skills, from the Claude Code, Codex and shared ~/.agents folders.

Command
umbra skills
Answerobserved
3 skills

sql-style                    agents         User
    How SQL is written in my projects. Use when writing or changing a SQL query or migration.
    C:\Users\jason\home\.agents\skills\sql-style\SKILL.md
release-notes                claude         User
    Write release notes for the changes since the last tag. Use when asked for release notes or a changelog entry.
    C:\Users\jason\home\.claude\skills\release-notes\SKILL.md
security-review              codex          User
    Review a change for injection, secrets in code and unsafe file handling. Use when asked for a security review.
    C:\Users\jason\home\.codex\skills\security-review\SKILL.md

The slash commands

One command, from ~/.claude/commands.

Command
umbra commands
Answerobserved
1 commands

/review                      claude     C:\Users\jason\home\.claude\commands\review.md

The MCP servers

Each server, on or off, and the file it came from. --connect starts them and lists their tools.

Command
umbra mcp
Answerobserved
3 MCP servers (first one found wins for each name). `umbra mcp enable <name>` and `umbra mcp disable <name>` change which start.

chordical            on   claude          Project C:\Users\jason\shop\.mcp.json
    stdio: C:/Users/jason/AppData/Local/Programs/Chordical DAW/chordical-cli.exe mcp serve (0 env vars)
termoil              on   claude          Project C:\Users\jason\shop\.mcp.json
    stdio: termoil +mcp (0 env vars)
unluminous           on   claude          Project C:\Users\jason\shop\.mcp.json
    stdio: C:/Users/jason/AppData/Local/Programs/Unluminous/unluminous-cli.exe mcp serve (0 env vars)

The exact prompt a task would get

Prints the system prompt and the first message without running anything.

Command
umbra prompt "two tests fail. find out why and fix it"
06

How the loop works

Umbra builds a system prompt and your task, asks the model, runs each command the model asks for in one shell kept for the session, adds the output to the conversation, and asks again, until the model replies without a command.

The model has one tool, bash. It reads files with cat, edits them with sed or a heredoc, and runs tests the way you would. MCP tools are offered next to it.

The shell persists, so cd and export carry over to the next command. environment.persistent_shell = false starts every command fresh, and then the model writes cd src && make in one command. Stdin is closed, so a command that waits for input ends instead of waiting forever. After environment.timeout seconds, 120 by default, the command and everything it started are stopped. The model can pass a timeout argument to bash for one slow command, up to environment.max_timeout, 600 seconds by default.

A reply Umbra cannot use, such as text mode with two fenced blocks, is sent back with what was wrong. Three in a row end the run.

Step
One model call and the commands it asked for.
Turn
Everything from one message you send to the reply that ends it. A session is one or more turns.
Observation
What the model is shown after a command: the output and the return code.
Trajectory
The JSON file of one session, in mini-swe-agent's format, written with umbra export or -o. Its messages are exactly what the model was sent.

Two ways to ask for a command

Tool mode is the default. Text mode works with a model that has no tool calling, and a local model that does tool calling badly often does better in it.

Tool modeText mode, `--text`
How the model asksA native tool call: bash with a commandA fenced block marked umbra_bash
Commands in one replyOne or moreExactly one
MCP toolsTool calls named mcp__<server>__<tool>A fenced block marked umbra_mcp holding JSON

How a turn ends

A turn ends with one of these exit statuses. In a terminal, a turn that ends with Replied or Submitted waits for your next message, and --exit-immediately exits instead.

Exit statusWhy
RepliedThe model replied with no command
SubmittedA command printed COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT as its first line and exited with code 0
LimitsExceededThe step limit or the cost limit was reached
TimeExceededwall_time_limit_seconds passed
RepeatedFormatErrorThree replies in a row could not be used
ModelErrorA model call still failed after its retries
InterruptedYou pressed Ctrl+C and then ended the run

The mini-swe-agent style

finish = "submit", or --finish submit, keeps mini-swe-agent's behavior for benchmark runs. The task is framed as an issue to solve, every reply must run a command, and the run ends when a command prints COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT. A mini-swe-agent YAML config passed with -c sets it, and its prompts and limits work unchanged.

The prompt cache

Anthropic and llama.cpp keep the start of a request they have already read, so the next request with the same start costs less and is answered sooner. Umbra changes earlier messages as little as it can so the cache keeps matching: the system prompt is written once and kept on resume, each assistant message goes back exactly as the model returned it, reasoning included, and a compaction that would free little space does nothing.

07

The fullscreen view

In a terminal Umbra opens on the alternate screen, the way vim and Claude Code's fullscreen mode do. From the top: the transcript, a popup or a picker when one is open, the input line, and the status bar on the last row.

The session starts on a clean screen, scrolling up shows the conversation and never the shell's earlier output, and when Umbra exits your shell comes back as it was. The input line is there while the agent works, so a message can be typed at any time.

--no-fullscreen, fullscreen = false or UMBRA_FULLSCREEN=0 prints inline below the shell prompt instead. A pipe, --quiet and TERM=dumb always get the inline view.

A whole session in the fullscreen view.

Replies and command groups

Consecutive shell commands are one row: โ–ธ โœ” Ran 2 shell commands ยท 0:02. A spinner and Running show while a command runs, โœ– and 1 failed when a command exited with another code, and the time is the total of the commands. A click on the row, or Enter when it has focus, opens it to one row for each command with its time and exit code. Opening a command shows the whole command, colored as shell, and its output.

Tab and Shift+Tab move the focus through everything you can click: the command groups, the header rows and the status bar boxes. The focused part has a โ€บ marker.

A reply with text between two commands closes the group, so the order on the screen is the order things happened. Code in a reply's fenced blocks is colored by its language, in the 16 basic terminal colors, so the colors follow your terminal's theme. syntax_highlight = false turns that off.

A reply's Markdown tables are drawn with box characters and aligned columns. A reply's footnotes are drawn as [1], [2] in the order they are first used, with the notes at the end of the reply under a short rule. Math between $...$ or \(...\) is drawn inside the line with Unicode characters, such as xยฒ + yยฒ = rยฒ. Math between $$...$$ or \[...\] is drawn as a block, with fractions stacked over a line, roots under a line, limits above and below โˆ‘, and matrices in tall brackets.

A fenced block whose language is mermaid is drawn as a diagram with box characters: a graph or flowchart as boxes in layers joined by arrows, in any of the five directions, a sequenceDiagram as lifelines with labeled arrows, notes and loops, and a pie as one bar per slice. Any other diagram, or one wider than the reply, is shown as its source.

A command group opened to show each command and its exit code.

The status bar

The status bar is the last row. It shows how full the context window is, as a bar with a mark where compaction starts, as a percent and in tokens; the agent time, which counts only while the agent works; the cost; the model; and for a Claude login the share of the weekly limit used. When an MCP server failed to start, a red โ–ฒ 1 MCP error box sits at the right edge, and a click on it lists the error and the server's log file.

The context bar is green while the request is under 60% of the compaction point, yellow under 85%, and red past that. On a narrow terminal the weekly usage shrinks to its percent first, then the bar gets shorter, then the model and the cost are left out. --no-status-bar or status_bar = false turns the bar off.

The input line and the status bar under it.

Scrolling, selecting and copying

While the view is scrolled up, new output does not move it, and a line under the transcript says how many rows are below. Umbra writes only the rows of the screen that changed, and all the wheel turns that arrive together become one frame, so scrolling keeps up in a terminal that draws slowly, such as one with shaders on. A drag selects text, a double click selects a word and a triple click a row, and the selection is copied to the clipboard when the button is released.

The terminal's own selection still works with its modifier key held: Shift in Windows Terminal and most Linux terminals, Option in iTerm2, Fn in Terminal.app. mouse = false or --no-mouse turns mouse reporting off. In a classic Windows console window the mouse is off by default and quick edit stays on.

Shortcuts

PageUp,PageDown
Scroll the transcript a screen at a time.
Ctrl+Home,Ctrl+End
Jump to the top, or back to the newest output. Esc also goes to the newest output.
Tab,Shift+Tab
Move the focus through the command groups, the header rows and the status bar boxes.
EnterorSpace
On a focused part, do what a click does.
Up,Down
Earlier prompts, from the first or last row of the input line.
Ctrl+A,Ctrl+E
The start or the end of the input line.
Ctrl+K,Ctrl+W,Ctrl+U
Delete to the end, the last word, or to the start.
Alt+Left,Alt+Right
Move by a word.
Enter
Send the line. A paste keeps its line breaks.
Ctrl+D
On an empty input line, end the session.
Ctrl+C
Stop what is running and ask for a comment.
08

Commands, pickers and panels

Typing `/` at the start of the input line opens a list of commands above it: Umbra's own, your slash commands and your skills. They work on the first prompt, on a follow up, and while the agent works.

The list holds up to 8 rows, each with the name, its kind (command, custom or skill) and a description. Matching is by the start of the name first, then by any part of it. Up and Down move, Tab completes the name, Enter on a complete name runs it, and Esc closes the list and keeps the text.

A command that only changes the screen, such as /config, /mcp, /model or /help, runs at once while the agent works. A command that needs the agent to be idle is refused with a one line notice. A built in name wins when a slash command or a skill has the same name.

The command list, the pickers, the MCP panel and the configuration modal.
CommandWhat it does
/configOpens the configuration modal
/model [name]Opens the model picker, or switches to the named model
/mcpOpens the MCP panel
/mode [confirm, yolo, human]Switches the mode, or opens a picker
/clearSaves this session and starts a new one with an empty history
/compact [focus]Summarizes the history now, keeping what focus names
/resumeOpens the session picker
/skillsOpens a picker of skills. Enter puts /<skill> in the input line
/updateInstalls the newest release and restarts into this session
/helpLists the commands and the keys
/quitEnds the session

The command list

The short commands inside the confirm, human and Ctrl+C prompts, /y, /u, /c, /q and /h, keep working there.

The list a slash opens above the input line.

The model picker

/model, or a click on the model row of the header, lists the models from [models.<name>] and models = [...] in the config, marks the current one, and ends with Other... for a provider/model typed by hand. Typing filters the list. A model whose credential is missing shows the error and the model does not change. A model chosen while the agent works applies from the next model call, for the rest of the session, and the config file is not changed.

The model picker.

The MCP panel

/mcp lists each server with a checkbox, its state and its number of tools, and for a failed server the first line of its error. s switches between this session and all sessions. Space ticks or clears the selected server, r starts a failed server again, and Enter on a failed server shows its whole error and its log file.

With this session selected, clearing a box stops the server for this session only, and a resumed session turns it off again. With all sessions selected, the change is written to ~/.umbra/config.toml and applies to this session too.

The MCP panel.

The configuration modal

/config opens every setting, in sections on the left and fields on the right. Up and Down move, Tab switches between the panes, Enter or Space edits a field, Ctrl+T switches between the user file and the project file, and Ctrl+S saves. Only the changed keys are written, and the comments and the order of keys in the file are kept. Esc closes it, and asks first when there are unsaved changes.

Each provider has a Test button that checks the credential, checks that the server answers, and asks for a "Say hello." completion. The MCP section shows a checkbox for each server found.

The configuration modal.
09

Confirm, yolo and human

In confirm mode, the default, Umbra shows each command and waits. In yolo mode commands run without asking. In human mode you type the commands and the model sees them.

-y starts in yolo mode and --mode human in human mode. mode = "yolo" in the config sets the mode every session starts in. The header's mode row always shows the current one.

Without a terminal on stdin, as in a script or a pipe, Umbra refuses to start in confirm mode and says to pass --yolo, so it never waits for an answer nobody can give.

At the confirm prompt

The prompt is Run? [Enter] yes, /y yolo, /u human mode, /h help, or type why not:. A reply that asks for several commands asks once for all of them.

You typeWhat happens
EnterThe commands run, and the model gets their output
A reason, then EnterThe commands do not run. The model is told you refused them, with your reason
/ySwitches to yolo mode and runs them
/uSwitches to human mode
/hShows the help for these short commands
Ctrl+CThe commands do not run, and Umbra asks for a comment

Commands that run without asking

whitelist_actions is a list of regular expressions matched at the start of a command, such as ["ls", "cat ", "git (status|diff|log)"]. A reply whose commands all match runs without asking in confirm mode. In an editor, Always allow does the same for one program for the rest of the session.

Switching during a session

/mode on the input line, /y, /u and /c at the confirm, human and Ctrl+C prompts, umbra session mode yolo from a command, and an editor's mode picker all switch the mode. In human mode, /c gives control back to the model in confirm mode and /y in yolo mode, and the model sees every command you ran.

Running every command in Docker

--docker <image> starts one container with docker run -d <image> sleep 2h, runs each command in it with docker exec, and removes the container at the end of the run.

Umbra does not mount your project into the container. The commands run in the image's own files, in / unless environment.cwd names another folder. To work on a checkout, add a mount to environment.run_args, such as ["--rm", "-v", "/home/me/shop:/work"], and set environment.cwd = "/work".

Limits

Each limit ends the turn when it is reached, and 0 means no limit. step_limit (--step-limit) counts model calls and cost_limit (-l) dollars, and both are 0 by default. wall_time_limit_seconds is the time for a run, and environment.timeout, 120 seconds, the time for one command, which the model can raise for one command up to environment.max_timeout, 600 seconds. In a terminal, Umbra asks whether to raise the step or cost limit when a run reaches it.

10

Ctrl+C and steering

Ctrl+C stops the command or the model call that is running and asks what to do next. What you type goes to the model as a comment, and the run carries on with everything it had.

A command is stopped with everything it started, and the model gets the output so far and how long it ran. A reply streaming from a local server is dropped, which closes the connection, so the server stops generating. An MCP call is cancelled, and the server keeps running. At the confirm prompt, the commands are not run.

Commands and MCP servers run in their own process group, so the key press reaches Umbra and not them. Without a terminal, Ctrl+C ends the run at once. If Umbra ever stops responding, a third Ctrl+C exits.

A slow command stopped with Ctrl+C, and the run steered with a comment.

The comment prompt

Interrupted. Type a comment/command (/h for available commands) waits on the input line. Text goes to the model as Interrupted by user: <your text>, and Enter alone carries on. /y, /c and /u switch the mode, and /compact compacts the history. /q, or a second Ctrl+C, ends the run with exit code 130, and the session is saved.

After Ctrl+C, Umbra waits for a comment.

Typing while the agent works

A line you send while Umbra works is queued. Before its next model call Umbra adds it to the conversation as The user sent this message while you were working: <your text>, and shows it under a message from the user heading. The agent sees it without the run having to stop.

To have it read at once, press Ctrl+C after sending it. The current command or model call stops, and the queued line becomes the comment.

A message sent while the agent works, read before its next step.

Sessions another program types into

--keep-open is for a session that a program such as a task board types into. An empty line or a Ctrl+C at an idle prompt asks again instead of ending the run, and a second Ctrl+C at the comment prompt carries on. Only /q, the end of input, or a third Ctrl+C ends it.

11

Sessions and resume

Umbra appends each step to the session's journal, so any run can be continued, including one that was interrupted or crashed. `umbra resume` opens a picker with a title and a one sentence summary for each session.

A resumed session continues in its own folder, with its own model unless -m says otherwise, and in the same journal. That holds with no model set in the config: umbra resume opens the picker, and the chosen session runs on the model it recorded. The model sees the whole history, images included, and the next task arrives the way a follow up does. The MCP servers turned off in the session are turned off again, and the step and cost limits count from the resume.

A session's title starts as the first line of its first prompt. After the first turn, Umbra makes one small request to the session's model for a title of at most six words and a one sentence summary. sessions.auto_title = false turns that request off.

A session started in Zed or Unluminous can be continued in the terminal with umbra --resume <id>, and the other way round.

A saved session picked from the list and continued with a new task.
CommandWhat it does
umbra sessionsThis folder's sessions, newest first. --all lists every folder's
umbra resumeThe session picker. Up and Down move, Enter resumes, typing filters
umbra resume <id>Resumes that session, by its id or the start of it
umbra --continue "<task>"The newest session in this folder, with a new task
--forkWith --resume or --continue: continue a copy and leave the original as it was
--at <n>With --resume and --fork: branch from an earlier point, keeping only the first n messages of the history
umbra export <id>Writes the session as a trajectory JSON file, next to its journal or to the file -o names
/resume, /clearThe picker inside a session, and a new empty session with the old one kept

The session picker

Each session takes two rows: its title with the age, the model and the number of model calls, and under it the summary.

umbra resume lists the saved sessions with a title and a summary.

Where sessions are kept

~/.umbra/trajectories/ holds each session as a journal, one line appended after every step, so a crash loses at most the step in progress. The trajectory JSON in mini-swe-agent's format is written only when you ask: umbra export <id> writes it at any time, and -o <file> writes it at the end of each turn. ~/.umbra/sessions/ holds a small index entry for each session, which the list reads without opening journals that may hold large images. While a terminal session runs it also keeps ~/.umbra/control/<id>/, which umbra session uses.

From the command line

List this folder's sessions

Newest first, with the id, the age, how each ended, the number of model calls, the model and the first task.

Command
umbra sessions
Answerobserved
0 sessions in C:\Users\jason\shop. Resume one with umbra --resume <id>

Continue the newest session

The model gets the whole earlier session and this new task.

Command
umbra --continue "now add a test"

Try something on a copy

The original session is left as it was.

Command
umbra --resume 17944981 --fork "try it a different way"

Write a session as a trajectory JSON file

The journal is the saved record. This writes the JSON next to it, or to the file -o names.

Command
umbra export 17944981 -o session.json

Branch from an earlier message

Keeps the first 6 messages of the history and continues from there in a new session.

Command
umbra --resume 17944981 --at 6 --fork "try it a different way"
12

Images

An image reaches the model with `-i`, as a path typed in a task or a comment, or as the result of an MCP tool such as a browser screenshot.

PNG, JPEG, GIF and WebP files are read. A path typed in a task, a follow up or a comment is attached with or without @, so you can drag a file into the terminal. An image over 1,568 pixels on its long edge is scaled down.

A local model needs a vision projector loaded to read images, such as llama.cpp's --mmproj. A server that cannot read images answers with an error, and the run ends with ModelError.

SettingDefaultWhat it does
images.max_long_edge1568Images whose long edge is bigger are scaled down to it. 0 never scales
images.max_bytes10485760An image whose base64 text is bigger is refused
images.detect_pathstrueAttach image files whose paths are typed in a task or a comment

From the command line

Attach a screenshot

-i is repeatable.

Command
umbra -m anthropic/claude-haiku-5-5 -i screenshot.png "why does this dialog cut off its last button?"

Name the images in the task

Both files are attached.

Command
umbra "compare @before.png with @after.png"
13

Models and providers

Umbra talks to two kinds of API: the Anthropic Messages API for Claude, and the OpenAI chat API, which OpenAI, OpenRouter, llama.cpp, Ollama, vLLM and LM Studio all accept.

A provider is a named way to reach a model. The five built in providers are anthropic, local, ollama, openai and openrouter. Any other server is one [providers.<name>] table in the config, with kind = "openai" and a base_url.

Cost is worked out from the token counts in each reply and a price table for the Claude models. A model with no price, such as a local one, costs nothing. input_cost_per_mtok and output_cost_per_mtok set a price for any other paid model.

A call that fails with HTTP 408, 409, 429 or 5xx, or never reaches the server, is tried again after 4, 8, 16 and 32 seconds and then every minute, up to 10 attempts. Authentication errors and bad requests fail at once.

Provider settingWhat it does
kindanthropic, openai or replay
base_urlThe API's base URL
api_key_env, api_keyThe variable that holds the key, or the key itself
max_tokens, temperature, effortThe reply length, the temperature (only sent when set) and the Anthropic effort
extra_body, headersFields added to every request body, and extra request headers
stream, send_reasoning, preserve_thinkingOpenAI compatible servers: streaming, and the reasoning sent back
input_cost_per_mtok, output_cost_per_mtokDollars per million tokens, for a model Umbra has no price for
max_retriesRetries after the first attempt, 9 by default

Claude

Umbra uses, in order, ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, and the login Claude Code saved, from ~/.claude/.credentials.json or the macOS Keychain. Umbra only reads that login and never refreshes it. When it has expired, run claude once. auth = "api_key" or auth = "claude_code" uses only one of them.

With a Claude login the status bar shows the share of the weekly limit used, read from Anthropic at most every 180 seconds. Every request asks the API to cache the conversation so far, and each reply goes back exactly as it came, thinking blocks included. effort sends output_config.effort.

Local models

Start llama.cpp's llama-server with --jinja so it does tool calling, then use -m local/<name>. Replies from an OpenAI compatible server are streamed, so Ctrl+C closes the connection and the server stops generating.

A local model such as Qwen streams its reasoning before its answer. Umbra sends that reasoning back with each assistant message, byte for byte, so the server renders the history exactly as it generated it and its prompt cache matches the whole conversation. For a server on this machine it also asks the chat template to keep the reasoning of earlier turns. send_reasoning and preserve_thinking turn each of these off.

From the command line

A server on another machine

Add a provider in ~/.umbra/config.toml, then use it as -m box/qwen. extra_body passes options the server reads.

Command
[providers.box]
kind = "openai"
base_url = "http://192.168.1.20:8080/v1"
max_tokens = 8192
extra_body = { chat_template_kwargs = { enable_thinking = false } }

A short name for a model

-m opus and /model opus then use it, and the pickers show the label.

Command
[models.opus]
provider = "anthropic"
model = "claude-opus-5-5"
label = "Opus 5.5"
14

MCP servers

Umbra is an MCP client. It starts the servers it finds in your other tools' files and its own, asks each for its tools, offers them to the model next to `bash` as `mcp__<server>__<tool>`, and runs a tool when the model calls it.

In a terminal the servers start in the background and Umbra does not wait: the MCP row of the header reads starting 2 of 5, and a server's tools are offered from the first model call after it is ready. A run with no terminal waits for every server before the first model call.

A server that does not start within 30 seconds is marked failed and the run goes on without it. A tool call that takes more than 120 seconds returns an error to the model. Images a tool returns reach the model as images.

When the tool definitions would take more than a tenth of the context window, the model gets only their names and a tool_search tool that loads the ones it needs. With 229 tools set up, that makes a "Hello" 31,905 input tokens instead of 214,611. mcp.defer_tools = "always" or "never" overrides the choice.

Adding a server for Umbra only

Servers you already use in Claude Code, Codex, Gemini CLI, opencode, Cursor, VS Code or Claude Desktop need nothing. A server for Umbra alone goes in ~/.umbra/config.toml, the project's .umbra/config.toml, or a .umbra/mcp.json in Claude Code's format. ${VAR}, ${VAR:-default}, ${env:VAR} and {env:VAR} are expanded in every value.

Turning a server on or off

The MCP panel does it for this session or for all sessions. umbra mcp disable <name> and umbra mcp enable <name> change the config file, and umbra session mcp off <name> changes a running session only. --no-mcp and --mcp a,b choose for one run.

Transports and protocol versions

An entry with a command is started over stdio, and its stderr goes to ~/.umbra/logs/mcp/<name>.log. An entry with a url uses Streamable HTTP, or the older HTTP and SSE transport for type = "sse". Umbra sends initialize first, and treats a server that refuses it as a 2026-07-28 server with no handshake. OAuth sign in, resources, prompts and sampling are not supported.

From the command line

A server over stdio

In ~/.umbra/config.toml, the Claude Code shape in TOML.

Command
[mcp_servers.files]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/me/projects"]

A server over HTTP

The token is read from your environment.

Command
[mcp_servers.tickets]
type = "http"
url = "https://mcp.example.com/mcp"
headers = { Authorization = "Bearer ${TICKETS_TOKEN}" }

Start the servers and list their tools

The demo project's .mcp.json names the Termoil MCP server.

Command
umbra mcp --connect
15

Editors and ACP

`umbra acp` runs Umbra as an Agent Client Protocol agent: the editor starts it and talks to it in JSON-RPC on stdin and stdout. The editor shows Umbra's messages, each command as a tool call with its output, and asks you before a command runs.

Use the full path to umbra if the editor does not see the same PATH as your shell, which is common for an editor started from the macOS Dock. -m, --yolo, --mode, -l and --step-limit go after acp.

With no model set, a new session asks the editor to sign in, and an editor that can run a terminal sign in runs umbra acp --setup. Umbra runs every command itself in the session's shell, and does not use the editor's file system methods or its terminals.

EditorHow to add Umbra
ZedIn settings.json: "agent_servers": { "Umbra": { "type": "custom", "command": "umbra", "args": ["acp"] } }
JetBrains IDEsIn ~/.jetbrains/acp.json: { "agent_servers": { "Umbra": { "command": "umbra", "args": ["acp"] } } }
UnluminousAn endpoint in Settings, AI Configuration, with wire acp, command umbra and the argument acp
Any ACP clientThe command umbra with the argument acp

What the editor gets

Every ACP session is an ordinary Umbra session, saved after every step, so a session started in the editor can be continued with umbra --resume <id> and the other way round.

ACP featureWhat Umbra does
Modesconfirm asks the editor before each command, yolo runs them. Always allow remembers the program for the rest of the session
Model pickerThe configured model and every model in models or [models.<name>]
Sessionssession/load, session/resume and session/list use the same sessions as umbra --resume
Stopsession/cancel stops the running command or model call, the way Ctrl+C does
Images and filesImage blocks go to the model, and attached files are added to the task
MCP serversThe editor's servers are added to the ones Umbra finds
Slash commandsYour slash commands and skills are offered as the editor's commands
16

Long sessions and compaction

Before each model call Umbra estimates the size of the request. Past 80% of the context window it compacts the history in two steps: old command outputs become short placeholders, and if that is not enough the model summarizes the middle of the session.

The mask pass replaces old command outputs with a one line placeholder that gives the size and return code and says to run the command again if it is needed. The newest outputs, short outputs and SKILL.md reads are kept. A pass that would free little space masks nothing, because changing an earlier message makes the prompt cache miss from that message on.

When masking leaves the request above 60% of the window, the model writes a summary under fixed headings. The new history is the system prompt, your tasks, the summary and the newest turns word for word. The summary call's cost is added to the session.

When a provider refuses a request as too long, Umbra summarizes and sends it once more. /compact, or /compact <what to keep>, summarizes by hand. Every compaction is recorded in the trajectory with the messages it removed.

The window comes from compaction.context_window, then the server (llama.cpp reports it at /props, and vLLM, llama.cpp and OpenRouter at /models), then a table, then 128,000. --no-compact turns compaction off for a run.

17

Configuration

Umbra works with no config file. Each setting is read from the built in defaults, then `~/.umbra/config.toml`, then the project's `.umbra/config.toml`, then each `-c` file, then the command line, and each overrides the one before it.

umbra config prints the merged result, umbra config list every setting with its value, the file it came from and what it does, and /config edits them in a modal. A -c file can also be a mini-swe-agent YAML config, whose agent, model and environment sections are mapped onto Umbra's settings.

The prompts are minijinja templates. [templates] replaces any of them, and an undefined variable is an error, so a typo shows at once. The full list of settings and template variables is on the Configuration page of the repository.

SettingDefaultWhat it does
modelnoneprovider/model, or a name from [models.<name>]
modeconfirmconfirm, yolo or human
action_formattoolstools or text
finishreplyreply, or submit for the mini-swe-agent style
step_limit, cost_limit0, 0Model calls and dollars. 0 means no limit
whitelist_actionsemptyCommands that run without asking
fullscreen, mouse, status_bar, bannertrueThe parts of the fullscreen view
syntax_highlighttrueColors in fenced code and opened commands
environment.timeout120Seconds before a command is stopped. The model can ask for more, up to environment.max_timeout, 600
environment.persistent_shelltrueEach command starts in the folder and with the variables the previous command ended with. Local bash only
sources.<name>trueRead that tool's files
mcp.only, mcp.disabledemptyWhich MCP servers start
mcp.defer_toolsautoauto, always or never
compaction.trigger_ratio0.8Compact past this share of the window
updates.checktrueThe daily update check
sessions.auto_titletrueThe request that titles a session
usage.enabledtrueThe weekly usage bar for a Claude login

From the command line

Read one setting

A ratio is printed as written in the file.

Command
umbra config get compaction.trigger_ratio
Answerobserved
0.8

The settings for running commands

The command timeout, the most the model may ask for, and whether the shell persists.

Command
umbra config list environment
Answerobserved
environment.kind                       local                    default  Where commands run: local or docker.
environment.cwd                        (empty)                  default  Empty means the folder Umbra started in (local) or / (docker).
environment.timeout                    120                      default  Seconds before a command is killed.
environment.shell                      (empty)                  default  The program and flags that run a command string. Empty means bash.
environment.env                        GIT_PAGER=cat, LESS=-R, MANPAGER=cat, PAGER=cat, PIP_PROGRESS_BAR=off, TQDM_DISABLE=1 default  Extra environment variables for every command, as name=value pairs.
environment.image                      (empty)                  default  The image for docker.
environment.docker_executable          docker                   default  The docker command to run.
environment.run_args                   --rm                     default  Arguments passed to docker run.
environment.forward_env                (empty)                  default  Host variables copied into the container when set.
environment.max_timeout                600                      default  The most seconds the model may give one command with the bash timeout argument.
environment.persistent_shell           true                     default  Local bash: the working directory and exported variables carry over from one command to the next.

Every compaction setting

With its value, the file it came from and what it does.

Command
umbra config list compaction
Answerobserved
compaction.enabled                     true                     default  Compact the history when it nears the context window.
compaction.context_window              0                        default  Tokens. 0 asks the provider, then a built in table.
compaction.trigger_ratio               80%                      default  Compact when the request passes this share of the window.
compaction.target_ratio                60%                      default  A mask pass that leaves more than this share is followed by a summary.
compaction.protect_recent_ratio        20%                      default  The newest outputs worth this share of the window are never masked.
compaction.protect_recent_max          40000                    default  Tokens. The protected outputs are never more than this.
compaction.min_reclaim_ratio           10%                      default  A mask pass must free this share of the window or it masks nothing.
compaction.min_reclaim_max             20000                    default  Tokens. The minimum to free is never more than this.
compaction.keep_recent_ratio           10%                      default  The newest turns worth this share of the window are kept word for word.
compaction.mask_min_chars              400                      default  Outputs shorter than this many characters are never masked.
compaction.max_context_tokens          0                        default  Tokens. Compact as if the window were at most this size, so a long session stays fast. 0 is no cap.

Change a setting

Written to ~/.umbra/config.toml with its comments kept. --project writes the project file.

Command
umbra config set mode yolo

Which file set and unset change

The demo home folder's config file.

Command
umbra config path
Answerobserved
C:\Users\jason\home\.umbra\config.toml
18

The command line

`umbra` runs a task, lists what it found, changes settings, controls a running session, and updates itself. `umbra --help` and `umbra <command> --help` describe every command and option.

A command the agent runs inside a session reaches its own session, so the agent can switch its own model or mode when you ask it to. Each command it runs has UMBRA_SESSION_ID, UMBRA_CONTROL_DIR and UMBRA_BIN in its environment. From another terminal, name the session with --session <id>.

The listing commands take --json. The full list of options and environment variables is on the Command line page of the repository.

CommandWhat it does
umbra [options] <task>Runs a task. umbra run is the same
umbra prompt <task>Prints the system prompt and first message, without running
umbra instructions, skills, commands, mcpLists what Umbra found
umbra config [list|get|set|unset|path]Reads and changes settings
umbra modelsLists the models
umbra session status|list|model|mode|mcp|compact|quitControls a running session
umbra sessions, umbra resumeLists and resumes saved sessions
umbra export <id> [-o file]Writes a saved session as a trajectory JSON file
umbra update [--check]Checks for a newer release and installs it
umbra acpRuns as an ACP agent for an editor
umbra setupChooses and saves a model

Exit codes

0 when the run ended with Replied or Submitted, 1 for any other exit status, 2 when Umbra could not start the command (a bad option, no model, confirm mode with no terminal), and 130 after Ctrl+C ended the run.

From the command line

The session this command runs in

Outside a session there is none, so this fails with exit code 2. Inside one it prints the id, model, mode and MCP servers.

Command
umbra session status
Answerobserved
umbra: no Umbra session is running

Switch a running session's model

Run by the agent, or from another terminal with --session <id>. It applies from the next model call.

Command
umbra session model anthropic/claude-sonnet-5-5

Stop an MCP server in this session only

The config file is not changed.

Command
umbra session mcp off termoil
19

What Umbra leaves out

Umbra keeps the loop as small as mini-swe-agent's: one `bash` tool, one list of messages, and the MCP tools you add. Some features other coding agents have are not in Umbra 0.8.0.

An MCP server can supply web search or a browser, and Umbra offers its tools to the model next to bash. See MCP servers.

  • Subagents.
  • Hooks.
  • Plan mode.
  • A todo tool.
  • Web search. An MCP server can provide it.
  • Dedicated edit tools. The model edits files with sed, a heredoc or any other command.
  • Pasting an image from the clipboard. Images come from -i, a path in a task, or an MCP tool.
  • Git checkpoints.
  • OAuth sign in for MCP servers.
20

Glossary

The words these chapters use, each in one or two sentences.

The Glossary page of the repository has these and more, grouped by topic.

ACP
The Agent Client Protocol. An editor starts umbra acp and talks to it in JSON-RPC on stdin and stdout.
Action
One command the model asks Umbra to run, or one MCP tool call.
Command group
Consecutive shell commands shown as one row in the transcript, opened with a click or Enter.
Compaction
Making the history smaller near the end of the context window: old outputs become placeholders, then the middle is summarized.
Context window
The most tokens a model can read in one request.
Deferred tools
MCP tools sent by name only, with a tool_search tool that loads their definitions.
Instruction file
A Markdown file such as CLAUDE.md or AGENTS.md that tells an agent how to work. Umbra adds them to the system prompt.
MCP
The Model Context Protocol. A server offers tools, and Umbra offers them to the model next to bash.
Mode
confirm, yolo or human: whether Umbra asks before each command, runs it, or lets you type it.
Prompt cache
A provider's store of a request it has already read, so a request with the same start costs less.
Provider
A named way to reach a model, such as anthropic, local or openrouter.
Session
One conversation, saved after every step, with an id that is its start time in milliseconds.
Skill
A folder with a SKILL.md whose frontmatter gives a name and a description. The model reads it when a task matches.
Slash command
A task that starts with /name, replaced by that command's prompt or that skill's instructions.
Status bar
The last row of the fullscreen view: context use, agent time, cost, model and weekly usage.
Journal
The file Umbra appends to after every step of a session. Resume reads it.
Trajectory
The JSON file of a session in mini-swe-agent's format, holding exactly what the model was sent. umbra export writes it from the journal.
Turn
Everything from one message you send to the reply that ends it.