Extensions

Extensions are opt-in, agent-agnostic add-ons that compose Thurbox through the public thurbox-cli surface — they never extend the binary. The core knows a declarative manifest format , never any specific extension, so the same machinery installs and manages any of them.

Overview #

An extension is a directory with a single extension.toml manifest plus the files it ships (specs, scripts, config templates). One command installs it: thurbox-cli fetches the manifest, lays the files down under a home directory, registers any agents the extension needs in agents.toml , then creates — and keeps alive — the sessions and automations the extension declares.

  • Data, not binary. Nothing about an extension is compiled in; the core reads a manifest and acts on it.
  • Agent-neutral. Extensions reach coding agents only through agents.toml aliases you remap to any CLI (claude, codex, antigravity, opencode, vibe, …).
  • Self-healing. While an extension is active, deleting its session or automation is a no-op — Thurbox recreates it. Deactivating is the real off-switch.

Two extensions ship with Thurbox, and both are built in. Everything else you install yourself, from a URL, a local directory, or a git repository. The worked example is fleet , a control-plane template you clone — see Available extensions . The commands and manifest fields below are generic; the examples use fleet.

Two extensions ship built in: agent hooks , which wires each agent's lifecycle hooks to thurbox-cli session signal so the session list shows working/blocked/done/idle, and ui-skill , which teaches whichever coding CLI you run how to edit the interface . Unlike the others both are embedded in the binary and auto-activated — they still install through the same manifest machinery shown here. Turn either off with thurbox-cli extension deactivate <name> .

Install #

Install an extension from a directory, a URL, or a git repository:

bash
thurbox-cli extension install ./my-extension
thurbox-cli extension install git+https://github.com/you/my-extension

That single command is the whole installer. It resolves the source, fetches the manifest and payload, writes them under the extension home ( ~/.config/thurbox/extensions/<name> by default; --home <dir> to override), registers the extension's agents, writes the resolved manifest to ~/.config/thurbox/extensions/<name>.toml , and activates it. It is idempotent — re-running refreshes shipped files while keeping your own data (seeded files you've edited, agent tweaks).

Lifecycle commands #

All extension management lives under thurbox-cli extension (alias ext ):

Command What it does
install <url|dir|git+repo|name> Fetch + lay down files, register agents, write manifest, and activate.
uninstall <name> Reverse an install: tear down the session/automation, remove the extension's agents, and delete the manifest. Add --purge to also delete the home dir.
activate <name> (Re)create the declared sessions/automations and mark the extension active.
deactivate <name> Tear down resources and stop self-heal (the off-switch). Add --force to kill tmux/worktrees, --purge to also drop the manifest.
list Show installed extensions with their active / healthy state.
status [<name>] Per-resource presence for one extension (or all).

Install vs. activate. install also lays down files and registers agents; activate only (re)creates the runtime resources from an already-installed manifest. Use deactivate to switch an extension off but keep it installed for a later activate .

Sources & versioning #

The install target can be four things:

  • A local directory ( ./my-extension ) — installs from a checkout.
  • An http(s):// base URL — installs from your own source, fetching each manifest-named file.
  • A git repository ( git+https://… , a URL ending in .git , or git@host:path ) — cloned as a working copy, which is how an extension that lives in its own repo installs. Recognised explicitly, so a plain https:// URL keeps meaning “a base to fetch files from”.
  • A bare name — fetched from the official source over curl / wget , pinned to your binary's release tag (dev builds track main ), so a fetched extension always matches the binary reading it. Nothing ships under a bare name today: the only extensions in the Thurbox repository are the two built-ins, which install themselves.
bash
thurbox-cli extension install ./my-extension
thurbox-cli extension install https://example.com/ext/my-extension --home ~/work/ext

Safety: payload paths are validated against traversal (no absolute paths or .. ), a config template you've edited isn't overwritten on reinstall (use --force ), and payload files are fetched as text (specs, scripts, JSON) — not binaries.

Self-healing #

An installed extension is recorded in an active set . On TUI startup and on every automation tick, Thurbox re-ensures each active extension's declared sessions and automations exist — so deleting them by hand (TUI Ctrl+D , a clean , or thurbox-cli session/automation delete ) is a no-op: they come back. This is what makes an extension hard to half-remove by accident.

  • deactivate <name> removes the extension from the active set, so self-heal stops resurrecting it — the real off-switch.
  • Headless self-heal (while the TUI is closed) rides the automation heartbeat, so it needs [features] automations = true ; with automations off, healing happens at the next TUI startup only.

The manifest #

An extension is fully described by its extension.toml . It has two halves: an install spec (what to lay down) and a runtime spec (what to keep alive). The {home} token is substituted with the resolved home directory.

The example below is modelled on fleet. Its real manifest uses [[agents]] , one [[files]] , three [[symlinks]] and one [[sessions]] ; the rest is illustrative, so every field the format supports appears once.

extension.toml
name = "fleet"
description = "Control-plane session: the repo map and thurbox orchestration"
config_version = 1
# home = "~/fleet"              # OPTIONAL; default is under the config dir
                                # ({home} is substituted everywhere it appears)

# install spec ---------------------------------------------------------------
[[agents]]                      # registered in agents.toml (existing kept)
name = "fleet"
command = "claude"
args = ["--model", "claude-haiku-4-5"]

[[files]]                       # fetched from the source, written under home
path = "FLEET.md"
[[files]]
path = "scripts/sync-registry.sh"
executable = true               # chmod +x
[[files]]
path = "repos.md"
if_absent = true                # seed once; never clobbered on reinstall
[[files]]
path = ".claude/settings.json"
source = "claude-settings.json" # source path differs from dest
substitute = true               # replace {home} in the content

[[symlinks]]                    # never clobbers a real file at `link`
link = "CLAUDE.md"
target = "FLEET.md"

# runtime spec (ensured on activate, self-healed if deleted) -----------------
[[sessions]]
name = "fleet"
agent = "fleet"
repo_path = "{home}"            # resolved to the absolute home at install.
                                # {home} is the EXTENSION home, so an extension
                                # whose session must open your own checkout ships
                                # a placeholder its installer renders instead

# [[automations]] is an OPTIONAL runtime resource (fleet ships none on purpose —
# its only scheduled candidate pushes to `main`). An extension that wants a
# scheduled tick declares:
[[automations]]
name = "example-tick"
trigger = "cron:*/10 * * * *"   # same grammar as `automation create --trigger`
session_ref = "fleet"           # must match a [[sessions]] name above
prompt = "tick"
Section Purpose
home Default install directory; overridable with --home .
[[agents]] Agent definitions appended to agents.toml (existing names are kept).
[[files]] Payload files written under home . Flags: executable , if_absent (seed once), substitute ( {home} in content), and source (when the source path differs from the destination).
[[symlinks]] Symlinks created under home (never clobber a real file).
[[sessions]] Sessions ensured on activate and self-healed if deleted.
[[automations]] Automations ensured on activate; session_ref names a session above.

Authoring an extension #

  1. Create a directory with an extension.toml and the files it references ( [[files]] paths are relative to both the source and the home dir).
  2. Build the behavior on the thurbox-cli surface ( session , task , automation ) plus standard tools — no core changes, no vendor lock-in.
  3. Test locally with thurbox-cli extension install ./your-ext , then publish it — a git repository is the usual home, and install git+https://… clones it.

For the exact config-file locations and the metadata key that tracks active extensions, see the configuration reference .

Available extensions #

Two ship with Thurbox and are on by default; anything else you install yourself.

Built in

Ships embedded in the binary and is active by default — no install step.

Install it yourself

Lives in its own repository, installs through the same manifest machinery.