Documentation

chela is a tiny control plane over a single tmux session: it schedules long-lived agents and dispatches a TODO list into pull requests, then lets you watch the whole fleet on a live terminal wall.

Quickstart

Five minutes from clone to a scheduled agent.

1. Install

chela uses uv. The core has two small deps; the dashboard + live terminal wall ship as a separate install (keeps the core lean). You also need tmux, git, the claude CLI on PATH (and gh for the dispatcher's PR flow).

# core
git clone https://github.com/Devail1/chelamux && cd chelamux
uv sync
uv run chela status

# dashboard + live terminal wall (separate install — keeps the core lean)
uv sync --extra dashboard
uv run chela dashboard

Authenticate Claude once. chela never touches credentials — it drives the claude CLI inside your tmux windows. Log in once on the machine (claude, then /login — or claude setup-token for a headless token); every agent window reuses the cached ~/.claude credentials. The whole fleet therefore runs as one Claude account and shares its 5h / 7d rate limits.

2. Make a session, schedule an agent

A tmux session is your fleet; each window is an agent (the window name is its display name).

# one tmux session whose windows are your agents
tmux new-session -d -s chela -n researcher

# see what chela can see
uv run chela status

# poke the agent every hour, then run the daemon
uv run chela schedule add researcher --every 1h --prompt "Run your research cycle."
uv run chela run

3. Dispatch a TODO list into PRs

Drop a WORKFLOW.md + TODO.md into a repo (copy examples/), then point chela at it. Each open - [ ] becomes a worktree → an agent → a PR.

uv run chela dispatch /path/to/repo/WORKFLOW.md --once   # one pass
uv run chela dispatch /path/to/repo/WORKFLOW.md          # poll forever

Concepts

tmux is the source of truth

chela holds no separate registry of agents. Discovery is tmux list-windows + pane_current_path, read live every tick — so what chela sees is exactly what's running right now. A window rename keeps the same agent; nothing to re-register.

One window per agent

Each window of your session (CHELA_TMUX_SESSION, default chela) is an agent. The window name is how you target it — schedules, messages, and the dashboard all key off it.

Two ways to put agents to work

The wall

The dashboard streams every agent's live terminal in one grid (drag, lock, maximize), with a context-window bar per tile and account-wide rate-limit pills — a first-class feature, just a separate install. The wall itself is opt-in (CHELA_TERMINALS_ENABLED=true) because it exposes writable shells — keep it on loopback or a tailnet. See Remote access & security.

Scheduling agents

A schedule pokes an agent's pane with a prompt on a cadence — the agent does the rest.

# interval: 30s / 5m / 1h / 1d
chela schedule add researcher --every 1h --prompt "Run your research cycle."

# cron expression
chela schedule add reporter --cron "0 */8 * * *" --prompt "Post the 8-hourly summary."

# one-shot at an ISO timestamp
chela schedule add deployer --once "2026-06-01T09:00" --prompt "Cut the release."

chela schedule list            # every task + its id
chela schedule remove 3        # delete by id

Standing context: the agent CLAUDE.md

A scheduled agent wakes into whatever its working directory contains. Drop a CLAUDE.md at the root to give it a stable role — what it is, what to do each cycle, and its boundaries (see examples/agent-template.md). The schedule supplies the recurring nudge; CLAUDE.md supplies the identity.

Dispatching work (the headline feature)

Turn a markdown checklist into a stream of pull requests, each built by its own isolated agent.

TODO.md — the work list

Every unchecked - [ ] bullet is a work item. Append <!-- blocked: reason --> to make the dispatcher skip a line.

## Open
- [ ] Add a --version flag to the CLI
- [ ] Write a docstring for the public API entry point
- [ ] Add a unit test for the config loader <!-- blocked: waiting on fixtures -->

WORKFLOW.md — the config + agent brief

A YAML front-matter block configures the dispatcher; the markdown body below it is the prompt template handed to each agent (with {{placeholders}} like {{task_title}}, {{branch_name}}, {{workspace_path}}). Put both files in the repo root.

project_key: PROJ          # branches/windows are <key>-<n>, e.g. proj-1

tracker:
  kind: markdown          # markdown TODO.md — also: gh_issues
  path: TODO.md           # relative to this file

workspace:
  root: ~/.chela/worktrees/proj   # where per-task worktrees go
  base_branch: main            # branch worktrees fork from + PRs target

concurrency:
  max: 1                  # tasks in flight at once

agent:
  cmd: claude --permission-mode auto   # or: bypassPermissions on a trusted repo
  startup_delay_seconds: 4
  ready_timeout_seconds: 60

hooks:                       # all optional, run in the worktree
  # after_create: seed .claude/settings.local.json (least-privilege perms)
  # before_run:   uv sync --quiet || true   (lockfile sync / codegen)
  # after_done:   runs in the repo when the PR merges (e.g. a deploy)

The lifecycle

Each task is keyed by a stable SHA of its source line (idempotent — a task is never picked up twice). For each open item the dispatcher:

The dispatcher shape (task-list → isolated worktree → autonomous agent → PR) is an adaptation of OpenAI's Symphony pattern.

Dashboard & the wall

uv sync --extra dashboard
uv run chela dashboard           # http://127.0.0.1:5001

The web UI has tabs for agents (live liveness — alive / waiting / offline), schedules, the dispatcher, and a Kanban of runs. Liveness is derived from the native session status — no heartbeat daemon.

The embedded terminal wall streams the live ttyd panes in a grid. It's off by default because it serves writable shells; enable it explicitly:

CHELA_TERMINALS_ENABLED=true uv run chela dashboard

Context & rate-limit pills

Exact context-window usage and the 5h / 7d rate-limit pills come from Claude Code's statusLine payload, which chela caches via a tiny hook. Install it once for precise numbers (without it, the context bar falls back to a coarse transcript estimate):

chela install-statusline           # prints the snippet
chela install-statusline --write   # writes it (won't clobber an existing one)

Keys not reaching the terminal

If Esc (or other keys) never reaches an embedded terminal, a vim-style browser extension such as Vimium is almost certainly capturing them at the page level — it injects into the terminal's iframe too and swallows the keypress.

Exclude the dashboard's URL in the extension's settings. In Vimium: Options → "Excluded URLs and keys" → add the dashboard URL and leave the Keys field blank to disable it on that site. Quick workaround: Ctrl+3 (or Ctrl+[) sends a literal Escape.

Command reference

One CLI, chela. Every command targets agents by their tmux window name.

Core

chela status

List the agent windows discovered in your tmux session — the source of truth.

chela run

Run the daemon: scheduler tick + dispatcher + needs-input notify. Leave it running.

chela dashboard [--host] [--port]

Launch the dashboard + live terminal wall (needs the dashboard install). Binds 127.0.0.1:5001.

Scheduling

chela schedule add <agent> --prompt … (--every|--cron|--once)

Schedule a prompt — an interval (30s/5m/1h/1d), a cron expression, or a one-shot ISO timestamp.

chela schedule list

List every scheduled task with its id.

chela schedule remove <id>

Delete a task by id.

Dispatch

chela dispatch <WORKFLOW.md> [--once] [--interval N] [--dry-run]

Turn each open - [ ] item into a worktree, an agent, and a PR. Polls every 60s; --once runs one tick.

chela dispatch-runs

List dispatcher runs and their status.

chela task-finished <task_id>

(agents call this) mark a run awaiting-review, record the PR, and kill its window.

Messaging

chela msg <agent> <message> [--from] [--priority]

Drop a message into one agent's pane. Priority: critical|high|normal|low.

chela broadcast <message> [--from] [--priority]

Send the same message to every other live agent at once.

Setup

chela install-statusline [--write] [--force] [--settings]

Print (or --write) the statusLine snippet for ~/.claude/settings.json so panes report live context + rate limits.

Configuration

All configuration is environment variables, with sensible defaults.

VariableDefaultPurpose
CHELA_TMUX_SESSIONchelatmux session chela orchestrates
CHELA_DIR~/.chelaState dir (scheduler.db, worktrees, context)
CHELA_SCHEDULER_POLL_INTERVAL30Daemon loop interval (s)
CHELA_DISPATCH_WORKFLOWSColon-separated WORKFLOW.md paths the daemon dispatches
CHELA_DISPATCH_TICK_INTERVAL60Dispatcher tick interval in the daemon (s)
CHELA_AGENT_CMDclaudeLaunch command for the dashboard Start button
CHELA_NOTIFY_URLNeeds-input notification target (ntfy / Telegram / webhook)
CHELA_DASH_HOST / CHELA_DASHBOARD_PORT127.0.0.1 / 5001Dashboard bind address
CHELA_TERMINALS_ENABLEDfalseEmbedded ttyd terminal wall (opt-in; streams live)
CHELA_DEFAULT_CONTEXT_WINDOW200000Window size assumed by the transcript-based context estimate (fallback)

Needs-input notifications

When an agent's pane enters the waiting state (a permission prompt or a question), chela fires one edge-triggered notification — so you don't have to babysit. Transport is auto-detected from the URL:

CHELA_NOTIFY_URL=https://ntfy.sh/my-chela-topic                                  # ntfy
CHELA_NOTIFY_URL="https://api.telegram.org/bot<token>/sendMessage?chat_id=<id>"  # Telegram
CHELA_NOTIFY_URL=https://example.com/hook                                       # generic webhook

Remote access & security

chela ships with zero built-in auth, by design. The dashboard and the ttyd terminals bind 127.0.0.1. The wall is a writable shell — exposing it on an untrusted network is remote code execution. The tailnet is the trust boundary, not a password.

For remote access, put the loopback dashboard behind one of:

Or skip the web UI entirely: SSH/Mosh in from a mobile terminal and tmux attach -t chela for the live panes straight from your phone.