CLI reference
envctl runs against the Docker host in front of you. It derives the environment from the git worktree of the current directory and prints human output by default; add --json for a single JSON document on stdout. Compose progress always goes to stderr.
Global flags
Section titled “Global flags”envctl [-C PATH] [--env NAME] [--backend local] [--port-mode domains|registry] [--json] <command>-C PATH operates on the worktree containing PATH instead of the current directory. --env NAME names the environment instead of deriving it from the branch; the value is slugified. --feature is a deprecated alias. --backend accepts local today. --port-mode overrides ports.mode from the manifest for this invocation. --json switches output to JSON on every command that reports something.
Experimental workflow selection
Section titled “Experimental workflow selection”envctl run workflowsenvctl run create --task "Add a --json flag to envctl version" --workflow smallenvctl run validate --workflow smallrun workflows lists the default workflow and each named workflow in envctl.yaml with its stages; --json returns the same list. run create --workflow and run validate --workflow select one by name, and an unknown name lists the choices. In the dashboard’s new-run input (n), tab and shift+tab cycle the workflow. run show and the dashboard name each run’s workflow. See Workflows.
Experimental workflow review
Section titled “Experimental workflow review”Version 2 workflow state belongs to its persistent coordinator. These commands inspect retained evidence without starting a worker or requiring the run’s VM:
envctl run diff <run-id> --node codeenvctl run diff <run-id> --node qa --revision <revision-id> --from <checkpoint-id> --jsonenvctl run artifact <digest> --output <new-file>diff defaults to comparing the selected checkpoint against its revision’s immutable source pins. --from selects any recorded checkpoint in the same run, including historical revisions. The response includes source patches, checkpoint summaries, checks, approvals, and artifact/dataset identities. Repository patches are bounded to 1 MiB each and explicitly report truncation or unavailable retained source. Binary files and LFS objects are represented by Git’s binary/pointer differences; database rows are not diffed. artifact verifies the transferred checksum and refuses to overwrite existing output.
In Bubble Tea, d opens the comparison. b selects the current checkpoint as the comparison base; navigate to the target and press d. B resets the base, and Escape closes the loaded review. Selection and review never mutate the workflow.
Experimental live progress and steering
Section titled “Experimental live progress and steering”envctl run show <run-id>envctl run message <run-id> --node code --to worker --text "keep the public API unchanged"run show prints each running attempt’s phase (worker, stack, checks, supervisor), its current check, and its latest agent activity: messages, tool and command invocations, and check output. --json includes the same progress object on each attempt, with at most 20 lines of 300 bytes each. Activity comes from the harness’s own event stream, redacted by the guest journal and again for configured credentials. It is display state only and never checkpoint evidence. Activity-only updates are written at most every few seconds; phase changes and deliveries are written at once.
run message (and i in Bubble Tea) records a message against the current revision. --node limits it to one stage; without it, the message applies to every stage. run show and the Bubble Tea Conversation panel show where each message went:
- included when … started: the message existed when the agent’s prompt was frozen.
- delivered live … (resume N): the message reached a running agent.
- pending live delivery: a matching attempt is running, and the message goes out on the coordinator’s next poll.
- queued for the next … attempt: nothing matching is running.
Neither harness accepts input mid-run, so live delivery interrupts and resumes. The coordinator durably records a new generation of the agent’s invocation, stops the running guest job, and resumes the same explicit harness session in the same worktree. The resumed prompt contains the new messages. Edits already made stay in place. A worker that finished before the message arrived is resumed too: its proposal is superseded and never accepted without the message. Delivery waits until the harness reports its session ID. A coordinator restart at any step reconciles from the receipt without a duplicate job.
Worker-directed messages reach the running worker. The supervisor’s prompt includes every message for its stage, with an instruction to reject work that ignores steering addressed to the worker. Messages that arrive while the supervisor is reviewing interrupt and resume the supervisor the same way. Each role allows 8 live resumes per attempt. After that, progress reports the limit, and further messages reach the supervisor review or the next attempt. Messages that arrive after an attempt’s review has finished apply to that stage’s next attempt, or to a rewind.
Pull requests
Section titled “Pull requests”envctl run pr <run> # print the pull requests the run publishedenvctl run pr <run> --open # open them in the browserIn the dashboard, g opens the selected run’s pull requests from any stage. Changes that also published submodule pull requests list each one. --json prints [{"key", "url"}]; before publication the command exits 1 with “no pull request yet”.
Web dashboard
Section titled “Web dashboard”envctl web # serve on 127.0.0.1:4777 and open the browserenvctl web --addr 127.0.0.1:0 --no-open # any free port; print the link onlyenvctl web serves the workflow dashboard to a browser on this machine. Runs are listed by what they need from you. Each run opens with one sentence saying what it needs next and the action for it, then its stages. For the selected stage:
- Chat: the worker’s and supervisor’s results, live activity, and your messages. Write to the supervisor or worker from the box below.
- Result: the summary, supervisor review, checks, documents (opened in a reading pane) and commits. A result waiting for you is approved here.
- Changes: the source diff against where the revision started or any earlier checkpoint.
- Log: the run’s decisions across every revision: rewinds, rejections, failures, approvals, messages and pull requests. Pick a revision to browse it read-only.
The side panel shows token use, the VM and services, readiness checks, the stage’s models and limits, and plugins. You can start runs with a workflow, approve, rewind, cancel, set priority and open pull requests. Keyboard shortcuts follow envctl ui (1 to 4 switch views); press ? for the list. Like the terminal dashboard it is only a client, so stopping it never stops a run.
The dashboard can approve and publish work, so it is locked to this machine:
- It listens only on a loopback address.
--addrrefuses anything else. - The printed link carries a token from
web-tokenin the state directory (created once, mode 0600). Opening it sets a same-site cookie; other visits need the link. - Requests with another Host header are refused, which blocks DNS rebinding. Changes also require the page’s token header, JSON, and a same-origin request.
- The page loads nothing from the network, and its content security policy allows only its own scripts. Artifacts render as Markdown without raw HTML.
- Every change carries the run version and revision the page showed, so it never applies to state you did not see.
Themes: the page follows the system light or dark setting, and the theme menu also offers the terminal dashboard’s color themes. The choice is saved in the browser.
Dashboard themes
Section titled “Dashboard themes”envctl ui --theme nord # this session onlyenvctl theme list # built-in themes with color swatches; ▸ marks the current oneenvctl theme set gruvbox # save to ~/.config/envctl/config.yamlenvctl theme show # explain the resolved theme, config path, and each color's sourceenvctl theme show nord --dark # preview a theme as if the terminal were darkIn the dashboard, press T to open the theme picker. Moving the selection previews each theme immediately; enter keeps and saves it, esc restores the previous theme. envctl theme list --json returns every palette’s colors, and envctl theme show [name] [--json] reports the resolved theme, the configuration file path, and each palette role’s value and source (a built-in theme, theme.custom.<role>, disabled by theme.background: false, or the terminal default). See Dashboard themes for the configuration file.
Environment lifecycle
Section titled “Environment lifecycle”envctl up [--build] [--no-wait]envctl down [-v|--volumes]envctl stopenvctl startup renders the isolated compose file, then runs docker compose up --detach --remove-orphans, and prints status. It converges an existing environment rather than failing. --build rebuilds images first. By default up waits for health checks; --no-wait returns as soon as containers are started, which the worktree hook uses so agents are not blocked by slow services.
down removes containers and the network and keeps volumes, so up restores the data. down -v also deletes volumes, releases this project’s port allocations, and removes .envctl/<feature>/. down renders on demand if nothing has been rendered yet, so it works on a fresh clone.
stop and start map to compose stop and start: containers stay, data stays, nothing is listening in between.
Start a rebuilt environment and print JSON for a script:
envctl up --build --json | jq '.endpoints'Named environments
Section titled “Named environments”envctl env create <name> [--branch BRANCH] [--no-up]envctl env link <name> <branch>envctl env unlink <name>envctl env listenvctl env rm <name>An environment named after the branch is created implicitly by up and linked to that branch. env create makes a named environment that is kept: CI never destroys it on merge, only env rm does. --branch links it so pushes to that branch update it; without --branch it is unlinked. --no-up records and renders without starting containers.
env link re-points an environment at another branch. env unlink keeps the environment but stops it following any branch. env list is the same as list. env rm is down --volumes plus removal of the record.
Locally the code always comes from the worktree you run in; the linked branch decides which pushes update the environment in CI. See the environment identity design note.
Inspection
Section titled “Inspection”envctl statusenvctl listenvctl renderstatus reads the rendered files and asks compose which containers exist. It prints the feature, project, backend, each service with its state and health, endpoints, and the env file path. In JSON the env array holds the env file lines. status requires a prior up or render.
list merges the environments recorded in this worktree (.envctl/*/env.json) with the compose projects on the host whose name starts with this manifest’s prefix. Each row has the name, backend, linked branch, whether it is kept, and compose’s status summary or - when it is not running here.
render writes .envctl/<feature>/compose.yaml and env without starting anything, and prints the same status shape without container state. Use it to inspect what a port mode did:
envctl --port-mode registry renderdocker compose -p mg-feat-thing -f .envctl/feat-thing/compose.yaml configWorking inside the environment
Section titled “Working inside the environment”envctl logs [-f|--follow] [service...]envctl exec <service> -- <command...>logs streams compose logs for the whole environment or the named services. exec runs a command in a running service container with a TTY when stdin is a terminal:
envctl exec postgres -- psql -U metergraph -c 'select now()'Repository setup
Section titled “Repository setup”envctl init --project PREFIX [--file PATH ...]envctl hook claudeinit writes a starter envctl.yaml in the target directory and refuses to overwrite one. --project is required; --file may repeat and defaults to docker-compose.yml.
hook claude prints the hooks block to merge into .claude/settings.json. See Agents.
Doctor
Section titled “Doctor”envctl doctor [--json]Checks whether this machine is ready to use envctl and prints an exact one-line fix for every problem. Each check reports ok, warning, or failed:
- docker / docker-compose — Docker must be reachable and
docker compose version --shortmust report major version 2 or newer; needed for every command.failedif either is missing. - git — must be on
PATH;failedif missing. - lima — Lima 2.x, needed only to run workflow revisions;
warning(notfailed) if absent or on an older major version. - gh — the GitHub CLI, authenticated (
gh auth status), needed only for pull-request/publication output;warningif absent or unauthenticated. - envctl.yaml — whether the current worktree has a manifest at its root and whether it parses, reporting the true on-disk version (1 or 2). Not being in a git repository, or the manifest being absent, is a
warning; a manifest that fails to parse isfailed. - agents.worker / agents.supervisor — for a version 2 manifest only, whether each configured agent credential resolves (Claude or Codex). Omitted entirely for a version 1 manifest. Credential values are never printed, only whether resolution succeeded and, on failure, a fix.
- xcode-clt — on macOS only (omitted elsewhere), whether the Xcode Command Line Tools are installed and current enough for Homebrew to accept them: a Command Line Tools major version older than the running macOS major version is
failed, since Homebrew refuses to install anything in that state, even thoughxcode-selectreports the tools present.
Every check runs with a short timeout, so a hung external tool cannot hang doctor itself; a timeout is scored at that check’s own severity. --json prints the same checks as an array of {name, status, detail, fix} objects, omitting fix when the status is ok. Exit code follows the Exit codes convention: 0 unless at least one check failed; warnings alone keep exit code 0.
Agent skill
Section titled “Agent skill”envctl agent install [--global]envctl agent snippetagent install copies the embedded skill (SKILL.md and REFERENCE.md) into .claude/skills/envctl/ and .agents/skills/envctl/ at the worktree root, then prints the CLAUDE.md paragraph. --global writes to ~/.claude/skills, ~/.agents/skills, and ~/.codex/skills instead. agent snippet prints only the paragraph. See Agents.
Exit codes
Section titled “Exit codes”0 on success. 1 on any failure, with a one-line reason on stderr prefixed envctl:. Compose’s own exit code is passed through when compose fails.
Feature names
Section titled “Feature names”Branch names are lowercased; runs of characters outside a-z0-9 become one -; leading and trailing - are trimmed; the result is cut to 40 characters. feat/Imported Pricing becomes feat-imported-pricing. A detached HEAD uses the worktree directory name. An empty result becomes default.
envctl run readiness <run> includes required capability names, structured discovered Plan requirements (capability, affected nodes, reason), current probes, and unresolved admission items. A reviewed Plan can remain pending while one of its discovered connections is prepared; its worker result remains retained.
Local MCP interface
Section titled “Local MCP interface”envctl mcp serve serves newline-delimited MCP over stdin/stdout and connects to the same persistent coordinator as CLI/TUI. Use --run <id> to restrict the server to one run and disable creation, or --read-only to expose only inspection tools. Closing the MCP client leaves the coordinator running.
Available tools are envctl_runs, envctl_run, envctl_events, envctl_diff, envctl_artifact, envctl_create, and envctl_action. Mutations require a caller-generated operation_id; actions also require the exact observed run version and revision. Reuse the identical request after a lost response. Stale mutations fail instead of silently rebasing onto current state. Agent actions cover steering, rewind, cancel, priority, and invocation plugin attachment/removal. Approval and publication remain outside this tool surface.
Artifact previews require a reference from the selected run, return base64, and default to 64 KiB (maximum 1 MiB), with explicit size/truncation metadata. MCP is a local stdio interface for trusted host clients; run scoping does not create a network authentication service or expose the host coordinator inside a worker VM.
Example MCP client configuration (replace the binary path and run ID):
{ "mcpServers": { "envctl": { "command": "/absolute/path/to/envctl", "args": ["mcp", "serve", "--run", "run_example", "--read-only"] } }}The implementation pins the official MCP Go SDK at v1.7.0 and uses its stdio server/client transport.