Declarative hook framework for Claude Code
Project description
captain-hook
Declarative hook framework for Claude Code. Write hooks as data, test them inline, and ship them to CI in the same shape they run in production.
Quickstart
No install step — everything runs through uvx. Pick a front door:
From your terminal:
uvx capt-hook init
From inside Claude Code — install the plugin, then ask Claude to set it up:
/plugin marketplace add yasyf/captain-hook
/plugin install captain-hook@captain-hook
set up captain hook
Either path lands in the same place: .claude/hooks/ scaffolded, Claude Code's settings wired, the bundled skills installed, and the session reviewer watching this repo. uvx fetches captain-hook into a throwaway environment, so it never enters your pyproject.toml — and every command below works the same way once you prefix it with uvx.
Your first hook
A hook is declarative Python with an event, some conditions, and an action. This one stops the agent from finishing a UI change it never looked at.
# .claude/hooks/visual_review.py
from captain_hook import gate, TouchedFile, UsedSkill
# A Stop gate: before the agent finishes, block if it edited UI files without doing a visual review.
gate(
# the one-line reason shown to the agent when the gate fires
"You edited UI files. Open them with agent-browser and verify they render before finishing.",
# fires only if UI files changed
only_if=[TouchedFile("**/src/routes/**", "**/src/components/**")],
# already reviewed -> don't block
skip_if=[UsedSkill("agent-browser")],
)
Conditions match tools, files, commands, and even which skills the agent used.
Test your hooks
Every deterministic hook carries inline tests, so a broken hook fails like broken code. Run them from your project root, where --hooks defaults to .claude/hooks.
# .claude/hooks/safety.py
from captain_hook import Allow, Block, Input, block_command
block_command(
["git", "stash"],
reason="Use the team's VCS workflow for shelving changes",
hint="Commit a WIP change instead of stashing",
tests={
Input(command="git stash"): Block(),
Input(command="git status"): Allow(),
},
)
uvx capt-hook test
init already wired Claude Code's settings. Each event runs uvx capt-hook run <Event>, with the event JSON arriving on stdin and the verdict written to stdout. Re-run uvx capt-hook register-hooks only after you add hooks on a new event; it writes .claude/settings.local.json for you.
Session reviewer
init also turns on the session reviewer. When a Claude Code session ends, it mines the transcript for the durable corrections you gave and the hooks that misfired, judges each one, and — once a pattern clears its thresholds — opens a pull request that adds a new hook or fixes the one that misfired. You review the PR like any other.
It's on by default after init. Turn it off for a repo with uvx capt-hook review disable, or skip it at setup with uvx capt-hook init --no-review. The session reviewer guide covers prerequisites (an authenticated claude and gh) and the HOOKS_REVIEW_* tuning knobs.
Agent Skills
captain-hook ships two Agent Skills so you don't have to write hooks by hand. bootstrapping-hooks surveys your repo's docs, CI, and git history and proposes gates and nudges; translating-styleguides turns a STYLEGUIDE.md into enforced rules. Both land in .claude/skills/ via init and ship as the plugin in the Quickstart — ask Claude to "set up captain hook" and bootstrapping-hooks takes it from there.
What this solves
captain-hook covers these jobs:
- Block dangerous tool calls before they execute on
PreToolUse, like force-push, package-manager footguns, and rawrm -rf. - Drive the agent with feedback that fires on the patterns it actually emits, such as repeated failures, weakened tests, and missed conventions.
- Enforce multi-step workflows with Stop gates and artifact validation, so the agent can't declare "done" without running tests, writing a report, or completing a checklist.
- Keep all of the above testable. Every hook ships with inline
tests = {...}thatuvx capt-hook testruns in CI, so you catch broken hooks the way you catch broken code.
Docs
Read the docs for the full guide to conditions, primitives, LLM hooks, workflows, state, and real-world patterns.
For working on captain-hook itself, see the development guide.
License
Licensed under PolyForm Noncommercial 1.0.0, free for noncommercial use.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file capt_hook-3.3.2.tar.gz.
File metadata
- Download URL: capt_hook-3.3.2.tar.gz
- Upload date:
- Size: 136.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fea8ee7ccde4252c5663074a22be6b18108f16ee457e75382b9ba89b0e21d58b
|
|
| MD5 |
6f1c53d7295adb433df17dd54e3db3e4
|
|
| BLAKE2b-256 |
c5fc6c2aa2d87a6a0408290176f9a383e11857bba490d07dd744b278274d0678
|
Provenance
The following attestation bundles were made for capt_hook-3.3.2.tar.gz:
Publisher:
release-pypi.yml on yasyf/captain-hook
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capt_hook-3.3.2.tar.gz -
Subject digest:
fea8ee7ccde4252c5663074a22be6b18108f16ee457e75382b9ba89b0e21d58b - Sigstore transparency entry: 1850538008
- Sigstore integration time:
-
Permalink:
yasyf/captain-hook@27167d67190396ac79bc3ef4fcb33c5b580d5405 -
Branch / Tag:
refs/tags/v3.3.2 - Owner: https://github.com/yasyf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@27167d67190396ac79bc3ef4fcb33c5b580d5405 -
Trigger Event:
push
-
Statement type:
File details
Details for the file capt_hook-3.3.2-py3-none-any.whl.
File metadata
- Download URL: capt_hook-3.3.2-py3-none-any.whl
- Upload date:
- Size: 180.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc517b795d45e15774fe37324506ae11fdcdf7a74bdbf39f205ff9e4def8dc19
|
|
| MD5 |
f5e5c7969ac659f21c1f82240ee3fc8c
|
|
| BLAKE2b-256 |
c219e5e752b3a3c3c77bf81c1377ba75df170b337199da46b6a3e5acdbe29595
|
Provenance
The following attestation bundles were made for capt_hook-3.3.2-py3-none-any.whl:
Publisher:
release-pypi.yml on yasyf/captain-hook
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capt_hook-3.3.2-py3-none-any.whl -
Subject digest:
fc517b795d45e15774fe37324506ae11fdcdf7a74bdbf39f205ff9e4def8dc19 - Sigstore transparency entry: 1850538108
- Sigstore integration time:
-
Permalink:
yasyf/captain-hook@27167d67190396ac79bc3ef4fcb33c5b580d5405 -
Branch / Tag:
refs/tags/v3.3.2 - Owner: https://github.com/yasyf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@27167d67190396ac79bc3ef4fcb33c5b580d5405 -
Trigger Event:
push
-
Statement type: