Skip to content

How-to: working with a running environment

Your environment is up. This page is about living with it day to day — attaching, detaching, inspecting, and parking it without losing work. For full flag detail see the command reference.

flowchart TD
    R[Running environment] -->|ffleet up SLUG| A[Attached to agent session]
    A -->|detach: Ctrl-B D| R
    R -->|ffleet goto SLUG| S[Shell in the worktree]
    S -->|exit| R
    R -->|ffleet ls / status / tail / path| I[Inspect: no state change]
    R -->|ffleet stop SLUG| P[Stopped: container gone, worktree+branch+metadata kept]
    P -->|ffleet up SLUG| A

Selecting an environment

Every environment command (up, remove, goto, stop, tail, status, path, continue) takes an optional SLUG. Omit it and Forge Fleet first tries to resolve the environment from your current directory; if that doesn't match, it opens an interactive picker:

  • On a capable terminal you get an arrow-key highlight bar — move with / and press Enter to choose. For ffleet up, the list ends with a "create a new environment" entry.
  • On a plain terminal (TERM=dumb, NO_COLOR, or no ANSI support) it degrades to a numbered 1) 2) 3) menu — type a number and press Enter.

When stdin isn't a terminal (a pipe or CI), no picker is shown: pass SLUG explicitly, or you'll get a "SLUG is required…" error.

Attach & detach

  • Attach: ffleet up SLUG drops you into the agent's session (when it's live). That's the same command you used to start it.
  • Detach: press tmux's Ctrl-B then D. This leaves the agent running — you're just stepping out. Re-attach later with ffleet up SLUG.
  • Peek without risk: ffleet up SLUG --peek attaches only if the agent is already live and never relaunches anything.

goto — a shell in the worktree (not the agent)

ffleet goto SLUG opens a shell in the environment's worktree directory — distinct from attaching to the agent. Use it to inspect files, run a command, or make a manual edit alongside the agent. Leave it with exit.

$ ffleet goto my-feature   # you're now in the worktree
$ git log --oneline -5
$ exit

Related: ffleet path SLUG just prints the worktree path, so you can cd "$(ffleet path my-feature)" from your own shell.

Environment metadata — where it lives

Each environment has a small metadata directory (session.json, container.git, claude.json) recording its live state — which container it maps to, the agent session to resume, and git bookkeeping. Forge Fleet reads it for ffleet ls / status and to resume your session on the next up.

New environments keep this outside the checkout, under ~/.forge-fleet/{project-id}/worktree-meta/{env-name}/. This keeps it outside the reach of even aggressive in-worktree cleanup — git clean -fdx, git stash --all — which can't delete it or orphan the container. You never need to tiptoe around a .forge-fleet/ directory in your worktree.

Older environments (created before this change) still keep the directory in-worktree at <worktree>/.forge-fleet/. There's no migration — they keep working. For those, deleting the directory by hand still orphans the environment, so use ffleet stop / ffleet remove to tear one down rather than removing files. Forge Fleet marks that in-worktree directory as ignored (via the worktree's info/exclude) so routine git stash -u / git clean -fd won't touch it, but git clean -fdx / git stash --all / rm -rf .forge-fleet still would.

Either way: to tear an environment down, use ffleet stop or ffleet remove — never delete metadata by hand.

Inspecting

None of these change state:

  • ffleet ls — all environments for the project (add --json for scripts).
  • ffleet status SLUG — live status: container up?, agent live/exited, blocked state.
  • ffleet tail SLUG — the last lines of the agent's transcript (--lines N, default 100).
  • ffleet path SLUG — the worktree/working-directory path.

Is the agent working, or waiting on you?

Forge Fleet watches the agent's terminal: if the screen is changing it's working; if it's sitting still at a prompt it's waiting for your input. Neither is an error — "waiting" just means the agent wants a reply. ls surfaces this in the BLOCKED_REASON column (- when nothing needs you; waiting for input when the agent is idle at a prompt):

$ ffleet ls
SHORT        ENV                     AGENT   CONTAINER               CONTAINER_STATE  RUNTIME  LAST_ACTIVITY              BLOCKED_REASON
-----------  ----------------------  ------  ----------------------  ---------------  -------  -------------------------  -----------------
add-logging  my-project-add-logging  claude  my-project-add-logging  running          running  2026-08-12T09:14:22+00:00  waiting for input
fix-auth     my-project-fix-auth     claude  my-project-fix-auth     running          running  2026-08-12T09:12:03+00:00  -

status shows the same thing per-environment, split into the semantic state and probe reason:

$ ffleet status add-logging
Environment status
  name: my-project-add-logging
  slug: add-logging
  branch: my-project-add-logging
  agent: claude
  runtime: running
  semantic: waiting for input
  probe reason: waiting for input
  container: my-project-add-logging
  session id: 6f1c…
  tmux session: main
  worktree: ~/.forge-fleet/my-project/worktrees/add-logging

stop — park it without losing work

ffleet stop SLUG removes the container but keeps the worktree, branch, and metadata. It's the safe "get this out of the way" move — reversible, unlike remove.

$ ffleet stop my-feature   # container gone, everything else preserved
$ ffleet up   my-feature   # revive: rebuilt from config, session resumed, no data loss

Because containers run with --rm, there's no lingering "stopped container" — a stop removes it, and up rebuilds a fresh one when you come back. Your work lives in the worktree and the saved session, not the container.

For Claude agents, session resume is exact — it reattaches this environment's own last session. For Codex agents it's best-effort: Codex resumes the most recently used session (filtered to sessions started from the same working directory) rather than one by ID. Since each environment's container workspace is mounted at its own host-identical path, this resolves correctly for concurrent environments in the common case even though they share --codex-dir by default — see the container-config note for the mechanism and the fallback if you ever hit a mismatch.

Working inside tmux

When you attach, you're inside a tmux session running the agent. You mostly just type at the agent, but a few tmux things are worth knowing:

  • Detach: Ctrl-B then D — leaves the agent running. The one key you must know.
  • Scroll back: the mouse wheel may not reach the agent's history. Enter copy-mode with Ctrl-B then [, scroll with arrows/PageUp, q to exit.
  • Copy / paste: tmux captures mouse selection, so a normal drag-select may not hit your clipboard. Hold Shift while selecting to use your terminal's own copy/paste (works in most terminals). Or copy within tmux via copy-mode (Ctrl-B [, select, Enter) and paste that buffer with Ctrl-B ].

New to tmux? See the tmux wiki or a shortcut cheat sheet.

Kept deliberately simple: one tmux session per container, a single pane (0). You can split extra panes yourself, but multiple tmux sessions — and attaching from inside another tmux (tmux-in-tmux) — are not supported.

Beyond tmux: driving Claude Code from the web

Attaching over tmux is Forge Fleet's built-in way to reach the agent, and it works for any agent. But if the agent is Claude Code, Claude Code can also expose its own session to the web — so you can watch and steer it from a browser or phone without ffleet up or tmux at all:

  • Remote Control — continue this session from another device via claude.ai/code. Execution stays inside the Forge Fleet container; you just reconnect to it from the web. This is the natural fit for detached/background work — start it here, keep an eye on it from anywhere.
  • Claude Code on the web — the fully hosted cousin, where sessions run on Anthropic-managed infra. Mentioned for completeness; it's a different execution model than Forge Fleet's containers.

Remote Control is a Claude Code feature, configured inside the agent (not by Forge Fleet), and has no equivalent for other agents such as Codex. Forge Fleet's tmux attach/detach above keeps working regardless.

Forge Fleet does name each Claude session after its environment (<project>-<slug>) and gives the container a matching hostname, so entries in Remote Control and the /resume picker are identifiable at a glance.

What can and can't change mid-life

When ffleet up rebuilds a stopped/broken environment, it refreshes config-derived settings from your current ffleet.toml/flags and saves them back. But identity/continuity settings are fixed at create time — to change those you must remove and recreate.

Refreshed on the next up (revive-time-mutable) Fixed at create (need remove to change)
image branch
extra_mounts worktree
extra_hosts in-place vs worktree mode
docker_env_file git-mode
credential auth coding agent
credential dirs (~/.claude / ~/.codex) the session
agent cmd / extra_args

So: to pick up a new image, an added mount, or a changed env file, just ffleet stop then ffleet up. To change the branch, git-mode, or which agent runs, ffleet remove and start fresh. This is the practical face of config precedence.

Next

Done with the work? Completing / cleanup.