Hooks
Hooks let gji run your setup and cleanup commands automatically at key lifecycle moments.
If you only need to copy gitignored local files such as .env.local into new worktrees, use gji sync-files instead of a custom hook. Hooks are best for commands that actually need to run.
Example
{
"hooks": {
"afterCreate": ["pnpm", "install"],
"afterEnter": ["printf", "switched to %s\n", "{{branch}}"],
"beforeRemove": "pnpm run cleanup"
}
}
This is the simplest way to make each new worktree feel “ready” instead of empty.
Available hooks
| Hook | When it runs |
|---|---|
afterCreate | After gji new or gji pr creates a worktree. |
afterEnter | After gji go switches to a worktree. |
beforeRemove | Before gji remove deletes a worktree. |
Template values
Hooks receive these template variables:
{{branch}}{{path}}{{repo}}
The same values are also available as environment variables:
GJI_BRANCHGJI_PATHGJI_REPO
Safer hook commands
Prefer argv-array hooks when you do not need shell features:
{
"hooks": {
"afterCreate": ["pnpm", "install"],
"afterEnter": ["printf", "switched to %s at %s\n", "{{branch}}", "{{path}}"]
}
}
Array hooks run without a shell. Each array item is passed as exactly one argument, so branch names and paths containing spaces or shell metacharacters are not re-parsed as shell syntax.
String hooks are still supported for shell features like &&, pipes, redirects, shell functions, and nvm use. Template values are interpolated before the shell parses the command, so avoid putting {{branch}}, {{path}}, or {{repo}} directly into shell strings.
For shell-string hooks, read the context from environment variables and double-quote each expansion:
{
"hooks": {
"afterCreate": "pnpm install && printf 'ready: %s\n' \"$GJI_PATH\""
}
}
Avoid unquoted template values in shell strings, such as echo {{branch}} or cd {{path}}.
Layering behavior
Hooks can come from:
- global config
- per-repo global overrides
- local
.gji.json
Different hook keys merge together. The same hook key is overridden by the highest-precedence layer.
That makes it possible to have a general default like afterEnter, while one repository overrides only afterCreate.
Manual execution
You can rerun any hook in the current worktree:
gji trigger-hook afterCreate
gji trigger-hook afterEnter
gji trigger-hook beforeRemove
This is useful after cloning on a new machine or when you need to rebuild a worktree’s setup without recreating it.
Practical examples
Install dependencies for each new worktree
{
"hooks": {
"afterCreate": ["pnpm", "install"]
}
}
Print context on entry
{
"hooks": {
"afterEnter": ["printf", "switched to %s at %s\n", "{{branch}}", "{{path}}"]
}
}