Skip to content

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|codex picks it for this environment.
  • default in ffleet.toml sets the project's choice, so you don't pass the flag every time. It must name one of the [claude] / [codex] sections.
  • CODING_AGENT is 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].auth table.
  • 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 --effort flag, Codex's model_reasoning_effort config override.
  • cmd and extra_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_args will 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.
  • -n is 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 your extra_args already carry -n, --name or --name=…, ffleet leaves the name alone and adds nothing.
  • extra_args wins over model / reasoning. Those two come first, so a --model you write in extra_args yourself 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:

  1. looks up NAME in ~/.codex/agents/*.toml (personal) and .codex/agents/*.toml in the repo (project) — matched on the file's name field, not the filename; a duplicate or missing name is a fail-fast error, not a silent fallback;
  2. 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.