marvel keeps a declared team of AI agent sessions running: it starts them, watches them, repairs them and rotates them, for any harness.
You describe a team in a short manifest: its roles, how many sessions each role runs, the command each one starts, and how marvel knows a session is alive. marvel runs each session in a tmux pane, compares what is running with what you declared every two seconds, and fixes the difference.
A session that dies is replaced. A role can be scaled with one command. A shift swaps every session for a fresh one while the old ones drain, so a long-running team sheds stale context without going dark. It works with Claude Code, Codex, opencode, or any command you name.
New here? The first-run page walks four stages, each optional and each with one proof command, so you can stop at the first one that does what you need.
Try it with an installed marvel. It needs no model login and costs nothing, and tmux is the one prerequisite. marvel example lists the manifests the binary carries:
marvel example
marvel daemon &
marvel example demo > demo.toml
marvel work demo.toml
marvel get sessions
marvel stop --teardownThen take the tour, which needs a source checkout, or go deep with the four-act demo.
brew tap arcavenae/tap # one-time (Homebrew >= 6.0 needs the tap trusted first)
brew install arcavenae/tap/marvelThe tap's marvel formula currently tracks the latest alpha release. A dedicated stable formula will be added when the first v* release ships.
mise is a polyglot version manager. It reads a per-project mise.toml, pulls the binary directly from GitHub Releases, and verifies GitHub Artifact Attestations natively, with no Homebrew tap required.
Alpha channel (prereleases from main): the first stable release is pending, so opt into prereleases per-tool:
# mise.toml
[tools]
"github:ArcavenAE/marvel" = { version = "latest", prerelease = true }mise install
marvel versionStable (when a v* release ships): drop the prerelease = true flag:
mise use github:ArcavenAE/marvel@latestMarvel's alpha assets are not -a-suffixed, so stable and alpha share the same marvel shim; mise pins one version at a time per mise.toml, and switching channels swaps the underlying binary.
macOS troubleshooting: if a quarantine-aware host copies the mise install and Gatekeeper blocks launch, clear the xattr once:
xattr -d com.apple.quarantine "$(mise which marvel)"Download the latest release from GitHub Releases. Binaries are available for macOS (arm64, amd64) and Linux (amd64, arm64: Raspberry Pi 3/4/5 64-bit, AWS Graviton, etc.). macOS binaries are code-signed and Apple-notarized.
Host requirements (tmux 3.5 or later, and nats-server only for a managed bus) are in the admin guide.
# See the manifests the binary carries
marvel example
# Start the daemon
marvel daemon &
# Load the demo manifest
marvel example demo > demo.toml
marvel work demo.toml
# Watch sessions
marvel get sessions -w
# Trigger a rolling shift (replace all sessions with fresh ones)
marvel shift demo/squad
# Clean up
marvel stop --teardownTo run from a source checkout instead, build first with just build; it puts marvel in bin/.
Where kubernetes orchestrates containers across nodes, marvel orchestrates agent sessions across tmux panes.
Workspace isolation boundary (k8s namespace)
└─ Team cohesive unit of agents (k8s deployment)
└─ Role one kind of agent: name, replicas, runtime, healthcheck
└─ Session one agent process in a tmux pane (k8s pod)
A team contains heterogeneous roles. A review team might have 1 supervisor, 3 reviewers, and 1 architect, each with its own runtime, replica count, restart policy, and healthcheck config.
Resources are declared in TOML manifests:
[workspace]
name = "my-project"
[[team]]
name = "review-squad"
[[team.role]]
name = "reviewer"
replicas = 3
[team.role.runtime]
image = "claude"
command = "claude"
[team.role.healthcheck]
type = "heartbeat"
timeout = "30s"
failure_threshold = 3
[[team.role]]
name = "supervisor"
replicas = 1
restart_policy = "always"
[team.role.runtime]
image = "claude"
command = "claude"image names the harness and is what selects the runtime adapter; marvel
falls back to command when it is unset, and a name matching no adapter
resolves to the generic one. Every adapter puts the session's identity in the
environment (MARVEL_SESSION, MARVEL_ROLE, MARVEL_TEAM,
MARVEL_WORKSPACE), so no role passes its own identity flags. The claude
adapter additionally names the session in the system prompt, unless the role
already passes its own --append-system-prompt or its command is a wrapper
around the harness (anything other than claude by name or path), which then
owns the prompt. Runnable manifests live in
examples/.
# Daemon and manifests
marvel daemon # start the daemon
marvel work <manifest.toml|.yaml> # load manifest, reconcile desired state
marvel stop # stop daemon, detach (agents keep running, adopted on restart)
marvel stop --teardown # stop daemon and end every agent
# Inspect
marvel get sessions # list sessions (-w for watch mode); --columns cpu,rss adds CPU% and RSS
marvel get teams # list teams and roles
marvel get workspaces # list workspaces
marvel get endpoints # list endpoints
marvel describe session <key> # session details
marvel events # recent session/team state-transition events (incl. agent.* for observed runtimes)
# Lifecycle
marvel scale <ws/team> --role <r> --replicas N # scale a role
marvel shift <ws/team> [--role <r>] # rolling shift
marvel run <cmd> [args...] --role <r> # one-off session
marvel kill <session-key> # kill a session (alias: delete session)
marvel delete <resource> <key> # delete a session, team, or workspace
# Session interaction
marvel capture <session-key> # capture a session's pane content
# a plain capture drops dim text and says so on stderr; it reads the pane twice, so a pane that changes between the reads can give a stray notice or miss one
marvel capture -e <session-key> # keep escape sequences, so dim text (a composer suggestion) can be told from a typed draft
marvel capture --composer <session-key> # report on stderr whether the composer is empty, holds text or is mid-turn; types nothing
marvel inject <session-key> [text] [--key <key>] # send keystrokes to a pane (executive privilege)
# Remote daemons (named clusters)
marvel config add-cluster <name> ... # add or update a cluster
marvel config list # list configured clusters
marvel config current # show the current cluster
marvel config use-cluster <name> # switch the current cluster
marvel config remove-cluster <name> # remove a cluster
# SSH keys (client auth to a daemon)
marvel keys generate # generate a client keypair
marvel keys show # print a client public key to share with a daemon admin
marvel keys list # list local client keypairs
marvel keys trust <cluster> # show a cluster's host key fingerprint, ask, then record it (--yes skips the question)
marvel keys authorize <pubkey> # authorize a client's key on this daemon
marvel keys authorized # list clients authorized on this daemon
marvel keys revoke <fingerprint> # revoke a client
marvel keys doctor # audit (and optionally fix) permissions under ~/.marvel/
# Version and self-upgrade
marvel version # print version and channel
marvel upgrade # upgrade to the latest releasestop defaults to detach: the daemon checkpoints state and exits while
agents keep running; the next marvel daemon adopts the live panes. Use
--teardown to end every agent and leave the machine clean.
marvel events includes agent.* events (session start/end with cost and
timing, tool calls, permission prompts) for runtimes marvel can observe,
today a headless stream-json launch (see examples/claude-headless.toml).
Shifts are rolling replacements of agent sessions. Agent sessions accumulate context, drift, and stale mental models. A shift starts fresh sessions, verifies they're running, then drains the old ones, preserving team identity.
# Shift all roles (workers first, supervisor last)
marvel shift my-project/review-squad
# Shift only one role
marvel shift my-project/review-squad --role reviewerSessions track their generation (visible in marvel get sessions as the GEN
column). During a shift, old-generation sessions drain one per reconciliation
tick while new-generation sessions are already running.
Roles can configure healthchecks. Currently supported: heartbeat (agent must
send periodic heartbeats to the daemon) and process-alive (tmux pane exists).
A passed heartbeat reads healthy in marvel get sessions; a process-alive
role reads alive, because nothing beyond the pane was checked.
[team.role.healthcheck]
type = "heartbeat" # "heartbeat" or "process-alive"
timeout = "30s" # heartbeat staleness threshold
failure_threshold = 3 # consecutive failures before actionRestart policies control what happens when a session is unhealthy:
always(default): delete and recreateon-failure: delete and recreate only if failednever: mark failed, don't restart
Sessions without a configured healthcheck stay in unknown health state
and are never restarted by health evaluation.
examples/ holds a manifest per scenario, each shipped in both TOML and YAML.
examples/README.md indexes every one of them: what it
demonstrates, grouped by topic, and which to open first.
Three entry points:
| Manifest | What it shows |
|---|---|
examples/generic-agent.toml |
The smallest thing that works: marvel managing an arbitrary command through the generic adapter. No model auth needed. |
examples/claude.toml |
Real Claude Code agents beside a simulator supervisor: adapter selection, permission injection, identity injection. Requires the claude CLI. |
examples/review-team.toml |
A heterogeneous team: reviewer, architect and supervisor roles, each with its own replica count. |
just build # build marvel + simulator
just test # run all tests
just demo # load demo manifest, show state
just demo-shift # demonstrate shift lifecycle
just watch # watch sessions (interactive)
just shift <team> # trigger a shift
just scale <t> <r> <n> # scale a role
just start # start daemon (foreground)
just stop # stop daemon
just clean # kill tmux sessions, remove binariesWritten in Go. The daemon manages agent sessions through a tmux substrate:
- Reconciliation loop (2s interval): compares desired state (manifests) with actual state (running sessions), creates/deletes to match
- Health evaluation: checks heartbeat staleness, applies restart policies
- Shift state machine: launching → draining → complete, driven by the reconciliation loop
- Usage accountant: derives context-window occupancy from each harness's own usage reporting, feeding the CTX% column (see user-guide)
- Simulator: context pressure simulation for testing without real agents
Marvel works with any BYOA console it can launch as a process in a tmux pane:
claude, codex, opencode, or a custom agent. The runtime is just a
command path plus args, and marvel does not care what the agent is: it
manages the process lifecycle.
The prompt is not delivered on stdin. The claude adapter passes it as a
positional argument under --print; the codex and opencode adapters close
stdin outright, because codex exec otherwise appends piped stdin to its
prompt and hangs on the pane tty.
A forestage adapter also ships, frozen as the reference implementation of the deep-integration contract rather than as a live target (forestage was retired 2026-07-31).
- Go 1.25+
- tmux
just(command runner)
