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 devNLP 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 pytestThe example hooks in docs/examples/ carry inline tests too:
uv run capt-hook --hooks docs/examples testDocs
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 previewBuild 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 --helpUsing a local checkout
From a consumer project, point uv run --project at your clone:
uv run --project path/to/captain-hook capt-hook testLive 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.1The 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.