--- name: berth-hooks description: Automate berth with hooks — run a command when something happens (an agent finishes or needs the user, a worktree is created or removed, a session starts) or gate an action with a before: hook that can refuse it. Covers where hooks live (a repository's .berth/config.json, a box, the laptop), the event catalog, the variables hooks get, and how to test them. Use when the user asks to automate a workflow, react to agents or worktrees, block something, or set up per-worktree databases and ports. --- # berth hooks A hook runs a shell command when an event happens. A hook whose `on` starts with `before:` runs first and can refuse the action by exiting non-zero; what it prints becomes the error everyone sees. ```json { "hooks": [ { "on": "agent.waiting", "run": "notify-send \"$BERTH_PATH needs you\"" }, { "on": "worktree.*", "run": "echo \"$BERTH_EVENT $BERTH_NAME\" >> ~/worktrees.log" }, { "on": "before:worktree.create", "run": "case \"$BERTH_BRANCH\" in main|master) echo 'not on main'; exit 1;; esac" } ] } ``` `on` is an event type, a prefix like `worktree.*`, or `*`. Optional: `timeout` (`30s`, `5m`; default 1m, 30s for gates) and `tool` (a hook with `"tool": "slack"` skips events whose origin is `slack`, which stops loops). ## Where a hook lives decides where it runs | Scope | File | Runs | Good for | | --- | --- | --- | --- | | A repository | `hooks` in `.berth/config.json` (or the box's config for the location) | on the box, only for that repo's events, **in the worktree** with its environment | per-worktree databases, seeding, codegen | | A box | `~/.berth/hooks.json` on the box | on the box, for every event there | box-wide policy, logging | | The laptop | `~/.berth/hooks.json` on the laptop | on the laptop, for events from every box | notifications, opening things locally | Repository hooks get the worktree's variables (`$BERTH_WORKTREE_PATH`, `$BERTH_WORKTREE_SLUG`, `$BERTH_PORT`, …) plus the repository's `env`. Prefer `setup` / `archive` in `.berth/config.json` for one-time worktree setup and teardown; use hooks for reactions to other events. ## What a hook gets - The event as JSON on stdin: `{"type","time","box","origin","data":{…}}`. - `BERTH_EVENT`, `BERTH_EVENT_BOX`, `BERTH_EVENT_ORIGIN`, and each data field upper-cased: `BERTH_PATH`, `BERTH_NAME`, `BERTH_LOCATION`, `BERTH_BRANCH`, … ## Events `worktree.created`, `worktree.removed`, `worktree.setup.{started,finished,failed}`, `worktree.archive.{started,finished,failed}`, `task.created`, `session.started`, `session.stopped`, `session.sent`, `agent.ready` (idle at its prompt), `agent.started`, `agent.waiting` (needs a human), `agent.finished`, `service.started`, `service.stopped`, `exec.finished`, `preview.open`, `location.added`, `location.removed`, `config.changed`, `share.started`, `share.stopped`, and on the laptop `box.connected`, `box.disconnected`, `forward.*`. Gates: `before:worktree.create`, `before:worktree.remove`, `before:task.create`, `before:session.start`, `before:session.stop`, `before:session.send`, `before:exec`, `before:location.add`, `before:config.change`, `before:skills.install`, `before:skills.uninstall`. ## Test before you rely on it ```sh berthd events # watch events arrive (Ctrl-C to stop) berthd emit agent.finished path="$PWD" # fire one to try a hook ``` ## Safety - Ask before adding or changing hooks: they run automatically, as the user. - Keep commands short, idempotent and quick; put long work in a script. - Never put secrets in a committed `.berth/config.json`; use the box's own config for the location, or read them from the environment at run time. - A gate that fails closed blocks the user; print a clear reason.