Development

Working on captain-hook itself. For using captain-hook in your project, see Installation.

Setup

Clone the repo and sync the dev extra:

git clone https://github.com/yasyf/captain-hook
cd captain-hook
uv sync --extra dev

NLP assets

Tests that exercise NLP signals need the spaCy English model and WordNet data:

uv run python -c "from captain_hook.util.model_cache import ensure_nlp_resources; ensure_nlp_resources()"

This mirrors what CI provisions — see .github/workflows/ci.yml.

Tests

Run the suite:

uv run pytest

The example hooks in docs/examples/ carry inline tests too:

uv run capt-hook --hooks docs/examples test

Docs

The docs are Great Docs (Quarto), published to GitHub Pages. The API reference is generated from docstrings at build time; the CLI reference comes from the Click app.

Sync the docs group alongside the dev extra, then preview locally:

uv sync --extra dev --group docs
uv run great-docs preview

Build and smoke-test the wheel

Build the wheel, then install and exercise it in a throwaway venv:

uv build
uv venv --seed .wheel-smoke
uv pip install --python .wheel-smoke/bin/python dist/*.whl
.wheel-smoke/bin/capt-hook --help

Using a local checkout

From a consumer project, point uv run --project at your clone:

uv run --project path/to/captain-hook capt-hook test

Live hooks in this checkout

This repo dispatches its own hooks through "$CLAUDE_PROJECT_DIR"/.venv/bin/hook, wired by the committed .claude/settings.json. The shim execs the app’s capt-hookd run <Event>, so those hooks run on the installed app’s wheel, not this checkout’s .venv. To exercise checkout code live, build and install the app from it.

Releasing

Add the CHANGELOG entry and commit it, together with uv.lock when a dependency change moved it. Then tag the new version and push. .github/workflows/release-pypi.yml builds the sdist and wheel, publishes to PyPI via trusted publishing, cuts a GitHub release, builds and signs the universal Captain Hook.app with the exact same versioned capt-hookd, and publishes the stable release as the bundled application resource in the yasyf/tap/captain-hook formula. It also commits captain_hook/.claude-plugin/plugin.json back to main with its version synced to the tag — that bump is what makes consumer plugin caches pick up the release, so no manual plugin.json edit is needed (a hand-bump before tagging is harmless; the sync no-ops):

git tag v0.9.1
git push origin v0.9.1

The release workflow re-derives the wheel version from the tag via uv version --frozen and stamps that version into the signed host — the committed pyproject.toml version is an inert 0.0.0 sentinel, so there is nothing to bump there either. The wheel, plugin, and app are one exact release unit; do not publish or document them as independently compatible artifacts. After the run goes green, git pull: the plugin.json sync commit landed on main, so your local branch is one commit behind.

See also

  • Examples holds the runnable hooks whose inline tests uv run capt-hook --hooks docs/examples test exercises
  • Reference covers the API and CLI surface the docs build renders