Hooks

Hooks customize two things: how slice up presents your processes, and what happens when you create a new worktree. They're defined in the TOML output of your thinslice.new-state.

Two kinds of hooks

hooks.up

Control how slice up launches and arranges your processes.

Without a hook, slice up starts all processes as background daemons. With a hook, it arranges them in tmux according to your layout.

slice up                  # background all processes
slice up dev              # use hooks.up.dev — arrange in tmux
slice up full             # use hooks.up.full — different layout

Defined in your template output:

[hooks.up.dev]
tmux = """
new-window -n $BRANCH
split-v 70
send-top "$SHELL"
send-bottom "slice logs --follow"
focus-top
"""

[hooks.up.full]
tmux = """
new-window -n $BRANCH
split-h
split-right-v
send-left "cargo run --bin api -- --port $API_PORT"
send-topright "npm run dev -- --port $FRONTEND_HTTP_PORT"
send-bottomright "psql -p $PG_PORT"
focus-left
"""

hooks.new-worktree

Run when creating a worktree with slice local, slice tmp, or slice pr checkout. These set up your interactive working environment — typically a tmux window with a coding agent or editor on one side and slice up on the other.

The hook name is a positional argument after the worktree name and flags. Everything after the hook name is passed through as arguments.

slice tmp fix-login --from current claude -p "fix the redirect"
#                                  ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^
#                                  hook   passed as $@ to the hook

Defined in your template output:

[hooks.new-worktree.claude]
tmux = """
new-window -n $BRANCH
split-h
send-left "cd $SLICE_CODE_DIR && claude $@"
send-right "cd $SLICE_CODE_DIR && slice up"
focus-prev
"""

[hooks.new-worktree.psql]
tmux = """
new-window -n $BRANCH
split-h
send-left "cd $SLICE_CODE_DIR && psql -p $PG_PORT $@"
send-right "cd $SLICE_CODE_DIR && slice up"
focus-prev
"""

[hooks.new-worktree.vim]
tmux = """
new-window -n $BRANCH
split-h 70
send-left "cd $SLICE_CODE_DIR && vim $@"
send-right "cd $SLICE_CODE_DIR && slice up dev"
focus-left
"""

Note that hooks.new-worktree hooks are responsible for calling slice up themselves. This gives you control over whether processes start in the background or with a tmux layout, and which pane they run in.

Hook types

A hook can use the tmux DSL, exec a script, or both.

tmux

Define a tmux layout using the thinslice tmux DSL. This is the common case for developers working in tmux.

[hooks.new-worktree.claude]
tmux = """
new-window -n $BRANCH
split-h
send-left "cd $SLICE_CODE_DIR && claude $@"
send-right "cd $SLICE_CODE_DIR && slice up"
focus-prev
"""

exec

Run an arbitrary script. Use this when you're not in tmux, or when you need to do something the tmux DSL doesn't cover.

[hooks.new-worktree.zellij]
exec = "$SLICE_PROJECT_DIR/scripts/start-zellij.sh $SLICE_CODE_DIR $SLICE_STATE_DIR $@"

The script receives the same environment variables as your template: $SLICE_CODE_DIR, $SLICE_STATE_DIR, $SLICE_PROJECT_DIR, $BRANCH, plus all variables from the [env] section of thinslice.toml. $@ and $1, $2, etc. come from the arguments passed after the hook name.

Both

If both tmux and exec are present, the tmux DSL runs first, then exec. This is useful for setting up terminal panes and also launching a GUI application.

[hooks.new-worktree.cursor]
tmux = """
new-window -n $BRANCH
send "cd $SLICE_CODE_DIR && slice up"
focus-prev
"""
exec = "cursor $SLICE_CODE_DIR"

Variable substitution

All hook definitions have access to these variables:

VariableDescription
$BRANCHBranch name (e.g. tmp/fix-login)
$SLICE_CODE_DIRAbsolute path to the worktree
$SLICE_STATE_DIRAbsolute path to the state directory
$SLICE_PROJECT_DIRProject root directory
$PG_PORT, etc.Port variables from your process definitions
$@All arguments passed after the hook name
$1, $2, $3...Individual positional arguments
${@:2}All arguments from the 2nd onward

Precedence

Hooks can be defined in three places, in order of precedence:

  1. Template output — defined in the TOML your thinslice.new-state produces. Project-specific.
  2. Project hook filethinslice.hooks.toml in your repo root. Checked into version control.
  3. Global hook file~/.config/thinslice/hooks.toml. Personal defaults across all projects.

A hook defined in the template output overrides a project-level hook with the same name, which overrides a global hook.

This means you can define a global claude hook that works for all your projects, and override it in a specific project's template if that project needs a different layout.

Example global hooks

# ~/.config/thinslice/hooks.toml

[hooks.new-worktree.claude]
tmux = """
new-window -n $BRANCH
split-h
send-left "cd $SLICE_CODE_DIR && claude $@"
send-right "cd $SLICE_CODE_DIR && slice up"
focus-prev
"""

[hooks.new-worktree.codex]
tmux = """
new-window -n $BRANCH
split-h
send-left "cd $SLICE_CODE_DIR && codex $@"
send-right "cd $SLICE_CODE_DIR && slice up"
focus-prev
"""

[hooks.new-worktree.editor]
exec = "$EDITOR $SLICE_CODE_DIR"

[hooks.up.dev]
tmux = """
new-window -n $BRANCH
split-v 70
send-top "$SHELL"
send-bottom "slice logs --follow"
focus-top
"""