Controlling the coding assistant
Forge Fleet launches a coding assistant inside each environment's container.
Which one it launches, which binary it invokes, and which flags that binary gets
are all yours to shape — this page covers the knobs (--coding-agent, cmd,
model, reasoning, extra_args) plus subagent selection.
Which assistant runs
Forge Fleet drives Claude Code and OpenAI Codex.
$ ffleet up my-feature --coding-agent codex -p "…"
--coding-agent claude|codexpicks it for this environment.defaultinffleet.tomlsets the project's choice, so you don't pass the flag every time. It must name one of the[claude]/[codex]sections.CODING_AGENTis the environment-variable equivalent.- With none of those set, ffleet launches Claude.
The choice is fixed when the environment is created. ffleet up on an
existing environment ignores a different --coding-agent — switching assistants
means ffleet remove SLUG and starting again.
Per-assistant configuration
Each assistant has its own table in ffleet.toml:
default = "claude"
[claude]
dir = "~/.claude"
auth = "auto"
model = "opus"
reasoning = "high"
[codex]
dir = "~/.codex"
model = "gpt-5"
reasoning = "medium"
dir— the host directory mounted in as the assistant's config and credentials (default~/.claude/~/.codex), which is why you don't re-authenticate per environment. See container configuration.auth(Claude only) — which credential ffleet injects. See the[claude].authtable.model— the model the assistant launches with. Omit it and the assistant picks its own default.reasoning— how hard the assistant thinks by default. The same key works on both assistants; ffleet maps it onto each one's own mechanism — Claude's--effortflag, Codex'smodel_reasoning_effortconfig override.cmdandextra_args— the launch command itself, below.
Both model and reasoning are opaque strings: ffleet passes them straight
to the binary without checking them against a list of its own, so a value your
assistant version accepts works the day it ships. Neither has a CLI flag or an
environment variable — like cmd and extra_args, they are a per-project
choice.
cmd — override the binary
By default ffleet invokes the binary named after the section: claude or
codex. cmd replaces it, which is how you run a wrapper script, a pinned
version, or a binary that isn't on PATH under the expected name:
[claude]
cmd = "/usr/local/bin/claude-1.2.3"
The binary has to exist inside the container, not on your host — so a custom
cmd usually goes together with a custom image.
extra_args — your own flags
extra_args is a list of arguments handed to the assistant at launch. Use it for
anything ffleet has no setting of its own for:
[claude]
dir = "~/.claude"
extra_args = [
"--autocompact", "300k",
"--permission-mode", "auto",
"--append-system-prompt", "=== Docker here uses the HOST daemon (docker-outside-of-docker) ===",
]
[codex]
dir = "~/.codex"
extra_args = ["--search"]
Each element is one argv entry, so a flag and its value are two separate
strings — ["--autocompact", "300k"], never ["--autocompact 300k"].
cmd and extra_args live in ffleet.toml only — there is no CLI flag and no
environment variable for either, so they are a per-project choice rather than a
per-run one. For the model and reasoning effort, prefer the dedicated keys
above over hand-written flags here.
Where your flags land in the command
Your extra_args go immediately after the binary, before every flag ffleet
adds for itself — with model and reasoning, when set, just ahead of them.
For Claude the assembled command is:
<cmd> [--model <model>] [--effort <reasoning>] <extra_args…> --dangerously-skip-permissions -n <env-name> --session-id <uuid> [prompt]
and on a revive --session-id <uuid> becomes --resume <uuid> — unless ffleet
finds no transcript for that session on the host, in which case it keeps
--session-id <uuid> and the session starts fresh under the same id. Codex
follows the same shape, with reasoning as a config override rather than a
flag:
<cmd> [--model <model>] [-c model_reasoning_effort=<reasoning>] <extra_args…> -c check_for_update_on_startup=false …
Codex always stays interactive. After the SQLite override, the remaining
arguments are --dangerously-bypass-approvals-and-sandbox, followed by
resume --last --include-non-interactive when reviving, and -- <prompt>
when a prompt is supplied. The resume flag includes conversations created by
older Forge Fleet versions using codex exec.
Three consequences follow from that order:
- ffleet's own flags win a last-one-wins duel. For a flag where the last
occurrence takes effect, putting the same flag in
extra_argswill not override ffleet's value. The flags ffleet sets are the ones it needs to run the assistant unattended and to resume the right session, so this is deliberate. -nis the one exception. ffleet names the Claude session after the environment so/resume, the terminal title and the Remote Control list identify it — but if yourextra_argsalready carry-n,--nameor--name=…, ffleet leaves the name alone and adds nothing.extra_argswins overmodel/reasoning. Those two come first, so a--modelyou write inextra_argsyourself is the later occurrence and takes effect. Set the key, or write the flag by hand — not both.
--append-system-prompt is merged rather than duplicated: when ffleet has
environment notes to add (see
[claude].extend_system_prompt)
they end up in a single flag with ffleet's text first and yours last.
Subagents
A subagent is a named agent definition the assistant itself resolves — a
reviewer, a test-writer, a domain specialist. Forge Fleet can launch an
environment straight into one, on both claude and codex, though the two
work quite differently under the hood:
$ ffleet up my-feature --subagent code-reviewer -p "Review the diff on this branch"
A template can carry the same choice, so environments seeded from it always run the right subagent:
[templates.review]
prompt_template = "Review issue #{number}: {title}"
subagent = "code-reviewer"
--subagent works with or without -t/--template, and when both are set the
flag wins — ffleet warns when the two disagree. See
templates for the rest of the template machinery.
--subagent is a create-time flag on both agents: on an ffleet up that
revives an existing environment it is ignored outright.
Claude — a deterministic flag
--subagent NAME is translated into Claude's own --agent NAME and appended to
extra_args, so it reaches the binary exactly as if you had written it there
yourself. That flag is what carries the choice forward — the subagent has no
stored setting of its own.
That has a consequence worth planning for: extra_args is re-read from your
config on every revive, and the saved list is used only when your config
supplies no extra_args at all. So an environment created with
--subagent in a project that also sets [claude].extra_args loses its
subagent on the next stop + up — the configured list replaces the saved one
rather than merging with it. If you need both, write the pair into your own
extra_args and drop the flag:
[claude]
extra_args = ["--agent", "code-reviewer"]
Codex — a best-effort delegation instruction
The Codex CLI has no --agent-equivalent flag, so ffleet cannot make Codex
adopt a subagent the way it can Claude. Instead, at create time only, ffleet:
- looks up
NAMEin~/.codex/agents/*.toml(personal) and.codex/agents/*.tomlin the repo (project) — matched on the file'snamefield, not the filename; a duplicate or missing name is a fail-fast error, not a silent fallback; - weaves a delegation instruction naming that agent into the launch prompt
(
-p "…"), or, for an interactive launch with no prompt, sends the same instruction into the pane right after Codex comes up.
Codex decides for itself whether to follow that instruction — there is no
enforcement — so treat --subagent on Codex as a strong hint, not a
guarantee the way it is on Claude. Nothing is written into extra_args or
saved with the environment, so there is nothing to carry forward on revive
even in principle: it applies once, at the moment of creation.
What a stop + up picks up
cmd, model, reasoning and extra_args are config-derived: they are
re-read from your config on every ffleet up and saved back, so changing them
takes effect on the next revive.
$ ffleet stop my-feature
$ ffleet up my-feature # relaunches with the new cmd / model / reasoning / extra_args
The section re-read is the environment's own assistant's — [claude] for an
environment running Claude, [codex] for one running Codex — regardless of what
--coding-agent, CODING_AGENT or your project default say when you type
ffleet up. For an environment whose assistant differs from that default, its
own section is authoritative: ffleet prints a warning, applies that section, and
replaces whatever cmd/extra_args were saved with the environment — so if
your config configures none for it, it revives with none.
The assistant is an identity setting instead — pinned when the environment
is created, changeable only by removing it and starting fresh. The subagent is
never stored separately either, but the two agents get there differently: on
Claude it rides along inside extra_args, so it follows the config-derived
rule above; on Codex it isn't stored at all — it's a one-time instruction
applied only at creation (see the subagents section). See
config precedence for the full split.
Related
- Starting an environment — the other
ffleet upchoices. - Templates — seeding a prompt and subagent from an issue.
- Config reference — every
[claude]/[codex]key.