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. For a fuller walkthrough through to your first dispatched agent, see the Getting Started guide.

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

# the Telegram bridge is its own extra. Name every extra you want in ONE
# command — `uv sync --extra X` replaces the env and drops the rest
uv sync --extra dashboard --extra telegram

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 is on by default but loopback-guarded: because it serves writable shells, the dashboard only serves it on a 127.0.0.1 bind. A non-loopback bind refuses it unless you set CHELA_TERMINALS_EXPOSE=true. 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 on by default but loopback-guarded: it serves writable shells, so the dashboard only serves it on a 127.0.0.1 bind. On a non-loopback bind it's refused unless you opt in explicitly:

CHELA_TERMINALS_EXPOSE=true uv run chela dashboard --host 0.0.0.0
# or turn the wall off entirely:
CHELA_TERMINALS_ENABLED=false uv run chela dashboard

The hooks plugin (recommended)

chela works without any Claude Code hooks — it scrapes each tmux pane as a fallback. But the event-log plugin (chela plugin) is strongly recommended: it POSTs every tool call, prompt and permission gate to the daemon before the fact, which unlocks lossless blocked-agent questions on Telegram (a scraped multi-question or preview selector otherwise reaches your phone with no options), answering a question with zero keystrokes, and the live event Feed. It fails open — if the daemon is down the hook logs a warning and your agent carries on.

Install it straight from this repo, inside Claude Code — works out of the box on the default dashboard port (5001):

/plugin marketplace add Devail1/chelamux
/plugin install chela@chela

On a non-default dashboard port? A hook URL is a literal (Claude Code doesn't expand env vars in it), so render your own copy with the port baked in:

chela plugin --dir ~/.chela/plugin        # bakes in the port the dashboard actually bound
claude --plugin-dir ~/.chela/plugin       # or: /plugin marketplace add ~/.chela/plugin

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.

Collaborative terminals (end-to-end encrypted)

Share a live terminal over the internet — encrypted end to end, through a relay that only ever sees ciphertext.

Share a session

In the dashboard, click Share (the link icon) on any pane's header — or Share current session from the overflow menu on mobile. chela mints a share and shows a join link plus a short pairing code. Send both to whoever's joining; they open the link, paste the code, and they're in the same live terminal with full access — watch, type, and scroll. Everyone sees each other as live, labeled cursors and a facepile of who's watching.

Sharing is off until you set CHELA_COLLAB_RELAY (see below) — chela never phones home. Once a relay is configured, the Share control appears on every pane.

How the encryption works

Presence

Everyone in a shared session is a live, labeled cursor mapped to the terminal grid, plus a facepile of who's watching (the host gets a ★). Cursors and names ride the same encrypted channel (k_pres), so the relay can't see who's present or where they're pointing. It's colorblind-safe — every cursor carries a name label, not just a color.

On a phone

Joiners on mobile get the full experience: the terminal letterboxes to fit, an on-screen keys-line (Esc / Tab / arrows / a sticky Ctrl / …) sits above the keyboard, swipe scrolls the session, and your touch shows to everyone else as a cursor.

Run your own relay

The relay is a small Cloudflare Worker (source in chela/collab-relay/) — a dumb, opaque fan-out with one Durable Object per room. Deploy your own and point chela at its wss:// URL so even the room-name/timing metadata stays yours:

# deploy the relay (Cloudflare Workers)
cd chela/collab-relay && npx wrangler deploy

# point chela at it, then start the dashboard
CHELA_COLLAB_RELAY=wss://your-relay.workers.dev uv run chela dashboard

Full access is the model. A paired joiner holds the code, so they can both watch and drive (scroll is input on a full-screen TUI). Share only with people you'd hand the keyboard to.

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 plugin [--dir PATH] [--port N]

Render the Claude Code hooks plugin (event log + zero-keystroke Telegram answers). Recommended — see above.

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_ENABLEDtrueEmbedded ttyd terminal wall (streams live; loopback-guarded)
CHELA_TERMINALS_EXPOSEfalseServe the writable wall on a non-loopback bind too (RCE risk — opt-in)
CHELA_COLLAB_RELAY— (off)Relay wss:// URL for collaborative terminal sharing (end-to-end encrypted). Empty = sharing off; chela never phones home. Details.
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.

Or run the built-in Telegram bridgechela telegram gives every agent window its own forum topic (1 topic = 1 window = 1 session), so you can drive or supervise any agent from your phone, two-way (text, images, and file attachments flow both ways). It's a full bridge, distinct from the one-shot needs-input notifications above — and it ships with chela, no separate service to run.

To share a session with someone else over the internet — rather than exposing your whole dashboard — use collaborative terminals: end-to-end encrypted, through a relay that only ever sees ciphertext.