Wire pks into Claude Code's hook system: register handlers for seven lifecycle events and block a turn from ending when your lint command fails.
pks hooks <command> [options]$ pks hooks initRegister pks handlers in this project's Claude settings
$ pks hooksConfigure the lint command the Stop hook runs
$ pks hooks listShow the hook events pks can handle
$ pks hooks init --scope userRegister handlers globally for every project
pks hooks connects pks to Claude Code's hook system — the callbacks Claude Code fires at fixed points in an agent turn. One command writes the wiring, one interactive menu configures the only handler that currently enforces anything, and the remaining subcommands are the handlers themselves.
pks hooks init merges a hooks block into a Claude Code settings.json. That block tells Claude Code to shell out to pks hooks <event> at seven lifecycle points: PreToolUse, PostToolUse, UserPromptSubmit, Stop, Notification, SubagentStop, and PreCompact. Each of those pks hooks <event> subcommands is the handler Claude Code invokes — it reads the event payload from stdin and, for the decision-carrying events, writes a JSON verdict on stdout.
pks hooks stop is supposed to run a lint command you configure and block Claude Code from ending its turn when that command exits non-zero — but the configured command is never persisted to disk (see Block a turn on a failing lint), so it doesn't survive into the separate process Claude Code actually invokes for each Stop event.pre-tool-use, post-tool-use, and user-prompt-submit read their payload and always allow the action.notification, subagent-stop, and pre-compact dump what they receive and exit.init merges into an existing hooks object instead of replacing it, so hook entries you already wrote survive.init detects the old camelCase hook keys an earlier pks version wrote (preToolUse, stop) and rewrites them to the PascalCase names Claude Code expects.settings.json is resolved from the running process, so the hook works whether or not pks is on PATH..claude folder.There are two halves. The setup half is what you run by hand: pks hooks init writes the wiring, and bare pks hooks opens a menu that stores the lint command. The runtime half is what Claude Code runs: the leaf event commands, invoked with the event payload piped to stdin. You never run those yourself except to inspect a payload.
The two halves are independent, and both are required. Storing a lint command with no Stop entry in settings.json does nothing, because nothing invokes the handler. Registering the Stop entry with no lint command configured also does nothing, because the handler returns immediately. The menu closes that gap: when you enable a lint command in a project whose settings.json doesn't yet reference pks hooks stop, it registers the Stop hook for you.
In practice only the Stop-entry half survives today: the lint command the menu "stores" is never written to disk, so it's gone as soon as that pks hooks process exits — see the known limitation on Block a turn on a failing lint.
init and the interactive menu — run by you, interactive, writes files.init · list · pre-tool-use · post-tool-use · user-prompt-submit · stop · notification · subagent-stop · pre-compact
Every subcommand shares the same three options. Full flag and behavior detail is on the pks hooks reference.
init, choose a scope, and confirm what landed in settings.jsonWhat each of the seven Claude Code hook events does when pks handles it — which one enforces policy, which pass through, and which only dump their payload.
Run pks hooks init to merge pks handlers into a Claude Code settings.json, pick the right scope, and verify the wiring survives a reinstall.
pks hooks init --scope project
Complete command and option reference for pks hooks — the setup commands, the seven Claude Code event handlers, and the three shared flags.
pks hooks <command> [options]
Configure the Stop hook so Claude Code cannot finish a turn while your lint command fails, and hand the failure output back to the agent to fix.
pks hooks