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 pressEnterto choose. Forffleet 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 numbered1) 2) 3)menu — type a number and pressEnter.
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 SLUGdrops 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-BthenD. This leaves the agent running — you're just stepping out. Re-attach later withffleet up SLUG. - Peek without risk:
ffleet up SLUG --peekattaches 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--jsonfor 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-BthenD— 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-Bthen[, scroll with arrows/PageUp,qto exit. - Copy / paste: tmux captures mouse selection, so a normal drag-select may
not hit your clipboard. Hold
Shiftwhile 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 withCtrl-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.