How-to: completing & cleanup
When a piece of work is merged or abandoned, tear the environment down. When
you're done with a whole project — or want to uninstall — clean up the
~/.forge-fleet registry. For full flags see the
command reference.
flowchart TD
E[Environment done] -->|ffleet remove SLUG| R[Worktree deleted; ffleet-created branch deleted]
E -->|ffleet remove --done| S[Tick the landed ones; all removed in one pass]
S --> D
R --> D{More environments in this project?}
D -->|yes| E
D -->|no| P[Project-level cleanup]
P -->|ffleet projects ls| L[See every known project and its environments]
P -->|ffleet projects prune| PR[Drop stale entries + their ~/.forge-fleet home dirs]
P -->|ffleet projects rm ID| RM[Delete one project's ~/.forge-fleet/ID dir + registry entry]
Get your work out first
Forge Fleet manages environments, not your git workflow: it never pushes
your branch or opens a PR for you. Landing the work is your job — or the
agent's, from inside the container — and you must do it before remove,
which deletes the branch Forge Fleet created.
Find the branch. A worktree environment lives on a branch named
<project>-<slug> (the same name as its container). Confirm it with status:
$ ffleet status my-feature
...
branch: my-project-my-feature
worktree: ~/.forge-fleet/my-project/worktrees/my-feature
Push / open a PR — either ask the agent ("commit, push, and open a PR"), or do it yourself from the worktree:
$ ffleet goto my-feature
$ git push -u origin my-project-my-feature
$ gh pr create
$ exit
Push credentials are not provided by default. The container knows your
origin URL but has no push auth — no SSH key, no gh/git token, no host
git config. It does speak HTTPS to your remote's host
(git_https_rewrite, on by default),
so all you supply is a token: create a fine-grained personal access token
scoped to this one repository and pass it through
docker_env_file:
# docker.env
GITHUB_TOKEN=github_pat_…
GH_TOKEN=github_pat_…
Prefer that to mounting ~/.ssh: a mounted key is your real, long-lived
credential, valid for every repository it can reach and for as long as the
environment exists. Your commit identity (name/email) is forwarded
automatically from [git] or your host git config — only push credentials need
this. Prefer to stay fully local?
git_local_main = true bases the branch on your local main so you can merge
locally with no push at all.
Once credentials are in place, the agent can push on its own. If you want it
to commit but never publish, set
git_allow_origin_writes = false: it
denies git push, gh pr create, gh pr merge, gh release create,
gh workflow run and the common spellings of a mutating gh api call at the
agent's own permission layer. Matching is by command prefix, so the coverage has
edges worth reading before you rely on it — see the
caveats. It pairs naturally
with git_local_main but is independent of it — neither implies the other, so
set both if you want a fully local-only environment.
Forge Fleet sandboxes your setup — it doesn't replace it. The real leverage is still your own prompts, your skills/subagents, and templates that wire an issue straight into a seeded session. Forge Fleet's job is to run all of that inside an isolated, disposable container — so you can let an agent work autonomously without it touching your host. It's the safe sandbox around your workflow, not a substitute for it.
Remove one environment
ffleet remove SLUG tears the environment down: it stops the container and
deletes the worktree. If Forge Fleet created the branch, that branch is
deleted too; a branch you brought in with --from is left alone.
$ ffleet remove my-feature
$ ffleet remove my-feature -y # skip the confirmation prompt
remove surfaces unpushed/uncommitted work before deleting so you don't lose it
by surprise. This is irreversible — if you only want the container gone,
use ffleet stop instead.
Before stopping the container, remove briefly asks the agent to exit on its
own (/exit for Claude Code, /quit for Codex), waiting a few seconds at most.
For Claude Code this is what makes it archive the session shown on
claude.ai/code — otherwise you'd have to archive it by
hand. stop deliberately skips this: a stopped environment is meant to come
back, so its session is left active rather than archived.
Plain remove makes no network call at all: it is deliberately fast and
offline-safe.
See which environments have landed
ffleet ls has a PR column. For each environment it shows what the pull
requests whose head branch is that environment's branch say about it:
PR shows |
What it means |
|---|---|
merged #4 |
the work landed |
merged #4,#7 |
it landed as a stack of pull requests |
open #9 |
still in review |
— |
nothing known: no pull request on that branch, only closed ones, or the lookup could not run |
The match is on the branch, which is a first-class GitHub field — so it still
works after the branch is deleted on merge. If the agent opened its pull request
from a sub-branch instead of the environment's branch, the column reads —:
the environment is shown, just never swept.
The lookup needs the gh CLI, and gh owns its own
auth. Forge Fleet never handles a token, a key or any credential for this —
run gh auth login once and that is the whole setup. gh is optional: with
it missing, unauthenticated, or pointed at a repo with no origin, the column
simply reads — and everything else works exactly as before — no error, no
warning, same exit code. Projects on
pm = "linear" or pm = "none" leave the column blank
and never invoke gh at all.
Sweep the landed environments (remove --done)
When several environments have finished, ffleet remove --done collapses N
teardowns into one reviewed action:
$ ffleet remove --done
[forge-fleet] Refreshing remote refs (git fetch --prune)
Remove these landed environments:
[x] fix-login [my-project-fix-login] merged #4
[x] add-export [my-project-add-export] merged #7,#9
[ ] flaky-test [my-project-flaky-test] merged #11 — unsaved work: uncommitted changes
Tick what you want gone and press enter. The checkmark is the approval — there is no second y/n prompt. Then a tally:
fix-login: removed
add-export: removed
flaky-test: skipped (uncommitted changes)
removed 2, skipped 1, failed 0
Two independent things must both be true before an environment is offered:
- it landed — at least one merged pull request on its branch, and no open one;
- nothing would be lost — the same unsaved-work check plain
removeruns.
A branch that is pushed but unreviewed is safe to delete yet not done; a merged branch with uncommitted changes is done yet not safe. An environment failing the second check is listed with its reason but never pre-ticked — you can still tick it deliberately. Every ambiguity resolves to "not swept": the worst case is that you clean up by hand, exactly as before.
Notes:
- The sweep starts with one visible
git fetch --prune, because the unsaved-work check reads your remote-tracking refs and is otherwise only as fresh as your last fetch. This is the only fetch in the teardown path; plainremovenever fetches. A failed fetch is a warning, not a stop — the check just stays conservative. - A landed branch is not flagged just because its commits were squashed away: the check asks whether the default branch already contains the branch's content, not whether the commits themselves are there. Uncommitted changes and untracked files are still flagged on such a branch.
--donetakes noSLUG. It sweeps, or it removes one — not both.ffleet remove --done --yesskips the picker and removes every environment that is landed and clean. On a non-interactive stdin (CI, a pipe)--yesis required — without it the command errors rather than acting silently.- Nothing is ever removed unattended: there is no auto-removal on any trigger.
The project registry (~/.forge-fleet)
Forge Fleet tracks the projects it knows about in
~/.forge-fleet/registry.json, with each project's home data under
~/.forge-fleet/{id}/. The ffleet projects commands manage that registry —
useful for multi-project cleanup and uninstalling.
ffleet projects ls— every known project with its environments grouped under it (add--json). Start here: each header carries the project's registry id, which is whatprojects rmtakes. Bareffleet projectsdoes the same.
$ ffleet projects ls
project: my-project [my-project-1a2b3c4d] /repos/my-project
ffleet projects prune— remove stale registry entries and delete their~/.forge-fleethome directories. Good housekeeping after you've deleted repos. Prompts first;-yto skip.
$ ffleet projects prune
ffleet projects rm PROJECT_ID— permanently delete one project's~/.forge-fleet/{id}directory and its registry entry. Prompts first;-yto skip.
$ ffleet projects rm my-project-1a2b3c4d
Uninstalling Forge Fleet
To leave no trace:
ffleet removeevery remaining environment (orffleet lsper project to find them).ffleet projects prune, orffleet projects rm IDfor each project, to clear~/.forge-fleet.- Delete the
ffleetbinary from yourPATH.
Removing environments deletes worktrees and ffleet-created branches — double-check nothing is unpushed first.