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.
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.
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.
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.
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.
`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.
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 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"
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.
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.
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"
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.
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.
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 status
Why
Replied
The model replied with no command
Submitted
A command printed COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT as its first line and exited with code 0
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.
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.
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.
The header is the first block of the transcript and scrolls with it: the umbra logo, the version, and a row each for the model, the folder, the mode, the number of skills and the MCP servers. A click on the model row opens the model picker, and a click on the MCP row opens the MCP panel.
The logo's stripes appear one at a time over about 1.4 seconds, and the input line takes typing during that time. UMBRA_BANNER_ANIMATION=0 shows the finished logo at once, and banner = false leaves the header out. In a terminal narrower than about 64 columns the logo is left out.
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 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.
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.
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.
Command
What it does
/config
Opens the configuration modal
/model [name]
Opens the model picker, or switches to the named model
/mcp
Opens the MCP panel
/mode [confirm, yolo, human]
Switches the mode, or opens a picker
/clear
Saves this session and starts a new one with an empty history
/compact [focus]
Summarizes the history now, keeping what focus names
/resume
Opens the session picker
/skills
Opens a picker of skills. Enter puts /<skill> in the input line
/update
Installs the newest release and restarts into this session
/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.
/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.
/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.
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.
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.
/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.
--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".
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.
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.
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.
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.
--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.
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.
Command
What it does
umbra sessions
This folder's sessions, newest first. --all lists every folder's
umbra resume
The 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
--fork
With --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, /clear
The picker inside a session, and a new empty session with the old one 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"
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.
Setting
Default
What it does
images.max_long_edge
1568
Images whose long edge is bigger are scaled down to it. 0 never scales
images.max_bytes
10485760
An image whose base64 text is bigger is refused
images.detect_paths
true
Attach 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?"
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 setting
What it does
kind
anthropic, openai or replay
base_url
The API's base URL
api_key_env, api_key
The variable that holds the key, or the key itself
max_tokens, temperature, effort
The reply length, the temperature (only sent when set) and the Anthropic effort
extra_body, headers
Fields added to every request body, and extra request headers
stream, send_reasoning, preserve_thinking
OpenAI compatible servers: streaming, and the reasoning sent back
input_cost_per_mtok, output_cost_per_mtok
Dollars per million tokens, for a model Umbra has no price for
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.
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.
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.
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.
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.
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.
`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.
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 feature
What Umbra does
Modes
confirm asks the editor before each command, yolo runs them. Always allow remembers the program for the rest of the session
Model picker
The configured model and every model in models or [models.<name>]
Sessions
session/load, session/resume and session/list use the same sessions as umbra --resume
Stop
session/cancel stops the running command or model call, the way Ctrl+C does
Images and files
Image blocks go to the model, and attached files are added to the task
MCP servers
The editor's servers are added to the ones Umbra finds
Slash commands
Your slash commands and skills are offered as the editor's commands
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.
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.
Setting
Default
What it does
model
none
provider/model, or a name from [models.<name>]
mode
confirm
confirm, yolo or human
action_format
tools
tools or text
finish
reply
reply, or submit for the mini-swe-agent style
step_limit, cost_limit
0, 0
Model calls and dollars. 0 means no limit
whitelist_actions
empty
Commands that run without asking
fullscreen, mouse, status_bar, banner
true
The parts of the fullscreen view
syntax_highlight
true
Colors in fenced code and opened commands
environment.timeout
120
Seconds before a command is stopped. The model can ask for more, up to environment.max_timeout, 600
environment.persistent_shell
true
Each command starts in the folder and with the variables the previous command ended with. Local bash only
sources.<name>
true
Read that tool's files
mcp.only, mcp.disabled
empty
Which MCP servers start
mcp.defer_tools
auto
auto, always or never
compaction.trigger_ratio
0.8
Compact past this share of the window
updates.check
true
The daily update check
sessions.auto_title
true
The request that titles a session
usage.enabled
true
The 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.
`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.
Command
What 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
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.
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.