fix(update-check): ship version.txt inside skill so npx-skills-add installs find it (#26)

## Problem

The skill assumed the whole repo is the plugin root (`PLUGIN_ROOT` =
repo root, `version.txt` at repo root). That holds in Claude Code plugin
mode, where Claude Code injects `CLAUDE_PLUGIN_ROOT`. But the README's
recommended path —

```
npx skills add chainbase-labs/agentkey
```

— uses [vercel-labs/skills](https://github.com/vercel-labs/skills),
which only copies the `skills/agentkey/` subdirectory to
`~/.claude/skills/agentkey/`. The repo-root `version.txt` doesn't come
along.

In that layout, `check-update.sh`'s fallback

```bash
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." 2>/dev/null && pwd)}"
```

resolves `../../..` from `~/.claude/skills/agentkey/scripts/` to
`~/.claude/`, so `VERSION_FILE` points at `~/.claude/version.txt` —
which doesn't exist. `LOCAL_VERSION` ends up empty, the script silently
`exit 0`s at line 35, and **skills-CLI users never see upgrade prompts
at all**.

(Worse case: if some other tool ever drops a `~/.claude/version.txt`,
AgentKey would read it as its own version.)

This is an interface contract mismatch between the two distribution
models, not a one-sided bug — both are valid, but the skill needs to
work under either.

## Fix

1. Move `version.txt` into the skill directory
(`skills/agentkey/version.txt`) so it travels with whichever subset of
the repo gets copied.
2. In `check-update.sh`, anchor on `SKILL_ROOT` (one level above
`scripts/`) instead of an external `CLAUDE_PLUGIN_ROOT`. Both
distribution paths now resolve identically:
   - Plugin: `<repo>/skills/agentkey/version.txt`
   - Skills CLI: `~/.claude/skills/agentkey/version.txt`
3. Point release-please at the new path via `version-file`. The
`plugin.json` `extra-files` entry is unchanged.
4. Update docs (README, `docs/README_zh.md`, `.claude/CLAUDE.md`,
`SECURITY.md`, `claude-pr-review.yml`) to reflect the new path.

## Test plan

- [x] `bash skills/agentkey/scripts/check-update.sh` in repo: resolves
`SKILL_ROOT` to `<repo>/skills/agentkey`, reads `LOCAL_VERSION=1.2.2`
correctly.
- [x] Simulated skills-CLI install: `cp -r skills/agentkey /tmp/sim/`
then ran `bash /tmp/sim/agentkey/scripts/check-update.sh` with
`CLAUDE_PLUGIN_ROOT` unset → resolves `SKILL_ROOT=/tmp/sim/agentkey`,
reads `LOCAL_VERSION=1.2.2` correctly.
- [ ] After merge, the next release-please Release PR should bump
`skills/agentkey/version.txt` (along with `plugin.json` and
`CHANGELOG.md`) — please verify the Release PR diff before merging it.
- [ ] After release, on a fresh `npx skills add chainbase-labs/agentkey`
install, an out-of-date version should now correctly produce
`UPGRADE_AVAILABLE <old> <new>` and trigger the existing AskUserQuestion
prompt flow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
不白
2026-05-08 15:48:37 +08:00
committed by GitHub
parent 3850243cb3
commit bc740c8015
8 changed files with 34 additions and 27 deletions
+7 -6
View File
@@ -23,10 +23,10 @@ agentkey/
├── .mcp.json # Auto-registers AgentKey MCP when installed as a plugin
├── skills/agentkey/
│ ├── SKILL.md # Decision tree + routing rules (end-user facing)
── scripts/ # check-mcp / check-update helpers
├── scripts/
│ └── uninstall.sh # End-user cleanup helper
└── version.txt # Managed by release-please only
── scripts/ # check-mcp / check-update helpers
│ └── version.txt # Managed by release-please only — must live inside the skill so it survives `npx skills add`
└── scripts/
└── uninstall.sh # End-user cleanup helper
```
## Key Commands
@@ -48,11 +48,12 @@ git tag -d vX.Y.Z && git push origin :refs/tags/vX.Y.Z
gh release delete vX.Y.Z --repo chainbase-labs/agentkey --yes
```
Releases are driven by [release-please](https://github.com/googleapis/release-please): merged PRs with Conventional Commit messages (`feat:`, `fix:`, `feat!:`, etc.) update an open Release PR that bumps `version`, `.claude-plugin/plugin.json` version, and `CHANGELOG.md`. Merging the Release PR tags the release and creates the GitHub Release, which in turn triggers plugin updates for users.
Releases are driven by [release-please](https://github.com/googleapis/release-please): merged PRs with Conventional Commit messages (`feat:`, `fix:`, `feat!:`, etc.) update an open Release PR that bumps `skills/agentkey/version.txt`, `.claude-plugin/plugin.json` version, and `CHANGELOG.md`. Merging the Release PR tags the release and creates the GitHub Release, which in turn triggers plugin updates for users.
## Version & Release Rules
- `version`, `.claude-plugin/plugin.json` version, and `CHANGELOG.md` are managed by release-please based on Conventional Commits — never edit manually except via PR that intentionally amends them.
- `skills/agentkey/version.txt`, `.claude-plugin/plugin.json` version, and `CHANGELOG.md` are managed by release-please based on Conventional Commits — never edit manually except via PR that intentionally amends them.
- `version.txt` lives inside `skills/agentkey/` (not at repo root) so it travels with the skill when the Skills CLI copies the subdirectory. `release-please-config.json` points at this path via `version-file`.
- Tag format: `v` prefix (e.g. `v0.4.5`)
- Plugin updates trigger on **GitHub Release** publication, not on plain commits
- `npx skills update` pulls from the default branch, so main must always be shippable
+1 -1
View File
@@ -186,7 +186,7 @@ jobs:
### 4b. Files that shouldn't be touched directly
- `archive/**` — retired code
- `version.txt` — managed by release-please
- `skills/agentkey/version.txt` — managed by release-please
- `.release-please-manifest.json` — managed
- `CHANGELOG.md` — managed (unless part of a release PR
from release-please itself, which won't trigger this
+8 -8
View File
@@ -342,16 +342,16 @@ agentkey/
├── .mcp.json # Used when installed as a plugin
├── skills/agentkey/
│ ├── SKILL.md # Decision tree + routing rules
── scripts/ # check-mcp / check-update helpers
├── scripts/
│ ├── install.sh # One-command installer (mac/linux)
├── install.ps1 # Windows PowerShell installer
├── uninstall.sh # One-command uninstaller (mac/linux)
── uninstall.ps1 # Windows PowerShell uninstaller
└── version.txt # Managed by release-please
── scripts/ # check-mcp / check-update helpers
│ └── version.txt # Managed by release-please
└── scripts/
├── install.sh # One-command installer (mac/linux)
├── install.ps1 # Windows PowerShell installer
── uninstall.sh # One-command uninstaller (mac/linux)
└── uninstall.ps1 # Windows PowerShell uninstaller
```
**Release a new version (maintainers):** releases are cut automatically by [release-please](https://github.com/googleapis/release-please). Merging a PR with a `feat:` or `fix:` title opens a Release PR that bumps `version.txt`, `plugin.json`, and `CHANGELOG.md`. Merging the Release PR creates the tag + GitHub Release + uploads the `agentkey.skill` asset.
**Release a new version (maintainers):** releases are cut automatically by [release-please](https://github.com/googleapis/release-please). Merging a PR with a `feat:` or `fix:` title opens a Release PR that bumps `skills/agentkey/version.txt`, `plugin.json`, and `CHANGELOG.md`. Merging the Release PR creates the tag + GitHub Release + uploads the `agentkey.skill` asset.
</details>
+2 -2
View File
@@ -32,7 +32,7 @@ We follow coordinated disclosure. Once a fix is available, we publish a security
The skill ships two helper scripts that the agent invokes:
- **`skills/agentkey/scripts/check-update.sh`** — **notify-only**. At most every 60 minutes (12 hours once an upgrade is known), it calls `https://api.github.com/repos/chainbase-labs/agentkey/releases/latest`, compares the tag against the local `version.txt`, and prints `UPGRADE_AVAILABLE <old> <new>` if they differ. The script also honors a snooze file (`~/.config/agentkey/update-snoozed`, escalating 24h/48h/7d backoff) and a disable file (`~/.config/agentkey/update-disabled`); both are read-only from this script's perspective. The script never runs `git`, never writes to anything except its TMPDIR cache, and never executes downloaded code.
- **`skills/agentkey/scripts/check-update.sh`** — **notify-only**. At most every 60 minutes (12 hours once an upgrade is known), it calls `https://api.github.com/repos/chainbase-labs/agentkey/releases/latest`, compares the tag against the local `skills/agentkey/version.txt`, and prints `UPGRADE_AVAILABLE <old> <new>` if they differ. The script also honors a snooze file (`~/.config/agentkey/update-snoozed`, escalating 24h/48h/7d backoff) and a disable file (`~/.config/agentkey/update-disabled`); both are read-only from this script's perspective. The script never runs `git`, never writes to anything except its TMPDIR cache, and never executes downloaded code.
When the agent sees `UPGRADE_AVAILABLE` it surfaces an `AskUserQuestion` prompt (Yes / Always / Not now / Never). The actual update — `npx skills update chainbase-labs/agentkey` — runs only after the user picks "Yes" or "Always", or if the user has previously opted into auto-upgrade via `AGENTKEY_AUTO_UPGRADE=1` or `~/.config/agentkey/auto-upgrade`. The agent invokes that command via its own Bash tool, not via this script.
- **`skills/agentkey/scripts/check-mcp.sh`** — reads `~/.claude.json` and `~/.env.local` to verify the AgentKey MCP server is registered and the API key is present. **Read-only**; no network egress; output is a single status code.
@@ -73,7 +73,7 @@ The skill ships two helper scripts that the agent invokes:
Automated scanners (VirusTotal, ClawScan) may flag this skill as `Suspicious` due to two intentional patterns. We document them here so reviewers can verify intent:
1. **`check-update.sh` contacts GitHub.** Pattern may match "remote-controlled binary update" heuristics. **Why this is intentional:** the script is notify-only — it issues a single `GET https://api.github.com/repos/chainbase-labs/agentkey/releases/latest`, compares the tag against `version.txt`, prints a one-line status, and exits. It never writes anywhere except the cache file at `${TMPDIR}/agentkey-update-check`, never invokes `git`, and never executes downloaded code. Update execution lives entirely in the agent's interactive layer (`AskUserQuestion``npx skills update`), gated by explicit user consent or a previously persisted opt-in flag.
1. **`check-update.sh` contacts GitHub.** Pattern may match "remote-controlled binary update" heuristics. **Why this is intentional:** the script is notify-only — it issues a single `GET https://api.github.com/repos/chainbase-labs/agentkey/releases/latest`, compares the tag against `skills/agentkey/version.txt`, prints a one-line status, and exits. It never writes anywhere except the cache file at `${TMPDIR}/agentkey-update-check`, never invokes `git`, and never executes downloaded code. Update execution lives entirely in the agent's interactive layer (`AskUserQuestion``npx skills update`), gated by explicit user consent or a previously persisted opt-in flag.
2. **`check-mcp.sh` reads `*API_KEY*` env values.** Pattern matches "credential harvesting" heuristics. **Why this is intentional:** the read is local-only, never transmitted, and exists purely to confirm `AGENTKEY_API_KEY` is configured before the agent attempts an MCP call. The script's only output is a one-word status code (`MCP_OK` / `MCP_NO_KEY` / `MCP_NOT_CONFIGURED`); the key value itself is discarded.
If you operate a scanner and need additional context to triage, please email `support@chainbase.com`.
+8 -8
View File
@@ -342,16 +342,16 @@ agentkey/
├── .mcp.json # 作为插件安装时使用
├── skills/agentkey/
│ ├── SKILL.md # 决策树 & 路由规则
── scripts/ # check-mcp / check-update 辅助脚本
├── scripts/
│ ├── install.sh # 一键安装脚本mac/linux
├── install.ps1 # Windows PowerShell 安装脚本
├── uninstall.sh # 一键卸载脚本mac/linux
── uninstall.ps1 # Windows PowerShell 卸载脚本
└── version.txt # 由 release-please 自动维护
── scripts/ # check-mcp / check-update 辅助脚本
│ └── version.txt # 由 release-please 自动维护
└── scripts/
├── install.sh # 一键安装脚本mac/linux
├── install.ps1 # Windows PowerShell 安装脚本
── uninstall.sh # 一键卸载脚本mac/linux
└── uninstall.ps1 # Windows PowerShell 卸载脚本
```
**发布新版本Maintainer** 发版由 [release-please](https://github.com/googleapis/release-please) 自动触发。合并一个 `feat:``fix:` 的 PR 后release-please 会开一个 Release PR自动 bump `version.txt``plugin.json``CHANGELOG.md`。合并这个 Release PR 即会创建 tag + GitHub Release + 上传 `agentkey.skill` 产物。
**发布新版本Maintainer** 发版由 [release-please](https://github.com/googleapis/release-please) 自动触发。合并一个 `feat:``fix:` 的 PR 后release-please 会开一个 Release PR自动 bump `skills/agentkey/version.txt``plugin.json``CHANGELOG.md`。合并这个 Release PR 即会创建 tag + GitHub Release + 上传 `agentkey.skill` 产物。
</details>
+1
View File
@@ -4,6 +4,7 @@
"package-name": "agentkey-skill",
"release-type": "simple",
"changelog-path": "CHANGELOG.md",
"version-file": "skills/agentkey/version.txt",
"bump-minor-pre-major": false,
"bump-patch-for-minor-pre-major": false,
"include-v-in-tag": true,
+7 -2
View File
@@ -19,8 +19,13 @@ CACHE_TTL_UP_TO_DATE=3600 # 60 min — detect new releases quickly
CACHE_TTL_UPGRADE=43200 # 12 h — keep nagging once an upgrade is known
CURL_TIMEOUT=3
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." 2>/dev/null && pwd)}"
VERSION_FILE="$PLUGIN_ROOT/version.txt"
# Anchor on the skill directory itself, not on a "plugin root" — the skill is
# distributed two ways with different layouts: as a Claude Code plugin (whole
# repo) or via the Skills CLI (only `skills/agentkey/` is copied to
# ~/.claude/skills/agentkey/). Resolving relative to this script keeps both
# paths working without depending on CLAUDE_PLUGIN_ROOT.
SKILL_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd)"
VERSION_FILE="$SKILL_ROOT/version.txt"
CACHE_FILE="${TMPDIR:-/tmp}/agentkey-update-check"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/agentkey"
DISABLED_FILE="$CONFIG_DIR/update-disabled"