diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..19869ca --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "hotdata-cli", + "description": "Teach Claude the Hotdata CLI: workspaces, databases, SQL, ingest, search, analytics, and geospatial", + "version": "0.43.0", + "author": { + "name": "Hotdata", + "url": "https://www.hotdata.dev" + }, + "homepage": "https://www.hotdata.dev", + "repository": "https://github.com/hotdata-dev/hotdata-cli", + "license": "MIT" +} diff --git a/Cargo.toml b/Cargo.toml index dbd08e9..3164f1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,6 +105,7 @@ pre-release-replacements = [ { 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 }, # README.md no longer carries a hardcoded version badge — it tracks GitHub releases. ] diff --git a/README.md b/README.md index 447a22c..d953b69 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,21 @@ checks for a new release before API commands and offers to upgrade (set `HOTDATA_NO_UPDATE_CHECK=1` to skip). Enable tab completion with `hotdata manage completions bash|zsh|fish`. +### Claude Code plugin + +To get the agent skill through Claude Code's plugin manager instead, which +updates it with each release: + +``` +/plugin marketplace add hotdata-dev/claude-plugins +/plugin install hotdata-cli@hotdata +``` + +The plugin checks for the CLI when a session starts and offers to install it if +it's missing. Use either the plugin or the installer's skill, not both: if you +installed the CLI with the script above, install it with `HOTDATA_NO_SKILLS=1` +or Claude Code will list the skill twice. + ## Quickstart ```sh diff --git a/hooks/check-cli.sh b/hooks/check-cli.sh new file mode 100755 index 0000000..9ada0cf --- /dev/null +++ b/hooks/check-cli.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +# SessionStart hook for the hotdata-cli Claude Code plugin. +# +# The plugin ships the skill, not the binary. Stay silent when the CLI is +# reachable. When it isn't, stdout becomes session context asking Claude to +# offer the installer rather than run it: piping curl into sh on every session +# start is not something users agreed to by installing a skill. + +set -u + +DIST_BIN_DIR="$HOME/.hotdata/cli" + +if command -v hotdata >/dev/null 2>&1; then + exit 0 +fi + +# Without Homebrew the installer puts the binary in ~/.hotdata/cli, which is +# not on PATH until the user's shell profile is reloaded. +if [ -x "$DIST_BIN_DIR/hotdata" ]; then + if [ -n "${CLAUDE_ENV_FILE:-}" ]; then + echo "export PATH=\"$DIST_BIN_DIR:\$PATH\"" >> "$CLAUDE_ENV_FILE" + fi + exit 0 +fi + +# HOTDATA_NO_SKILLS=1: the plugin already provides the skill, so the installer +# must not add a second copy to ~/.claude/skills. +cat <<'EOF' +The hotdata CLI is not installed. Before running any hotdata command, tell the +user and ask whether to install it. If they agree, run: + + curl -fsSL https://hotdata.dev/install | HOTDATA_NO_SKILLS=1 sh + +If the binary lands in ~/.hotdata/cli, call it as ~/.hotdata/cli/hotdata for the +rest of this session. +EOF +exit 0 diff --git a/hooks/hooks.json b/hooks/hooks.json new file mode 100644 index 0000000..411d41e --- /dev/null +++ b/hooks/hooks.json @@ -0,0 +1,14 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/check-cli.sh" + } + ] + } + ] + } +} diff --git a/scripts/check-skills.sh b/scripts/check-skills.sh index de84697..6671671 100755 --- a/scripts/check-skills.sh +++ b/scripts/check-skills.sh @@ -12,8 +12,9 @@ # flag in its --help, must be named in 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:` must equal Cargo.toml -# (finish phase only — prepare runs before cargo-release bumps them). +# 3. Version: every SKILL.md frontmatter `version:`, and the Claude Code +# plugin manifest's `version`, must equal Cargo.toml (finish phase only — +# prepare runs before cargo-release bumps them). # # Usage: # scripts/check-skills.sh [--require-version] @@ -125,6 +126,11 @@ if [ "$REQUIRE_VERSION" = 1 ]; then fail=1 fi done + pv="$(sed -nE 's/^ *"version": "([^"]+)".*/\1/p' .claude-plugin/plugin.json | head -1)" + if [ "$pv" != "$crate" ]; then + echo "error: .claude-plugin/plugin.json declares version $pv, Cargo.toml is $crate" >&2 + fail=1 + fi [ "$fail" = 0 ] && echo "→ skills: version ok ($crate)" fi diff --git a/skills/hotdata/SKILL.md b/skills/hotdata/SKILL.md index 35c5806..f904c1f 100644 --- a/skills/hotdata/SKILL.md +++ b/skills/hotdata/SKILL.md @@ -491,10 +491,10 @@ Upgrades the CLI in place to the latest release (`brew upgrade` for Homebrew ins If `hotdata` is not installed at all (not on PATH and not at `~/.hotdata/cli/hotdata`), install it with: ```sh -curl --proto '=https' --tlsv1.2 -LsSf https://github.com/hotdata-dev/hotdata-cli/releases/latest/download/install.sh | sh +curl -fsSL https://hotdata.dev/install | sh ``` -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. +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. 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.