docs(website): rewrite worktrees page around scripts, services, and proxy
This commit is contained in:
@@ -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() {
|
||||
<div>
|
||||
<h1 className="text-3xl font-medium font-title mb-4">Git Worktrees</h1>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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 <code className="font-mono">paseo.json</code> file at your repo root.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Why worktrees */}
|
||||
{/* Layout & workflow */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Why worktrees?</h2>
|
||||
<h2 className="text-xl font-medium">Layout and workflow</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Directory structure */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Directory structure</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo creates worktrees under <code className="font-mono">$PASEO_HOME/worktrees/</code>,
|
||||
organized by a short hash of the source checkout path:
|
||||
Worktrees live under <code className="font-mono">$PASEO_HOME/worktrees/</code>, 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.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`~/.paseo/worktrees/
|
||||
├── 1vnnm9k3/
|
||||
│ ├── tidy-fox/ # random slug
|
||||
│ └── bold-owl/ # random slug
|
||||
└── 4k8q2d1p/
|
||||
└── swift-hare/ # random slug`}</pre>
|
||||
└── 1vnnm9k3/ # hash of source checkout path
|
||||
├── tidy-fox/ # worktree slug (branch set on first agent)
|
||||
└── bold-owl/`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Branches */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Branches</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
This means the worktree directory and branch are independent. You can rename the branch
|
||||
later without affecting the worktree path.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Multiple agents */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Multiple agents per worktree</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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.
|
||||
</p>
|
||||
<ol className="text-white/60 space-y-2 list-decimal list-inside">
|
||||
<li>Create a worktree — Paseo runs your setup hooks</li>
|
||||
<li>Launch an agent — a branch is created or assigned</li>
|
||||
<li>Review the diff against the base branch</li>
|
||||
<li>Merge or archive — archive runs teardown and removes the directory</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
{/* paseo.json */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Lifecycle hooks with paseo.json</h2>
|
||||
<h2 className="text-xl font-medium">paseo.json</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
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{" "}
|
||||
<code className="font-mono">paseo.json</code> file in your repository root:
|
||||
Drop a <code className="font-mono">paseo.json</code> 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.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"worktree": {
|
||||
"setup": ["npm ci"],
|
||||
"teardown": ["rm -rf .cache"]
|
||||
},
|
||||
"scripts": {
|
||||
"test": { "command": "npm test" },
|
||||
"web": { "command": "npm run dev", "type": "service", "port": 3000 }
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* Setup & teardown */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Setup and teardown</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
<code className="font-mono">setup</code> runs once after the worktree is created. A fresh
|
||||
worktree has no installed dependencies and no ignored files (like{" "}
|
||||
<code className="font-mono">.env</code>), so use setup to install and copy what you need.
|
||||
<code className="font-mono">teardown</code> runs during archive, before the directory is
|
||||
removed.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
The <code className="font-mono">setup</code> 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 <code className="font-mono">cwd</code>. Use{" "}
|
||||
<code className="font-mono">$PASEO_SOURCE_CHECKOUT_PATH</code> to reach files in the
|
||||
original checkout (untracked config, local caches, etc).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Scripts & services */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Scripts and services</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You can also add a <code className="font-mono">teardown</code> array for cleanup commands
|
||||
that run before Paseo removes the worktree directory during archive:
|
||||
<code className="font-mono">scripts</code> are named commands you can run inside a
|
||||
worktree on demand. Mark one as a <em>service</em> 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.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-medium mt-4">Plain scripts</h3>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"scripts": {
|
||||
"test": { "command": "npm test" },
|
||||
"lint": { "command": "npm run lint" },
|
||||
"generate": { "command": "npm run codegen" }
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Services</h3>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"scripts": {
|
||||
"web": {
|
||||
"type": "service",
|
||||
"command": "npm run dev -- --port $PASEO_PORT",
|
||||
"port": 3000
|
||||
},
|
||||
"api": {
|
||||
"type": "service",
|
||||
"command": "npm run api -- --port $PASEO_PORT"
|
||||
}
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Omit <code className="font-mono">port</code> to let Paseo auto-assign one. Bind your
|
||||
process to <code className="font-mono">$PASEO_PORT</code> rather than hard-coding — each
|
||||
worktree gets a distinct port so multiple copies of the same service coexist.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Reverse proxy</h3>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Every service is reachable through the daemon at a deterministic hostname:
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`http://<script>.<branch>.<project>.localhost:<daemon-port>
|
||||
|
||||
# on the default branch, the branch label is dropped:
|
||||
http://<script>.<project>.localhost:<daemon-port>`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
<code className="font-mono">*.localhost</code> resolves to{" "}
|
||||
<code className="font-mono">127.0.0.1</code> on modern systems, so these URLs work out of
|
||||
the box. The proxy supports WebSocket upgrades.
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Service-to-service</h3>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Services launched from the same workspace see each other's ports and proxy URLs. Given{" "}
|
||||
<code className="font-mono">web</code> and <code className="font-mono">api</code> above,
|
||||
each process gets:
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`PASEO_PORT=3000 # this service's port
|
||||
PASEO_URL=http://web.my-app.localhost:6767 # this service's proxy URL
|
||||
PASEO_SERVICE_API_PORT=51732
|
||||
PASEO_SERVICE_API_URL=http://api.my-app.localhost:6767
|
||||
PASEO_SERVICE_WEB_PORT=3000
|
||||
PASEO_SERVICE_WEB_URL=http://web.my-app.localhost:6767`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Script names are upper-cased and non-alphanumerics become{" "}
|
||||
<code className="font-mono">_</code>. Point your frontend at{" "}
|
||||
<code className="font-mono">$PASEO_SERVICE_API_URL</code> instead of hard-coding a port.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Terminals */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Terminals</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Open terminals automatically when a worktree is created. Useful for tailing logs or
|
||||
leaving a REPL ready to go.
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"worktree": {
|
||||
"teardown": [
|
||||
"pkill -f \\"vite --port $PASEO_WORKTREE_PORT\\" || true",
|
||||
"rm -rf \\"$PASEO_WORKTREE_PATH/.cache\\""
|
||||
"terminals": [
|
||||
{ "name": "logs", "command": "tail -f dev.log" },
|
||||
{ "name": "shell", "command": "bash" }
|
||||
]
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
<div className="bg-yellow-500/10 border border-yellow-500/30 rounded-lg p-4 text-white/80">
|
||||
<strong>Important:</strong> Setup commands come from{" "}
|
||||
<code className="font-mono">paseo.json</code> in the selected base branch. If you pick{" "}
|
||||
<code className="font-mono">main</code>, Paseo reads the committed file on{" "}
|
||||
<code className="font-mono">main</code>. Local or uncommitted changes in another branch
|
||||
are not used for that worktree.
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Environment variables */}
|
||||
{/* Env vars */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Environment variables</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Setup and teardown commands have access to these environment variables:
|
||||
Setup, teardown, scripts, and services all see:
|
||||
</p>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_SOURCE_CHECKOUT_PATH</code> — your source checkout
|
||||
path (original repository root)
|
||||
<code className="font-mono">$PASEO_SOURCE_CHECKOUT_PATH</code> — the original repo root
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_ROOT_PATH</code> — legacy alias of{" "}
|
||||
<code className="font-mono">$PASEO_SOURCE_CHECKOUT_PATH</code>
|
||||
<code className="font-mono">$PASEO_WORKTREE_PATH</code> — the worktree directory
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_WORKTREE_PATH</code> — the new worktree directory
|
||||
<code className="font-mono">$PASEO_BRANCH_NAME</code> — the worktree's branch
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_BRANCH_NAME</code> — the branch name created
|
||||
<code className="font-mono">$PASEO_WORKTREE_PORT</code> — legacy per-worktree port
|
||||
(prefer <code className="font-mono">$PASEO_PORT</code> inside services)
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-white/60 leading-relaxed">Services additionally get:</p>
|
||||
<ul className="text-white/60 space-y-2 list-disc list-inside">
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_WORKTREE_PORT</code> — the worktree port, when
|
||||
runtime metadata exists
|
||||
<code className="font-mono">$PASEO_PORT</code> — this service's assigned port
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_URL</code> — this service's proxy URL
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$PASEO_SERVICE_<NAME>_PORT</code> /{" "}
|
||||
<code className="font-mono">_URL</code> — peer service ports and URLs
|
||||
</li>
|
||||
<li>
|
||||
<code className="font-mono">$HOST</code> — <code className="font-mono">127.0.0.1</code>{" "}
|
||||
for local-only daemons, <code className="font-mono">0.0.0.0</code> when the daemon binds
|
||||
all interfaces
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Use <code className="font-mono">$PASEO_SOURCE_CHECKOUT_PATH</code> to copy files that
|
||||
shouldn't be in git (like <code className="font-mono">.env</code>) from your source
|
||||
checkout to the worktree.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
<code className="font-mono">$PASEO_WORKTREE_PORT</code> is available when the worktree was
|
||||
bootstrapped with a port. That makes it useful for both starting services in setup and
|
||||
stopping them again in teardown.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* Teardown */}
|
||||
{/* CLI */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Teardown</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Teardown runs during archive, before Paseo removes the worktree directory. Use it for
|
||||
cleanup that needs access to the worktree path or its assigned port.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Common uses include stopping dev servers on{" "}
|
||||
<code className="font-mono">$PASEO_WORKTREE_PORT</code>, deleting generated files, or
|
||||
deregistering services tied to that worktree.
|
||||
</p>
|
||||
<h2 className="text-xl font-medium">CLI</h2>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"worktree": {
|
||||
"setup": [
|
||||
"npm ci",
|
||||
"nohup npm run dev -- --port $PASEO_WORKTREE_PORT > \\"$PASEO_WORKTREE_PATH/dev.log\\" 2>&1 &"
|
||||
],
|
||||
"teardown": [
|
||||
"pkill -f \\"npm run dev -- --port $PASEO_WORKTREE_PORT\\" || true",
|
||||
"rm -f \\"$PASEO_WORKTREE_PATH/dev.log\\""
|
||||
]
|
||||
}
|
||||
}`}</pre>
|
||||
<pre className="text-white/80">{`paseo run --worktree feature-auth --base main "implement auth"
|
||||
paseo worktree ls
|
||||
paseo worktree archive feature-auth`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* Common patterns */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Common patterns</h2>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Node.js / npm</h3>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"worktree": {
|
||||
"setup": ["npm ci"]
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Python / Poetry</h3>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"worktree": {
|
||||
"setup": ["poetry install"]
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Copy environment files</h3>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"worktree": {
|
||||
"setup": [
|
||||
"npm ci",
|
||||
"cp \\"$PASEO_SOURCE_CHECKOUT_PATH/.env\\" \\"$PASEO_WORKTREE_PATH/.env\\"",
|
||||
"cp \\"$PASEO_SOURCE_CHECKOUT_PATH/.env.local\\" \\"$PASEO_WORKTREE_PATH/.env.local\\""
|
||||
]
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
|
||||
<h3 className="text-lg font-medium mt-6">Run database migrations</h3>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`{
|
||||
"worktree": {
|
||||
"setup": [
|
||||
"npm ci",
|
||||
"cp \\"$PASEO_SOURCE_CHECKOUT_PATH/.env\\" \\"$PASEO_WORKTREE_PATH/.env\\"",
|
||||
"npm run db:migrate"
|
||||
]
|
||||
}
|
||||
}`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* Workflow */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Workflow</h2>
|
||||
<p className="text-white/60 leading-relaxed">The typical workflow is:</p>
|
||||
<ol className="text-white/60 space-y-2 list-decimal list-inside">
|
||||
<li>Create a worktree — Paseo creates the directory and runs setup</li>
|
||||
<li>Launch an agent — Paseo creates or assigns a branch</li>
|
||||
<li>Agent works in isolation — changes stay in its worktree</li>
|
||||
<li>Review the diff — compare against the base branch</li>
|
||||
<li>Merge or discard — if approved, merge the branch; otherwise archive</li>
|
||||
<li>Archive the worktree — cleans up the directory and optionally the branch</li>
|
||||
</ol>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You can run multiple agents in different worktrees simultaneously. Each worktree has its
|
||||
own branch and working directory.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* CLI reference */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">CLI reference</h2>
|
||||
<p className="text-white/60 leading-relaxed">Create an agent in a new worktree:</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`paseo run --worktree feature-auth --base main "implement auth"`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">List all worktrees:</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`paseo worktree ls`}</pre>
|
||||
</Code>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Archive a worktree (stops agents, removes directory):
|
||||
</p>
|
||||
<Code>
|
||||
<pre className="text-white/80">{`paseo worktree archive feature-auth`}</pre>
|
||||
</Code>
|
||||
</section>
|
||||
|
||||
{/* Metadata */}
|
||||
<section className="space-y-4">
|
||||
<h2 className="text-xl font-medium">Metadata</h2>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
Paseo stores metadata in each worktree's git directory to track the base branch. This is
|
||||
used for diff operations and to know what branch to merge into.
|
||||
</p>
|
||||
<p className="text-white/60 leading-relaxed">
|
||||
You don't need to manage this manually — Paseo handles it when creating and archiving
|
||||
worktrees.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user