Oga is an orchestrator for coding agents — the layer above the AI coding tools you already use. You describe a piece of work; Oga decides which tool and which of your accounts should run it, hands it the brief along with what your project has already taught Oga, runs it in the background against only the files you allow, and tells you the moment it finishes or needs an answer.
The tools it drives are Claude Code, Codex, OpenCode, Antigravity, and Pi. Oga
is not one of them and writes no code itself; it decides who does, and carries
the work there and back. Hand it a prompt and a working directory and you get a
task ID the moment dispatch succeeds. The worker runs independently while you
get on with other work, and a backgrounded oga watch process sleeps for free
and reports the moment anything settles.
Oga runs on macOS as a menu-bar app with a built-in SQLite store, a local
HTTP+MCP API on 127.0.0.1:7331, and a unix event socket for push delivery.
It is for developers and coding agents who want to fan out implementation,
research, review, and analysis across providers and accounts without paying
context-window cost for work they are not actively steering.
A bounded task — implement this module, review that diff, research a question — can run on an external account with scoped filesystem access. The caller writes the prompt, approves a data scope, and gets back a task ID. From there the worker runs unattended; the caller checks in when it settles.
This matters for three reasons:
Separate accounts, separate limits. Each provider has session caps and weekly quotas. Delegating spreads work across accounts so a rate limit on one does not block the whole session.
No context tax. A delegated worker's prompt and output do not consume the caller's context window. The caller pays a few hundred characters for the task ID and state — not 80,000 characters of re-shipped prompt every time it checks in.
Provider choice per task. A mechanical rename goes to a cheap fast model;
a concurrency audit goes to a frontier model. Oga routes automatically from
the prompt and the project's .oga.yaml policy, or the caller names a
profile and model explicitly.
Oga requires macOS 14 or later.
curl:
curl -fsSL https://downloads.desgn.space/oga/install.sh | shHomebrew:
brew install --cask DesgnSpace/tap/ogaBuild from source:
git clone https://github.com/DesgnSpace/oga.git
cd oga
bun install
make installA source build installs as Oga (local), so it sits in Applications next to
a released Oga without replacing it. Only one can run at a time — they share
port 7331 and ~/.oga — so quit one before opening the other. make install
quits whichever is open, launches the one it just built, and points oga in
~/.local/bin at it. Confirm Oga is running with:
curl http://127.0.0.1:7331/healthMCP client config: Connect tools in the app's Settings →
Connections writes a global MCP entry for every installed client — Claude
Code, Codex, OpenCode, and Antigravity — pointing at
http://127.0.0.1:7331/mcp. Existing configs get a .bak.
First launch detects which provider CLIs are installed and lets you add,
edit, and enable profiles. No accounts are hardcoded. Each profile stores its
provider, default model, and environment variables; secret-like values
(KEY, TOKEN, SECRET, PASS) are masked on every surface.
make install links the compiled binary onto your PATH as oga, replacing any
earlier link — after a source build, oga is the Oga (local) build. It carries
its own runtime, so every command below works with no Bun and no checkout.
| Command | What it does |
|---|---|
oga |
Print help — what Oga is, the commands, and a first run. |
oga serve |
Run Oga's local service. The menu-bar app starts it for you. |
oga delegate "<task>" |
Hand a task to a worker and print its id. Pass - to read the task from standard input. --worker, --model, and --kind choose who runs it; --effort sets how hard it thinks; --worktree runs it in its own checkout; --cwd runs it elsewhere. |
oga watch <task-id>... |
Wait for a task; prints one line when it settles. |
oga inflight |
List the tasks still running, so you know what a restart interrupts. |
oga tasks --query <text> |
Search active task history by title, summary, or prompt. |
oga config [cwd] |
Print the effective config for a directory — profiles, models, routes, brief rules — and which file each setting came from. |
oga love [worker:model[:effort]] |
Show or set the model used for work that names no model. Add --clear to return to per-task selection or --global to apply the choice to every project. |
oga version |
Print which build this is. |
Open Oga's Settings screen after the first launch. Add or edit a worker with a
worker ID, display name, command-line tool, default model, capabilities, and
environment variables. The supported provider names are exactly:
claude, codex, opencode, opencode-2, antigravity, and pi.
opencode-2 is OpenCode 2; see docs/opencode-2.md for
which install it runs and how to point it at another one. opencode runs
OpenCode 1: the first opencode that reports version 1, or the path in the
worker's OPENCODE_BIN.
Each worker is one provider account. The account directory always comes from
the worker, never from the shell Oga was started in. Claude workers each get
their own — $HOME/.claude for the worker named claude, $HOME/.<worker-id>
for the rest — while CODEX_HOME defaults to $HOME/.codex and
PI_CODING_AGENT_DIR to $HOME/.pi/agent. Set one of them in a worker's
environment to point that worker at a directory you have already signed into.
Secret-like environment keys containing KEY, TOKEN, SECRET, or PASS are
masked in the app and in oga config output.
The Settings screen controls which workers and models can receive work. A model must be enabled for a worker before Oga can send work there. You can mark one worker/model pair as loved, which becomes the default for unnamed work.
You can inspect the effective setup from the terminal:
oga config
oga loveTo choose the default explicitly, use the worker ID and model ID:
oga love opencode:openai/gpt-5.6-lunaUse oga love --clear to let routing choose per task again. Add --global to
any love command to read or write the user-wide setting instead of the current
project's .oga.yaml.
A delegated task runs unattended. Following it should cost nothing while it works. Oga gives you two layers, from cheapest to richest:
1. Background oga watch <taskId> — the default. A backgrounded process
that connects to the broker's unix event socket at ~/.oga/oga.sock for
instant push delivery, with a SQLite fallback when the socket is absent. It
sleeps for free, prints lifecycle and error lines as they happen, and exits
when any watched task settles. Exit codes carry the news on their own — 0 means
something settled, 1 means the deadline passed with nothing, 2 means bad input.
In Claude Code, a background command that exits re-invokes the agent — so
oga watch is the notification.
oga watch 8f2c1a94-... --timeout 30m &2. inspect — read one task's full record: output, scope, spend,
completion, and (on request) prompt and attempts.
Read docs/oga.md for the maintained command reference.
Every tool is available over MCP (http://127.0.0.1:7331/mcp) and the REST API.
| Tool | What it does |
|---|---|
delegate |
Start scoped work. Auto-routes or takes an explicit profile/model. |
models |
List preferred, enabled models ready for delegate. |
inspect |
Full record of one task. |
tasks |
Search tasks by text, state, time, profile, or fan-out batch. |
reply |
Answer a needs_input question on the same provider session. |
resume |
Retry a failed, cancelled, or blocked task on the same session. |
steer |
Send an instruction to work that is still active. |
handoff |
Move a dead task to a different profile, keeping the same task ID. |
edit |
Change a task that has not started — its prerequisites, settings, or brief — or requeue one cancelled before it started. |
cancel |
Stop a task and its worker process tree. |
complete |
Assert completion when work demonstrably landed but the worker never attested it. |
archive |
Soft-hide old tasks without deleting history. |
memory |
Durable project facts shared across callers and workers. |
query |
Ask in plain language where something lives in the project. |
health |
Broker and MCP contract versions. |
MCP client MCP / HTTP Oga broker sandbox-exec provider CLI
(Claude, ─────────────────→ 127.0.0.1:7331 ────────────────→ worker
Codex, …) ←── task view Rust broker ←── events, text (subprocess)
│
┌───────┴────────┐
│ SQLite (WAL) │
│ ~/.oga/ │
│ oga.db │
└───────┬────────┘
│
┌───────┴────────┐
│ event socket │
│ ~/.oga/ │
│ oga.sock │
└───────┬────────┘
│
┌───────┴────────┐
│ desktop app │
│ Tauri + React │
└────────────────┘
- Broker (
rust/apps/oga-cliandrust/crates/oga-service): Rust HTTP server on loopback-only127.0.0.1:7331, started byoga serve. Handles delegation, lifecycle, MCP, and REST. - Providers: Claude Code, Codex, OpenCode, Antigravity, and Pi. Each
spawned as a sandboxed subprocess with per-task read/write scope enforced by
sandbox-exec. Scope is relative to the task'scwd; literal file paths stay literal,dir/**is recursive,**grants the whole tree. - Event socket (
rust/crates/oga-events): Unix-domain socket at~/.oga/oga.sock. Pushes task-event batches to local subscribers as NDJSON.oga watchconsumes it for zero-poll follow-along; the desktop app can consume it too. - Desktop app (
rust/apps/oga-desktop): Tauri shell over the React UI inweb/, showing broker health, recent tasks with full event traces, and profile management. Ships the broker binary as a bundled sidecar.
Configuration resolves through three layers, highest first:
<project>/.oga.yaml— the project's own file, resolved from the task'scwd~/.oga.yaml— your personal config- Oga's built-in defaults — discovered accounts and the default brief rules
A missing file at any layer is normal. A file that fails to parse fails the
read that consulted it, naming the file. oga config [cwd] prints the
effective config for a directory and which file each setting came from.
A .oga.yaml constrains which provider/model pairs may run in a project.
Rules name providers and models, never local profile IDs. Oga still chooses
between matching local accounts using their availability.
Route names describe the work: mechanical, context, build, reasoning,
general. Each route has a provider/model allow list and optional
preference and min_quality.
Routes layer like everything else: a project file's routes map merges
over the user file's, per class. Scalar fields (preference, min_quality)
override; an allow list the project writes replaces the user's whole list,
because allow lists are written best-first and merging two would scramble their
meaning. A class neither file mentions stays unconstrained.
A profiles.<id> entry tunes one profile:
profiles:
claude-work:
label: Work Claude
claude-personal:
enabled: falseTwo rules carry the semantics:
- A declared table changes that profile and no other: turning one account off leaves every other account exactly as it was, and accounts no file mentions stay available. A disabled profile cannot be dispatched to, routed to, or listed in that project.
- A named id merges field by field onto the same id below it: a layer
overrides only the fields it writes, so the user file's
labeland the project file'smodelboth land. A file may also introduce an id no lower layer has;provideris required then, andmodelfalls back to the provider's default.
Profile env is one field. A project env map replaces the user env map
for that profile, and every value supports a leading $HOME or ~. For a
Claude profile, set CLAUDE_CONFIG_DIR in env to choose its account state:
profiles:
claude-work:
env:
CLAUDE_CONFIG_DIR: $HOME/.claude-workThe same resolved environment runs Claude tasks and reads claude /usage.
Without an override, claude uses ~/.claude; every other Claude profile
uses ~/.<profile-id>. Log into each directory once with the Claude CLI.
To restrict a project to a chosen set of accounts, name them in only. Every
id it leaves out is excluded in that scope — nothing else narrows the list:
profiles:
only: [claude-work, opencode]Profiles you manage in the Oga app stay in the app's own store — the file
layer only reads. The narrowing binds where it matters: delegate, handoff,
and every routing read resolve profiles against the task's cwd, so a project
file's only list is what a dispatch into that project sees.
Model settings are defaults at the user level and overrides at the project level. A project can enable a model disabled globally, or set its own preferred models:
models:
openai/gpt-5.6-luna:
enabled: true
preferred: true
capabilities: [reasoning, long-context]Unlisted models are enabled but not preferred. If a project declares no
preferred model, models falls back to every enabled model. Derived capabilities are
reasoning, long-context, tool-use, and free; a model entry's capabilities
array replaces those derived tags.
A project can set, in its own file, the rules a coding agent follows when it writes a brief:
caller:
prompt: |
Name the entry file in every brief.The file is read again on every dispatch. While it is there it replaces
whatever Settings → Brief rules holds for that project, and {{project}} in
the text becomes the project folder path.
The Tauri packaging workflow builds unsigned macOS and Linux bundles and the
updater artifacts consumed by the desktop app. Run a local package build from
the Rust workspace with cargo tauri build --no-sign;
rust/packaging/release.yml holds the matrix for platform builds and broker
smoke tests.
| File | What |
|---|---|
| docs/oga.md | Command line and task actions |
| docs/routing.md | Selecting workers and models |
| docs/scope.md | Files a task may read or change |
| docs/worktree.md | Separate checkouts and branches |
| docs/cleanup.md | Removing old task activity and worktrees |
Oga is licensed under the GNU Affero General Public License v3.0. You may use, modify, and share it freely. If you modify Oga and offer it to others over a network, you must publish your modified source under the same license.
Contact: yong@malico.me
See LICENSE.md for the full terms.
Early and moving fast. The broker, app, and MCP surface work. The event socket
and oga watch are new.
Expect rough edges and rapid iteration.