Skip to content

Hooks

Hooks allow you to run commands automatically during the worktree lifecycle.

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
[hooks]
pre_start = ["echo 'Preparing worktree...'"]
post_start = [
"pnpm install",
"pnpm db:migrate"
]
pre_clean = ["git stash"]
post_clean = ["echo 'Cleanup complete'"]

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
[hooks]
post_start = [
"echo 'Worktree created at: $VIBE_WORKTREE_PATH'",
"echo 'Origin repository: $VIBE_ORIGIN_PATH'"
]

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/
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 .

Situation stdout stderr
Progress display active Suppressed Always shown
Progress display inactive Written to stderr Always shown
Failed hooks N/A Always shown

For vibe start:

  1. Worktree is created
  2. Listed submodules are initialized when [submodules] configs = [...]
  3. Each listed submodule’s trusted pre_start, copy rules, and post_start run from that submodule root
  4. Parent pre_start hooks run in origin repository
  5. Parent files/directories are copied
  6. Parent post_start hooks run in new worktree

For vibe clean:

  1. pre_clean hooks run in current worktree
  2. Worktree is removed
  3. post_clean hooks run in main repository

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.

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 provisioned

The 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.
[hooks]
post_start = [
"pnpm install",
"pnpm build"
]
pre_clean = ["git stash --include-untracked"]
[hooks]
post_start = [
"bun install",
"bun run build"
]
pre_clean = ["git stash --include-untracked"]
[hooks]
post_start = [
"pnpm install",
"pnpm db:migrate",
"pnpm db:seed"
]
[hooks]
post_start = [
"docker-compose up -d",
"sleep 5",
"pnpm db:migrate"
]
pre_clean = ["docker-compose down"]

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.

You can skip hooks with the --no-hooks option:

Terminal window
vibe start feat/quick-fix --no-hooks

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"
}
]
}
]
}
}

When Claude Code creates a worktree (via natural language, isolation: "worktree", or the /worktree command), vibe handles the full lifecycle:

WorktreeCreate:

  1. Reads worktree name from stdin (Claude Code hook protocol)
  2. Creates the git worktree
  3. Initializes listed submodules and runs their trusted setup when [submodules] configs = [...]
  4. Runs parent pre_start hooks in the origin repository
  5. Copies parent files/directories using CoW
  6. Runs parent post_start hooks in the new worktree (e.g., pnpm install)
  7. Outputs the worktree path to stdout for Claude Code

WorktreeRemove:

  1. Reads worktree path from stdin (Claude Code hook protocol)
  2. Runs pre_clean hooks in the worktree
  3. Removes the git worktree
  4. Runs post_clean hooks in the main repository

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