diff --git a/.claude-plugin/icon.png b/.claude-plugin/icon.png deleted file mode 100644 index c19fd97b..00000000 Binary files a/.claude-plugin/icon.png and /dev/null differ diff --git a/.github/workflows/cut-release.yml b/.github/workflows/cut-release.yml index 2fb093e5..27a5efa5 100644 --- a/.github/workflows/cut-release.yml +++ b/.github/workflows/cut-release.yml @@ -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 diff --git a/Cargo.toml b/Cargo.toml index ae5968f0..2cde711f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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. ] diff --git a/README.md b/README.md index f6de5344..26baa46e 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/dist-workspace.toml b/dist-workspace.toml index 6b726a55..9c3662b3 100644 --- a/dist-workspace.toml +++ b/dist-workspace.toml @@ -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"] diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 3f0a34ff..ff5873ac 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -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 @@ -29,10 +29,10 @@ If you run `prepare` locally, the merge of its `release/` 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 ` 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 ` 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. diff --git a/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json similarity index 92% rename from .claude-plugin/plugin.json rename to plugin/.claude-plugin/plugin.json index 7d64de45..1325c339 100644 --- a/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -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" } diff --git a/hooks/check-cli.sh b/plugin/hooks/check-cli.sh similarity index 100% rename from hooks/check-cli.sh rename to plugin/hooks/check-cli.sh diff --git a/hooks/hooks.json b/plugin/hooks/hooks.json similarity index 100% rename from hooks/hooks.json rename to plugin/hooks/hooks.json diff --git a/skills/hotdata/SKILL.md b/plugin/skills/hotdata/SKILL.md similarity index 98% rename from skills/hotdata/SKILL.md rename to plugin/skills/hotdata/SKILL.md index 4293afd5..18b65e93 100644 --- a/skills/hotdata/SKILL.md +++ b/plugin/skills/hotdata/SKILL.md @@ -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. diff --git a/skills/hotdata/references/DATA_MODEL.template.md b/plugin/skills/hotdata/references/DATA_MODEL.template.md similarity index 100% rename from skills/hotdata/references/DATA_MODEL.template.md rename to plugin/skills/hotdata/references/DATA_MODEL.template.md diff --git a/skills/hotdata/references/MODEL_BUILD.md b/plugin/skills/hotdata/references/MODEL_BUILD.md similarity index 100% rename from skills/hotdata/references/MODEL_BUILD.md rename to plugin/skills/hotdata/references/MODEL_BUILD.md diff --git a/skills/hotdata/references/WORKFLOWS.md b/plugin/skills/hotdata/references/WORKFLOWS.md similarity index 100% rename from skills/hotdata/references/WORKFLOWS.md rename to plugin/skills/hotdata/references/WORKFLOWS.md diff --git a/skills/hotdata/subskills/analytics/SKILL.md b/plugin/skills/hotdata/subskills/analytics/SKILL.md similarity index 100% rename from skills/hotdata/subskills/analytics/SKILL.md rename to plugin/skills/hotdata/subskills/analytics/SKILL.md diff --git a/skills/hotdata/subskills/analytics/references/WORKFLOWS.md b/plugin/skills/hotdata/subskills/analytics/references/WORKFLOWS.md similarity index 100% rename from skills/hotdata/subskills/analytics/references/WORKFLOWS.md rename to plugin/skills/hotdata/subskills/analytics/references/WORKFLOWS.md diff --git a/skills/hotdata/subskills/geospatial/SKILL.md b/plugin/skills/hotdata/subskills/geospatial/SKILL.md similarity index 100% rename from skills/hotdata/subskills/geospatial/SKILL.md rename to plugin/skills/hotdata/subskills/geospatial/SKILL.md diff --git a/skills/hotdata/subskills/geospatial/references/functions.md b/plugin/skills/hotdata/subskills/geospatial/references/functions.md similarity index 100% rename from skills/hotdata/subskills/geospatial/references/functions.md rename to plugin/skills/hotdata/subskills/geospatial/references/functions.md diff --git a/skills/hotdata/subskills/search/SKILL.md b/plugin/skills/hotdata/subskills/search/SKILL.md similarity index 100% rename from skills/hotdata/subskills/search/SKILL.md rename to plugin/skills/hotdata/subskills/search/SKILL.md diff --git a/skills/hotdata/subskills/search/references/INDEXES.md b/plugin/skills/hotdata/subskills/search/references/INDEXES.md similarity index 100% rename from skills/hotdata/subskills/search/references/INDEXES.md rename to plugin/skills/hotdata/subskills/search/references/INDEXES.md diff --git a/scripts/check-skills.sh b/scripts/check-skills.sh index 66716711..959ab984 100755 --- a/scripts/check-skills.sh +++ b/scripts/check-skills.sh @@ -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 @@ -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 --------------------------------------------------------------- @@ -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)" 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 @@ -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 @@ -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)"