Skip to content
All Docs pages

Sessions

Terminals and run scripts

Named scripts per repository, a real shell in the worktree, and a deterministic block of ten ports per session so four dev servers do not collide.


An agent that cannot run your dev server cannot tell whether its change works. Fleet gives each session a place to run things and a set of ports that are actually its own.

Run scripts

A repository declares named scripts: dev, test, storybook. Each has a command, arguments, a working directory relative to the worktree, an icon, and one is the default that the Run control fires without opening its menu.

Scripts run concurrently by default. A repository can instead say non-concurrent, so starting one stops the others, which is what you want for anything that binds a port.

Two lifecycle scripts sit alongside them and stream into the same pane:

  • Setup runs once, after the worktree exists and before the agent starts.
  • Archive runs before the worktree is torn down, to clean up anything living outside it. It is time limited, because teardown must not hang on it.

Ports

Each session gets a deterministic block of ten consecutive ports from the range 3100 to 3999.

Deterministic matters: the block is derived from the session's identity rather than handed out by a counter, so the same session lands on the same block after a restart and a bookmarked localhost:3140 keeps working. Blocks are probed a whole block at a time, because a block starting one port into another session's would hand out ports two sessions both believed they owned.

The range sits above 3000 so Fleet's blocks do not fight whatever you started by hand.

Your scripts and the agent both see the block through the environment:

FLEET_PORT     the base, what a dev server should bind
FLEET_PORT1    the rest of the block
FLEET_PORT2
...
FLEET_PORT9

Ten per session rather than one, so a project can serve an app, an API and a database from a single block without inventing its own offsets.

Everything else in the environment

Every child Fleet starts in a worktree, including the agent process, sees the same variables, so npm run dev and the agent writing a .env agree about the port:

FLEET=1
FLEET_SESSION_ID
FLEET_WORKSPACE     the session title
FLEET_WORKTREE
FLEET_REPO
FLEET_BRANCH

A repository can add its own variables too. The order is fixed and it matters: the inherited environment first, then the repository's variables, then the FLEET_* names last. A repository that declared FLEET_PORT itself would otherwise break every script that reads it, and the failure would look like a port collision rather than a settings mistake.

The terminal

A session also gets a real shell in its worktree, with escape sequences intact so anything that draws a progress bar draws one.

The run log is the opposite treatment of the same bytes: it strips ANSI, because it is a list of lines and the escapes would be noise. Both are right for what they are.

Stopping things properly

A run child is spawned into its own process group and stopped by signalling the whole group. Signalling the process alone would reach npm and leave the vite it forked still running and still holding the port, so the next start collides with a process nothing in the interface can point at.

Terminals hold their worktree open, so every shell is killed before a worktree is removed, alongside the run scripts.