Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .claude-plugin/icon.png
Binary file not shown.
2 changes: 1 addition & 1 deletion .github/workflows/cut-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# tag.
#
# The skill-drift check inside `prepare` is not bypassed here (no
# SKIP_SKILL_DRIFT). If the job fails on it, update skills/ on main first and
# SKIP_SKILL_DRIFT). If the job fails on it, update plugin/skills/ on main first and
# dispatch again.

name: Cut Release
Expand Down
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ homebrew_formula = "hotdata-dev/tap/cli"
pre-release-hook = ["git-cliff", "--unreleased", "--tag", "v{{version}}", "--prepend", "CHANGELOG.md"]
publish = false
pre-release-replacements = [
{ file = "skills/hotdata/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = "skills/hotdata/subskills/search/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = "skills/hotdata/subskills/analytics/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = "skills/hotdata/subskills/geospatial/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = ".claude-plugin/plugin.json", search = "\"version\": \"[^\"]+\"", replace = "\"version\": \"{{version}}\"", exactly = 1 },
{ file = "plugin/skills/hotdata/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = "plugin/skills/hotdata/subskills/search/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = "plugin/skills/hotdata/subskills/analytics/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = "plugin/skills/hotdata/subskills/geospatial/SKILL.md", search = "^version: .+", replace = "version: {{version}}", exactly = 1 },
{ file = "plugin/.claude-plugin/plugin.json", search = "\"version\": \"[^\"]+\"", replace = "\"version\": \"{{version}}\"", exactly = 1 },
# README.md no longer carries a hardcoded version badge — it tracks GitHub releases.
]

Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,15 @@ will list the skill twice.

What the plugin runs, sends, and fetches:

- **SessionStart hook** (`hooks/check-cli.sh`, Claude Code and Cowork only):
- **SessionStart hook** (`plugin/hooks/check-cli.sh`, Claude Code and Cowork only):
checks whether `hotdata` is on `PATH` or in `~/.hotdata/cli`. If it's only in
`~/.hotdata/cli`, it adds that folder to `PATH` for the session. If it's
missing, it asks Claude to offer to install the CLI. The hook makes no
network requests, installs nothing, and sends no data.
- **Installer**, only after you agree: `curl -fsSL https://hotdata.dev/install |
HOTDATA_NO_SKILLS=1 sh` downloads the CLI from this repository's GitHub
releases, or installs it with Homebrew if `brew` is present.
- **Installer**, only after you agree: `brew install hotdata-dev/tap/cli` when
`brew` is present. Otherwise Claude downloads `https://hotdata.dev/install` to
a file, shows it to you, and runs it with `HOTDATA_NO_SKILLS=1`. It installs
the CLI from this repository's GitHub releases.
- **The skill** teaches Claude to run `hotdata` commands. Those commands talk to
the Hotdata API (`https://api.hotdata.dev/v1`, or `HOTDATA_API_URL`) with your
Hotdata credentials, and send the queries, files, and data source settings
Expand Down
4 changes: 2 additions & 2 deletions dist-workspace.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ install-updater = false

[[dist.extra-artifacts]]
artifacts = ["skills.tar.gz"]
build = ["tar", "-czf", "skills.tar.gz", "skills/"]
build = ["tar", "-czf", "skills.tar.gz", "-C", "plugin", "skills/"]

[[dist.extra-artifacts]]
artifacts = ["SKILL.md"]
build = ["cp", "skills/hotdata/SKILL.md", "SKILL.md"]
build = ["cp", "plugin/skills/hotdata/SKILL.md", "SKILL.md"]

[[dist.extra-artifacts]]
artifacts = ["install.sh"]
Expand Down
10 changes: 5 additions & 5 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ gh workflow run cut-release.yml -f version=0.40.0

The workflow acts as the `hotdata-automation` GitHub App. A PR opened or a tag pushed with the default `GITHUB_TOKEN` would trigger no other workflows.

If the `prepare` job fails on the skill check, the skills lag the code: update `skills/` on `main` first (see below), then dispatch again. CI does not set `SKIP_SKILL_DRIFT=1`.
If the `prepare` job fails on the skill check, the skills lag the code: update `plugin/skills/` on `main` first (see below), then dispatch again. CI does not set `SKIP_SKILL_DRIFT=1`.

## Manual path

Expand All @@ -29,10 +29,10 @@ If you run `prepare` locally, the merge of its `release/<version>` PR still trig

## Skill check

Both phases run `scripts/check-skills.sh`. The agent skills under `skills/` ship in every release (`skills.tar.gz`, `SKILL.md`), so the script refuses to release when they lag the code:
Both phases run `scripts/check-skills.sh`. The agent skills under `plugin/skills/` ship in every release (`skills.tar.gz`, `SKILL.md`), so the script refuses to release when they lag the code:

- **Drift** — if `src/` or `README.md` changed since the last `v*` tag and `skills/` did not, the run fails and lists the commits to review. Update the skills and commit, or set `SKIP_SKILL_DRIFT=1` (local runs only) when the release is verified to be skill-neutral.
- **Coverage** — every subcommand the built binary exposes must appear as `hotdata <group> <sub>` somewhere in `skills/**/*.md`, and every long flag in its `--help` must be mentioned too. CI runs this check on every pull request (with the drift check skipped), so a new command or flag cannot merge undocumented.
- **Drift** — if `src/` or `README.md` changed since the last `v*` tag and `plugin/skills/` did not, the run fails and lists the commits to review. Update the skills and commit, or set `SKIP_SKILL_DRIFT=1` (local runs only) when the release is verified to be skill-neutral.
- **Coverage** — every subcommand the built binary exposes must appear as `hotdata <group> <sub>` somewhere in `plugin/skills/**/*.md`, and every long flag in its `--help` must be mentioned too. CI runs this check on every pull request (with the drift check skipped), so a new command or flag cannot merge undocumented.
- **Version** (`finish` only) — every `SKILL.md` frontmatter `version:` must equal the crate version.

The drift check is a reminder, not a judge of prose. Before cutting a release, read the commits since the last tag and update `skills/hotdata/SKILL.md` and the subskills to match the current `--help` output and behavior.
The drift check is a reminder, not a judge of prose. Before cutting a release, read the commits since the last tag and update `plugin/skills/hotdata/SKILL.md` and the subskills to match the current `--help` output and behavior.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@
"repository": "https://github.com/hotdata-dev/hotdata-cli",
"license": "MIT",
"privacyPolicyUrl": "https://www.hotdata.dev/privacy-policy",
"termsOfServiceUrl": "https://www.hotdata.dev/terms-of-service",
"icon": "./.claude-plugin/icon.png"
"termsOfServiceUrl": "https://www.hotdata.dev/terms-of-service"
}
File renamed without changes.
File renamed without changes.
13 changes: 10 additions & 3 deletions skills/hotdata/SKILL.md → plugin/skills/hotdata/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -489,13 +489,20 @@ hotdata manage upgrade

Upgrades the CLI in place to the latest release (`brew upgrade` for Homebrew installs, otherwise a direct binary download), refreshing bundled skills to match. After a successful upgrade, re-run your command.

If `hotdata` is not installed at all (not on PATH and not at `~/.hotdata/cli/hotdata`), install it with:
If `hotdata` is not installed at all (not on PATH and not at `~/.hotdata/cli/hotdata`), ask the user before installing. When `brew` is present, install with Homebrew:

```sh
curl -fsSL https://hotdata.dev/install | sh
brew install hotdata-dev/tap/cli
```

The installer uses Homebrew (`brew install hotdata-dev/tap/cli`) when `brew` is present, otherwise it places a prebuilt binary at `~/.hotdata/cli/hotdata`, and then installs this skill. `HOTDATA_NO_BREW=1` forces the prebuilt path; `HOTDATA_NO_SKILLS=1` skips the skill step. When this skill came from the `hotdata-cli` Claude Code plugin, always set `HOTDATA_NO_SKILLS=1` (`curl -fsSL https://hotdata.dev/install | HOTDATA_NO_SKILLS=1 sh`) so the installer does not add a second copy.
Without Homebrew, download the installer to a file, show it to the user, and run it only after they agree:

```sh
curl -fsSL https://hotdata.dev/install -o hotdata-install.sh
HOTDATA_NO_SKILLS=1 sh hotdata-install.sh
```

The installer places a prebuilt binary at `~/.hotdata/cli/hotdata` (or uses Homebrew if present; `HOTDATA_NO_BREW=1` forces the prebuilt path) and then installs this skill unless `HOTDATA_NO_SKILLS=1` is set. When this skill came from the `hotdata-cli` Claude Code plugin, always set `HOTDATA_NO_SKILLS=1` so the installer does not add a second copy.

A newer release can be incompatible with the API, so in an **interactive terminal** the CLI checks for a new release before running any API-touching command and prompts to upgrade. Declining (or `Ctrl-D`) exits without running the command — `hotdata manage upgrade` is then required to continue. The check is a **no-op in non-interactive sessions** (no TTY, `--no-input`, or `HOTDATA_NO_UPDATE_CHECK` set), so typical agent and CI usage is never blocked; set `HOTDATA_NO_UPDATE_CHECK=1` to disable it entirely.

Expand Down
18 changes: 9 additions & 9 deletions scripts/check-skills.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
# Runs before a release branch is cut and again before the tag is pushed.
# Three checks, all mechanical:
#
# 1. Drift: if src/ changed since the last release tag, skills/ must have
# 1. Drift: if src/ changed since the last release tag, plugin/skills/ must have
# changed too. The script cannot judge prose, so this is the reminder
# that a code change needs its SKILL.md follow-up. Override with
# SKIP_SKILL_DRIFT=1 when a release is verified to be skill-neutral.
# 2. Coverage: every subcommand the built binary exposes, and every long
# flag in its --help, must be named in skills/**/*.md, so a new command
# flag in its --help, must be named in plugin/skills/**/*.md, so a new command
# or flag cannot ship undocumented. Hidden flags are not in --help and
# are not checked.
# 3. Version: every SKILL.md frontmatter `version:`, and the Claude Code
Expand All @@ -27,7 +27,7 @@ REQUIRE_VERSION=0
[ "${1:-}" = "--require-version" ] && REQUIRE_VERSION=1

BIN="${HOTDATA_BIN:-target/debug/hotdata}"
SKILL_FILES=(skills/hotdata/SKILL.md skills/hotdata/subskills/*/SKILL.md)
SKILL_FILES=(plugin/skills/hotdata/SKILL.md plugin/skills/hotdata/subskills/*/SKILL.md)
fail=0

# --- 1. drift ---------------------------------------------------------------
Expand All @@ -36,16 +36,16 @@ if [ -z "$LAST_TAG" ]; then
echo "→ skills: no previous v* tag; skipping drift check"
else
src_changes="$(git log --oneline "$LAST_TAG"..HEAD -- src/ README.md | grep -v -E 'chore\(deps\)|chore: Release' || true)"
skill_changes="$(git log --oneline "$LAST_TAG"..HEAD -- skills/ || true)"
skill_changes="$(git log --oneline "$LAST_TAG"..HEAD -- plugin/skills/ || true)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: (not blocking) The rename commit in this PR touches every file under plugin/skills/. git log "$LAST_TAG"..HEAD -- plugin/skills/ therefore returns that commit for the next release. The drift check then passes even if no skill content changed. Before the next release, review the skills against the src/ commits by hand. A filter such as --diff-filter=AM alone does not help, because git log without --follow reports the move as add/delete.

if [ -n "$src_changes" ] && [ -z "$skill_changes" ]; then
if [ "${SKIP_SKILL_DRIFT:-}" = "1" ]; then
echo "→ skills: code changed since $LAST_TAG without a skills change (SKIP_SKILL_DRIFT=1, continuing)"
else
echo "error: code changed since $LAST_TAG but skills/ did not." >&2
echo "error: code changed since $LAST_TAG but plugin/skills/ did not." >&2
echo "" >&2
echo "$src_changes" | sed 's/^/ /' >&2
echo "" >&2
echo "Review each commit against skills/hotdata/SKILL.md and the subskills, update them," >&2
echo "Review each commit against plugin/skills/hotdata/SKILL.md and the subskills, update them," >&2
echo "and commit. If none of these change agent-visible behavior, rerun with SKIP_SKILL_DRIFT=1." >&2
fail=1
fi
Expand Down Expand Up @@ -79,7 +79,7 @@ walk() {
}
walk

skill_text="$(cat "${SKILL_FILES[@]}" skills/hotdata/references/*.md skills/hotdata/subskills/*/references/*.md)"
skill_text="$(cat "${SKILL_FILES[@]}" plugin/skills/hotdata/references/*.md plugin/skills/hotdata/subskills/*/references/*.md)"
missing=()
for leaf in "${leaves[@]}"; do
if ! grep -qF "hotdata $leaf" <<<"$skill_text"; then
Expand Down Expand Up @@ -126,9 +126,9 @@ if [ "$REQUIRE_VERSION" = 1 ]; then
fail=1
fi
done
pv="$(sed -nE 's/^ *"version": "([^"]+)".*/\1/p' .claude-plugin/plugin.json | head -1)"
pv="$(sed -nE 's/^ *"version": "([^"]+)".*/\1/p' plugin/.claude-plugin/plugin.json | head -1)"
if [ "$pv" != "$crate" ]; then
echo "error: .claude-plugin/plugin.json declares version $pv, Cargo.toml is $crate" >&2
echo "error: plugin/.claude-plugin/plugin.json declares version $pv, Cargo.toml is $crate" >&2
fail=1
fi
[ "$fail" = 0 ] && echo "→ skills: version ok ($crate)"
Expand Down
Loading