Metadata-Version: 2.4
Name: capt-hook
Version: 9.2.1
Summary: Stop repeating yourself to Claude.
Keywords: claude,claude-code,hooks,llm,agents,guardrails,cli
Author: Yasyf Mohamedali
Author-email: Yasyf Mohamedali <yasyfm@gmail.com>
License-Expression: PolyForm-Noncommercial-1.0.0
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Typing :: Typed
Requires-Dist: cc-transcript[judge]>=11,<12
Requires-Dist: pydantic>=2.0
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: ast-grep-py>=0.39,<1
Requires-Dist: funcy>=2.0
Requires-Dist: spacy>=3.7
Requires-Dist: click>=8
Requires-Dist: rich>=13
Requires-Dist: orjsonl>=1.0
Requires-Dist: wn>=1.1.0
Requires-Dist: lazy-object-proxy>=1.12.0
Requires-Dist: filelock>=3
Requires-Dist: loguru>=0.7.3
Requires-Dist: spawnllm>=0.5.5,<0.6
Requires-Dist: pytest>=8.0 ; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24 ; extra == 'dev'
Requires-Dist: pyright>=1.1 ; extra == 'dev'
Requires-Dist: pyyaml>=6 ; extra == 'dev'
Requires-Dist: ruff>=0.8 ; extra == 'dev'
Requires-Python: >=3.13
Project-URL: Homepage, https://github.com/yasyf/captain-hook
Project-URL: Documentation, https://yasyf.github.io/captain-hook/
Project-URL: Repository, https://github.com/yasyf/captain-hook
Project-URL: Issues, https://github.com/yasyf/captain-hook/issues
Project-URL: Changelog, https://github.com/yasyf/captain-hook/blob/main/CHANGELOG.md
Provides-Extra: dev
Description-Content-Type: text/markdown

# ![captain-hook](https://github.com/yasyf/captain-hook/raw/main/docs/assets/readme-banner.webp)

**Stop repeating yourself to Claude.** captain-hook mines your transcripts for the corrections you keep giving and opens PRs that turn each one into a typed, tested Python hook.

[![CI](https://github.com/yasyf/captain-hook/actions/workflows/ci.yml/badge.svg)](https://github.com/yasyf/captain-hook/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/capt-hook)](https://pypi.org/project/capt-hook/)
[![License: PolyForm Noncommercial](https://img.shields.io/badge/license-PolyForm--Noncommercial--1.0.0-blue)](https://github.com/yasyf/captain-hook/blob/main/LICENSE)

## Get started

```bash
uvx capt-hook init
```

`init` scaffolds `.claude/hooks/`, wires Claude Code's settings, and arms the session reviewer. One `block_command` later, a force-push dies at `PreToolUse` and the hook's inline tests run green:

<img src="https://github.com/yasyf/captain-hook/raw/main/docs/assets/demo.png" alt="Terminal running 'uvx capt-hook test' — a hook blocks git push --force and both inline tests pass" width="700">

Driving with an agent? Paste this:

```text
/plugin marketplace add yasyf/captain-hook
/plugin install captain-hook@captain-hook
```

<details>
<summary>Prefer a prompt over the plugin?</summary>

```text
Run `uvx capt-hook init` in this repo, write one hook that blocks force-pushes,
and verify it with `uvx capt-hook test`. Read https://yasyf.github.io/captain-hook/
if you get stuck.
```

</details>

---

## Use cases

### Block force-push and rm -rf before they run

One bad Bash call rewrites shared history or eats a directory, and by the time you spot it in the transcript it already ran. Declare the block once, tests inline:

```python
# .claude/hooks/safety.py
from captain_hook import Allow, Block, Input, block_command

block_command(
    ["git", "push", "--force"],
    reason="Force-pushing rewrites shared history",
    hint="Use `git push --force-with-lease` instead",
    tests={
        Input(command="git push --force"): Block(),
        Input(command="git push origin main"): Allow(),
    },
)
```

The next `git push --force` never executes: the agent sees `BLOCKED: Force-pushing rewrites shared history` plus the hint, and reaches for `--force-with-lease` instead. `rm -rf` is one more `block_command` away.

### Turn repeated corrections into rules Claude can't forget

You've typed "use uv, not pip" in a dozen sessions, and session thirteen makes the same mistake. After `init`, the session reviewer reads each transcript as the session ends, keeps the corrections that are standing rules, and — once a pattern proves itself across sessions — opens a PR that codifies it as a hook. Watch the pipeline:

```bash
uvx capt-hook status
```

The dashboard lists every correction it's tracking, staged from first sighting to open PR. You review the PR like any other; merged hooks enforce the rule from then on.

### Gate "done" until the tests actually pass

The agent declares victory while the suite is red. A Stop gate holds the line:

```python
# .claude/hooks/quality.py
from captain_hook import RanCommand, TouchedFile, gate

gate(
    "You edited Python files but never ran the tests. Run `uv run pytest` before finishing.",
    only_if=[TouchedFile("**/*.py")],
    skip_if=[RanCommand(r"\bpytest\b")],
)
```

The agent can't end the turn until a pytest run shows up in the transcript, and the gate stands down on its own once one does.

## More in the docs

- **Session reviewer** — the full corrections lifecycle, from transcript to merged hook PR — [guide](https://yasyf.github.io/captain-hook/docs/guide/session-reviewer.html)
- **Conditions** — typed filters over tools, files, commands, and transcript history — [guide](https://yasyf.github.io/captain-hook/docs/guide/conditions.html)
- **LLM hooks** — gate on a model's verdict when a regex can't decide — [guide](https://yasyf.github.io/captain-hook/docs/guide/llm-hooks.html)
- **Workflows** — multi-step Stop gates with artifact checks and checklists — [guide](https://yasyf.github.io/captain-hook/docs/guide/workflows.html)
- **Packs** — the shipped `general`, `python`, and `go` hook packs — [guide](https://yasyf.github.io/captain-hook/docs/guide/packs.html)
- **Testing** — run `uvx capt-hook test --json` in CI so a regressed hook fails the build — [guide](https://yasyf.github.io/captain-hook/docs/guide/testing.html)

Read the [docs](https://yasyf.github.io/captain-hook/) for the full guide. Licensed under [PolyForm Noncommercial 1.0.0](https://github.com/yasyf/captain-hook/blob/main/LICENSE), free for noncommercial use.
