Base plugin.
Warning
Hooks require pre-configuration to function. See #project-configuration
/commit— clean commit workflow (branch creation, staging, imperative messages)
Quality guards that block bad patterns before they reach the codebase:
ban_redundant_cd.py— blocks redundantcdinto directories the shell is already inban_lint_suppressions.py— blocks# noqa,# type: ignore,# pyright: ignore
Runs on Write|Edit|MultiEdit — auto-formats changed files using the project's configured commands.
Runs before Claude stops — executes slow checks on modified directories, including files already committed on the current branch.
The PostToolUse and Stop hooks read .claude/ox-hooks.json from the project root to determine what to run. If the file is missing, the hooks are no-ops.
Each entry in checks defines a fast command (run on PostToolUse) and a slow command (run on Stop). If directory is set, the command only triggers when files under that directory have changed and runs inside that subdirectory. If omitted, the command triggers on any file change and runs at the project root.
Stop checks include both working-tree changes and committed branch changes compared with base_ref. The default base_ref is origin/main; set it for projects that branch from another long-lived branch.
Whole-project (e.g. a Python project using ruff):
{
"base_ref": "origin/main",
"checks": [
{ "fast": "uv run ruff format .", "slow": "uv run ruff check ." }
]
}Multi-directory (e.g. a fullstack monorepo):
{
"checks": [
{ "directory": "backend", "fast": "make format", "slow": "make check" },
{ "directory": "frontend", "fast": "npm run format", "slow": "npm run check" },
{ "directory": "hocuspocus", "fast": "npm run format", "slow": "npm run check" }
]
}The script also skips formatting for import-only edits (Python and JS/TS) to avoid unnecessary formatter runs while imports are being added.
When Claude makes many sequential edits, running the formatter on every single one is wasteful. The fast_every option throttles PostToolUse fast checks so they only run on the 1st edit and every Nth edit thereafter. The Stop hook always runs slow checks regardless of the throttle, catching any missed formatting.
{
"fast_every": 5,
"checks": [
{ "fast": "make format", "slow": "make check" }
]
}- Default:
5— format runs on edit 1, then every 5th edit (1, 5, 10, 15, ...) - Set to
1to disable throttling (run on every edit)