Skip to content
DesgnSpacePublic

About

Local broker for delegating bounded tasks to AI provider CLIs

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

Oga

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.

Why delegation

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.

Install

Oga requires macOS 14 or later.

curl:

curl -fsSL https://downloads.desgn.space/oga/install.sh | sh

Homebrew:

brew install --cask DesgnSpace/tap/oga

Build from source:

git clone https://github.com/DesgnSpace/oga.git
cd oga
bun install
make install

A 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/health

After install

MCP 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.

Command line

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.

Set up workers

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 love

To choose the default explicitly, use the worker ID and model ID:

oga love opencode:openai/gpt-5.6-luna

Use 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.

Following a task

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.

Tool surface

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.

Architecture

  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-cli and rust/crates/oga-service): Rust HTTP server on loopback-only 127.0.0.1:7331, started by oga 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's cwd; 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 watch consumes 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 in web/, showing broker health, recent tasks with full event traces, and profile management. Ships the broker binary as a bundled sidecar.

Configuration

Configuration resolves through three layers, highest first:

  1. <project>/.oga.yaml — the project's own file, resolved from the task's cwd
  2. ~/.oga.yaml — your personal config
  3. 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.

Routing policy

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.

Profiles

A profiles.<id> entry tunes one profile:

profiles:
  claude-work:
    label: Work Claude
  claude-personal:
    enabled: false

Two 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 label and the project file's model both land. A file may also introduce an id no lower layer has; provider is required then, and model falls 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-work

The 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.

Models

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.

Brief rules

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.

Releasing

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.


Docs

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

License

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.

Status

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.

About

Local broker for delegating bounded tasks to AI provider CLIs

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages