habit-hooks-prompting
Write or revise a habit-hooks coaching prompt. Use when a linter / knip / jscpd rule fires and the agent's default fix is wrong…
Cut a new release of the habit-hooks packages. Use when asked to release, publish, or bump the version. Reviews what lands, enforces the in-sync versioning rule, validates the changelog, and drives the tag-triggered PyPI publish.
$ npx -y skills add habit-hooks/habit-hooks --skill release-habit-hooks --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/release-habit-hooksContext preview
The summary Claude sees to decide when to auto-load this skill.
Cut a new release of the habit-hooks packages. Use when asked to release, publish, or bump the version. Reviews what lands, enforces the in-sync versioning rule, validates the changelog, and drives the tag-triggered PyPI publish.
name: release-habit-hooks description: "Cut a new release of the habit-hooks packages. Use when asked to release, publish, or bump the version. Reviews what lands, enforces the in-sync versioning rule, validates the changelog, and drives the tag-triggered PyPI publish."
Seven packages ship from this repo: `habit-hooks` (core), `habit-hooks-generic`, `habit-hooks-python`, `habit-hooks-typescript`, `habit-hooks-php`, `habit-hooks-java`, `habit-hooks-ruby`. A `v*` tag triggers `.github/workflows/release.yml`, which builds all seven and publishes each with `skip-existing: true` — so an unchanged package at an already-published version is simply skipped. Publishing is irreversible: **never tag or push without Ivett's explicit go-ahead.**
git describe --tags --abbrev=0 # last release tag git log <lastTag>..HEAD --oneline
For each package, diff since the tag matching its *current* version and decide if it changed:
git diff --stat <lastTag>..HEAD -- src/ # core git diff --stat <tagForItsVersion>..HEAD -- plugins/<name>/
A plugin's shipped package data (`eslint.config.mjs`, `knip.json`, `.jscpd.json`, `ruff.toml`, sensors/guides) is consumer-facing — a change there means that plugin changed. Read every diff; classify each as consumer-facing vs internal.
semver bump. Decide the bump from what actually landed: **patch** for fixes/internal, **minor** only for genuinely new backward-compatible features.
habit-hooks` upgrades a plugin only when the new core stops being satisfied by the installed one, so a plugin left behind hands someone the new core with last release's plugins. `tests/test_the_plugin_floor_tracks_the_release.py` gates it.
`plugins/*/pyproject.toml`, **and raise each `habit-hooks-<name>` floor in the core's `dependencies` and `optional-dependencies` to the new minor.** Then `uv lock` and confirm the lock shows the new versions. The same test gates both halves, so a forgotten floor fails the suite rather than shipping quietly.
`X.Y.0rc1` sorts *below* `X.Y`, so `~=X.Y` makes a release candidate declare floors its own plugins cannot satisfy — and no `--pre` flag lifts it, because it is the specifier excluding the version, not a policy about candidates. The same spelling serves every rc and the final release, so nothing is rewritten at the tag.
all pre-releases of the same `X.Y.0`; only the `version` lines move between them. Iterate as far as the reporters need.
`CHANGELOG.md` must have a heading for **every** released version — no lingering `## Unreleased` content that has actually shipped (this has bitten us: 1.0.0–1.0.2 all sat under a stale "Unreleased"). Before releasing:
`## X.Y.Z` section.
uv run pytest -q uv build --all-packages --out-dir dist/_relcheck # mirrors release.yml; check artifact versions
All green. Then present the tag command as a ⚠️ checkpoint for Ivett to run — do not tag or push yourself:
git tag vX.Y.Z && git push origin main --tags
Anyone handed an install command already has habit-hooks — that is *why* they are reading it. So test the command from their state, not from an empty machine.
**Half of this belongs before the tag, and half cannot.** The upgrade *mechanics* need only the wheels, which `uv build` has already produced in step 4 — so run them from `dist/_relcheck` while the tag can still be moved. Only the cache behaviour below needs a live PyPI, because the stale cache does not exist until the version does.
Before the tag, against the built wheels:
export UV_TOOL_DIR=$(mktemp -d) UV_TOOL_BIN_DIR=$(mktemp -d) uv tool install 'habit-hooks[typescript]==<the version they are on now>' uv tool install --force --find-links dist/_relcheck 'habit-hooks[typescript]==X.Y.Z' "$UV_TOOL_BIN_DIR/habit-hooks" --version # did it actually move?
Every package must move together in that output — a core that upgrades while a plugin stays behind is the failure the in-sync rule exists to stop, and it is visible here and nowhere else. Then run the installed binary against a throwaway project and read a real finding out of it, so the answer covers the plugins the core just pulled in, not only its own version string.
After the publish, against PyPI:
uv tool install <the exact command about to be published> "$UV_TOOL_BIN_DIR/habit-hooks" --version
`uv tool install` **does not upgrade an already-installed tool.** It prints "already installed" and exits 0. It needs `--force`. 1.4.0rc1 was announced on #133 and #134 without it; both reporters retested 1.3.1, and one reported a 1.3.1 bug back as an rc failure.
**`--force` is not enough either: it resolves from uv's cached index.** Anyone who installed the previous version minutes ago has a cache that predates the one just published, and uv answers `No solution found … there is no version of habit-hooks[typescript]==X.Y.Z` for a version sitting on PyPI. So the published command is
uv tool install --force --refresh 'habit-hooks[<extras>]==X.Y.Z'
and all three parts earn their place: the pin, because uv will not upgrade to a pre-release without one; `--force`, because it skips a
Turn best-practice coding advice into AI habits. Stop reciting software engineering literature to your AI agent.
Write or revise a habit-hooks coaching prompt. Use when a linter / knip / jscpd rule fires and the agent's default fix is wrong…
Spawn a reviewer sub-agent to assess a change set against habit-hooks's coding principles. Use AFTER habit-hooks reports clean —…
Write or trim habit-hooks documentation under docs/. Use when creating, editing, reviewing or cutting any doc in this repo —…