diff --git a/packages/website/src/routes/docs/worktrees.tsx b/packages/website/src/routes/docs/worktrees.tsx index 39745339d..696a30b6c 100644 --- a/packages/website/src/routes/docs/worktrees.tsx +++ b/packages/website/src/routes/docs/worktrees.tsx @@ -5,7 +5,7 @@ export const Route = createFileRoute("/docs/worktrees")({ head: () => ({ meta: pageMeta( "Git Worktrees - Paseo Docs", - "Run agents in isolated git worktrees for parallel feature development.", + "Run agents in isolated git worktrees with setup hooks, scripts, and long-running services.", ), }), component: Worktrees, @@ -25,285 +25,236 @@ function Worktrees() {

Git Worktrees

- Git worktrees let you have multiple working directories from the same repository. Paseo - uses them to run agents in isolated branches without switching contexts. + Each agent runs in its own git worktree — a separate directory on a separate branch — so + parallel agents never step on each other. You configure setup, scripts, and long-running + services through a paseo.json file at your repo root.

- {/* Why worktrees */} + {/* Layout & workflow */}
-

Why worktrees?

+

Layout and workflow

- Without worktrees, running multiple agents on the same repo means they share the working - directory. One agent's changes interfere with another's. You can't safely run parallel - tasks. -

-

- With worktrees, each agent gets its own directory and branch. They can work simultaneously - without conflict. When an agent finishes, you review the diff, merge the branch, and - archive the worktree. -

-
- - {/* Directory structure */} -
-

Directory structure

-

- Paseo creates worktrees under $PASEO_HOME/worktrees/, - organized by a short hash of the source checkout path: + Worktrees live under $PASEO_HOME/worktrees/, grouped by + a hash of the source checkout path. Each worktree gets a random slug; the branch name is + chosen when you first launch an agent.

{`~/.paseo/worktrees/
-├── 1vnnm9k3/
-│   ├── tidy-fox/            # random slug
-│   └── bold-owl/            # random slug
-└── 4k8q2d1p/
-    └── swift-hare/          # random slug`}
+└── 1vnnm9k3/ # hash of source checkout path + ├── tidy-fox/ # worktree slug (branch set on first agent) + └── bold-owl/`}
-

- The hash avoids collisions between repositories that share the same directory or remote - name. Worktree directory names are random slugs — the branch name is separate and chosen - when you first launch an agent in the worktree. -

-
- - {/* Branches */} -
-

Branches

-

- When you create a worktree, Paseo generates a random directory name. The branch name is - set when you first launch an agent — Paseo generates one automatically. -

-

- This means the worktree directory and branch are independent. You can rename the branch - later without affecting the worktree path. -

-
- - {/* Multiple agents */} -
-

Multiple agents per worktree

-

- You can launch multiple agents into the same worktree. They share the working directory - and branch, which is useful when you want agents to collaborate on the same feature or - when one agent hands off to another. -

-

- Be mindful of conflicts — agents working on the same files simultaneously can step on each - other. This works best when agents have distinct responsibilities or run sequentially. -

+
    +
  1. Create a worktree — Paseo runs your setup hooks
  2. +
  3. Launch an agent — a branch is created or assigned
  4. +
  5. Review the diff against the base branch
  6. +
  7. Merge or archive — archive runs teardown and removes the directory
  8. +
{/* paseo.json */}
-

Lifecycle hooks with paseo.json

+

paseo.json

- When Paseo creates a worktree, it's a fresh checkout. Dependencies aren't installed, - config files aren't copied. You can automate setup by creating a{" "} - paseo.json file in your repository root: + Drop a paseo.json in your repo root. Paseo reads it + from the committed version of the base branch you picked, so uncommitted changes in other + branches don't apply. +

+ +
{`{
+  "worktree": {
+    "setup":    ["npm ci"],
+    "teardown": ["rm -rf .cache"]
+  },
+  "scripts": {
+    "test": { "command": "npm test" },
+    "web":  { "command": "npm run dev", "type": "service", "port": 3000 }
+  }
+}`}
+
+
+ + {/* Setup & teardown */} +
+

Setup and teardown

+

+ setup runs once after the worktree is created. A fresh + worktree has no installed dependencies and no ignored files (like{" "} + .env), so use setup to install and copy what you need. + teardown runs during archive, before the directory is + removed.

{`{
   "worktree": {
     "setup": [
       "npm ci",
-      "cp \\"$PASEO_SOURCE_CHECKOUT_PATH/.env\\" \\"$PASEO_WORKTREE_PATH/.env\\""
+      "cp \\"$PASEO_SOURCE_CHECKOUT_PATH/.env\\" .env",
+      "npm run db:migrate"
+    ],
+    "teardown": [
+      "npm run db:drop || true"
     ]
   }
 }`}

- The setup array contains shell commands that run after - the worktree is created. Use it to install dependencies, copy local config files, or run - any other initialization. + Commands run with the worktree as cwd. Use{" "} + $PASEO_SOURCE_CHECKOUT_PATH to reach files in the + original checkout (untracked config, local caches, etc).

+
+ + {/* Scripts & services */} +
+

Scripts and services

- You can also add a teardown array for cleanup commands - that run before Paseo removes the worktree directory during archive: + scripts are named commands you can run inside a + worktree on demand. Mark one as a service and Paseo supervises it as a + long-running process, assigns it a port, and routes HTTP traffic to it through the + daemon's reverse proxy. +

+ +

Plain scripts

+ +
{`{
+  "scripts": {
+    "test":     { "command": "npm test" },
+    "lint":     { "command": "npm run lint" },
+    "generate": { "command": "npm run codegen" }
+  }
+}`}
+
+ +

Services

+ +
{`{
+  "scripts": {
+    "web": {
+      "type": "service",
+      "command": "npm run dev -- --port $PASEO_PORT",
+      "port": 3000
+    },
+    "api": {
+      "type": "service",
+      "command": "npm run api -- --port $PASEO_PORT"
+    }
+  }
+}`}
+
+

+ Omit port to let Paseo auto-assign one. Bind your + process to $PASEO_PORT rather than hard-coding — each + worktree gets a distinct port so multiple copies of the same service coexist. +

+ +

Reverse proxy

+

+ Every service is reachable through the daemon at a deterministic hostname: +

+ +
{`http://