Hooks
Hooks allow you to run commands automatically during the worktree lifecycle.
Available Hooks
Section titled “Available Hooks”| Hook | When | Working Directory |
|---|---|---|
pre_start |
After worktree creation | Origin repository |
post_start |
After worktree creation | New worktree |
pre_clean |
Before worktree removal | Current worktree |
post_clean |
After worktree removal | Main repository |
Configuration
Section titled “Configuration”[hooks]pre_start = ["echo 'Preparing worktree...'"]post_start = [ "pnpm install", "pnpm db:migrate"]pre_clean = ["git stash"]post_clean = ["echo 'Cleanup complete'"]Environment Variables
Section titled “Environment Variables”The following environment variables are available in all hook commands:
| Variable | Description |
|---|---|
VIBE_WORKTREE_PATH |
Absolute path to the created worktree |
VIBE_ORIGIN_PATH |
Absolute path to the original repository |
Example Usage
Section titled “Example Usage”[hooks]post_start = [ "echo 'Worktree created at: $VIBE_WORKTREE_PATH'", "echo 'Origin repository: $VIBE_ORIGIN_PATH'"]Hook Output Behavior
Section titled “Hook Output Behavior”vibe displays a real-time progress tree during hook execution:
✶ Setting up worktree feature/new-ui…┗ ☒ Pre-start hooks ┗ ☒ npm install ☒ cargo build --release ⠋ Copying files ┗ ⠋ .env.local ⠋ node_modules/Markers
Section titled “Markers”| Marker | Meaning |
|---|---|
⠋ |
Pending or running (the spinner animates once it starts) |
☒ |
Completed successfully |
✗ |
Failed (red); the reason follows as (failed: …) |
⊘ |
Abandoned (dim) — still pending when the run ended |
A phase line (the outer ┗) reports no result of its own; it aggregates the
tasks below it. It shows ⊘ if any task was still pending, otherwise ✗ with
(failed: N task(s) failed) if any task failed, otherwise ☒.
Output Handling
Section titled “Output Handling”| Situation | stdout | stderr |
|---|---|---|
| Progress display active | Suppressed | Always shown |
| Progress display inactive | Written to stderr | Always shown |
| Failed hooks | N/A | Always shown |
Hook Execution Order
Section titled “Hook Execution Order”For vibe start:
- Worktree is created
- Listed submodules are initialized when
[submodules] configs = [...] - Each listed submodule’s trusted
pre_start, copy rules, andpost_startrun from that submodule root - Parent
pre_starthooks run in origin repository - Parent files/directories are copied
- Parent
post_starthooks run in new worktree
For vibe clean:
pre_cleanhooks run in current worktree- Worktree is removed
post_cleanhooks run in main repository
Hook Failure Behavior
Section titled “Hook Failure Behavior”A hook that exits non-zero is a warning, not an error: vibe prints
Warning: Hook "..." failed: ... and still exits with status 0. What changes is
whether the directory change happens, and that depends on where the hook sits in
the lifecycle.
| Hook | On failure |
|---|---|
pre_start |
Acts as a gate: the copy and post_start are skipped and you stay in the original directory |
post_start |
Warns only: the worktree is fully set up, so vibe still moves you into it |
pre_clean |
Acts as a gate: the worktree is not removed and you stay in it |
post_clean |
Warns only: the worktree is already gone, so vibe still moves you back to the main repository |
This makes pre_start and pre_clean usable as precondition checks — for
example, a pre_start that verifies a secrets vault is reachable will keep you
out of a worktree it could not provision.
A gated vibe start still leaves the worktree directory on disk; only entering
it is withheld. Re-running vibe start for the same branch then finds that
worktree and asks whether to navigate to it — and the gate runs again on that
path, so a pre_start that still fails still keeps you out. Once the cause is
fixed, the re-run also performs the copy and the post_start that the gated run
skipped, so the worktree you finally enter is fully provisioned.
Claude Code hook mode
Section titled “Claude Code hook mode”vibe start --claude-code-worktree-hook has no shell to keep in place, so a
pre_start gate cannot express itself by withholding a directory change there.
The mode keeps its contract instead — the worktree path on stdout and exit
status 0 — and reports the gate on stderr as a fixed, machine-readable line:
vibe: pre_start hook failed; worktree is not provisionedThe line is emitted at most once per invocation, unstyled (no color codes), and
only for a pre_start gate; a failing post_start produces the usual
Warning: Hook "..." failed: ... and nothing more. Treat it as the signal that
the path you just received points at a worktree whose copy and post_start were
skipped.
Two caveats:
- The setup example below ends the command with
2>/dev/null, which discards stderr and therefore this signal too. Drop that redirection if you want to observe the gate. - The signal accompanies the run that creates the worktree. Invoking the hook again for a branch that already has a worktree returns the existing path without re-running hooks, so no signal is emitted on that second call.
Common Patterns
Section titled “Common Patterns”Node.js Project
Section titled “Node.js Project”[hooks]post_start = [ "pnpm install", "pnpm build"]pre_clean = ["git stash --include-untracked"]Bun Project
Section titled “Bun Project”[hooks]post_start = [ "bun install", "bun run build"]pre_clean = ["git stash --include-untracked"]Database Migration
Section titled “Database Migration”[hooks]post_start = [ "pnpm install", "pnpm db:migrate", "pnpm db:seed"]Docker Environment
Section titled “Docker Environment”[hooks]post_start = [ "docker-compose up -d", "sleep 5", "pnpm db:migrate"]pre_clean = ["docker-compose down"]Git Submodules
Section titled “Git Submodules”Use first-class submodule configs when a direct submodule has its own setup files or hooks:
[submodules]configs = ["libs/foo"]This runs git submodule update --init -- libs/foo in the new worktree, then loads libs/foo/.vibe.toml through the normal trust store. The submodule’s copy rules and hooks resolve paths from libs/foo, and they run before the parent repository’s pre_start hooks.
Skipping Hooks
Section titled “Skipping Hooks”You can skip hooks with the --no-hooks option:
vibe start feat/quick-fix --no-hooksClaude Code Integration
Section titled “Claude Code Integration”vibe integrates with Claude Code’s WorktreeCreate and WorktreeRemove hooks. This replaces Claude Code’s default git worktree add behavior with vibe’s full workflow, including hooks, CoW file copying, and configuration.
Add the following to your Claude Code settings.json:
{ "hooks": { "WorktreeCreate": [ { "hooks": [ { "type": "command", "command": "vibe start --claude-code-worktree-hook --quiet 2>/dev/null" } ] } ], "WorktreeRemove": [ { "hooks": [ { "type": "command", "command": "vibe clean --claude-code-worktree-hook --force 2>/dev/null" } ] } ] }}How It Works
Section titled “How It Works”When Claude Code creates a worktree (via natural language, isolation: "worktree", or the /worktree command), vibe handles the full lifecycle:
WorktreeCreate:
- Reads worktree name from stdin (Claude Code hook protocol)
- Creates the git worktree
- Initializes listed submodules and runs their trusted setup when
[submodules] configs = [...] - Runs parent
pre_starthooks in the origin repository - Copies parent files/directories using CoW
- Runs parent
post_starthooks in the new worktree (e.g.,pnpm install) - Outputs the worktree path to stdout for Claude Code
WorktreeRemove:
- Reads worktree path from stdin (Claude Code hook protocol)
- Runs
pre_cleanhooks in the worktree - Removes the git worktree
- Runs
post_cleanhooks in the main repository
Benefits
Section titled “Benefits”Without this integration, Claude Code’s worktree creation only runs git worktree add. By using vibe’s hooks, you get:
- Automatic dependency installation (
pnpm install,bun install, etc.) - CoW file copying (
.env,node_modules, etc.) - Custom setup scripts (database migrations, Docker containers, etc.)
- Cleanup hooks on worktree removal