fix(ce-update): derive cache dir from CLAUDE_PLUGIN_ROOT parent (#645)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,21 +17,28 @@ version, and fix stale marketplace/cache state if it doesn't. Claude Code only.
|
||||
|
||||
## Pre-resolved context
|
||||
|
||||
The three sections below contain pre-resolved data. Only the **Plugin root
|
||||
path** determines whether this session is Claude Code — if it contains an error
|
||||
The sections below contain pre-resolved data. Only the **Plugin root path**
|
||||
determines whether this session is Claude Code — if it contains an error
|
||||
sentinel, an empty value, or a literal `${CLAUDE_PLUGIN_ROOT}` string, tell the
|
||||
user this skill only works in Claude Code and stop. The other two sections may
|
||||
user this skill only works in Claude Code and stop. The other sections may
|
||||
contain error sentinels even in valid Claude Code sessions; the decision logic
|
||||
below handles those cases.
|
||||
|
||||
`CLAUDE_PLUGIN_ROOT` points at the currently-loaded plugin version directory
|
||||
(e.g. `~/.claude/plugins/cache/<marketplace>/compound-engineering/<version>`),
|
||||
so the plugin cache directory that holds every cached version is its parent.
|
||||
|
||||
**Plugin root path:**
|
||||
!`echo "${CLAUDE_PLUGIN_ROOT}" 2>/dev/null || echo '__CE_UPDATE_ROOT_FAILED__'`
|
||||
|
||||
**Latest released version:**
|
||||
!`gh release list --repo EveryInc/compound-engineering-plugin --limit 30 --json tagName --jq '[.[] | select(.tagName | startswith("compound-engineering-v"))][0].tagName | sub("compound-engineering-v";"")' 2>/dev/null || echo '__CE_UPDATE_VERSION_FAILED__'`
|
||||
|
||||
**Plugin cache directory:**
|
||||
!`case "$(dirname "${CLAUDE_PLUGIN_ROOT:-}")" in */cache/*/compound-engineering) dirname "${CLAUDE_PLUGIN_ROOT}" ;; *) echo '__CE_UPDATE_CACHE_FAILED__' ;; esac`
|
||||
|
||||
**Cached version folder(s):**
|
||||
!`ls "${CLAUDE_PLUGIN_ROOT}/cache/compound-engineering-plugin/compound-engineering/" 2>/dev/null || echo '__CE_UPDATE_CACHE_FAILED__'`
|
||||
!`case "$(dirname "${CLAUDE_PLUGIN_ROOT:-}")" in */cache/*/compound-engineering) ls "$(dirname "${CLAUDE_PLUGIN_ROOT}")" 2>/dev/null ;; *) echo '__CE_UPDATE_CACHE_FAILED__' ;; esac`
|
||||
|
||||
## Decision logic
|
||||
|
||||
@@ -56,12 +63,12 @@ Take the **latest released version** and the **cached folder list**.
|
||||
- Tell the user: "compound-engineering **v{version}** is installed and up to date."
|
||||
|
||||
**Out of date or corrupted** — multiple cached folders exist, OR the single folder name
|
||||
does not match the latest version. Use the **Plugin root path** value from above to
|
||||
construct the delete path.
|
||||
does not match the latest version. Use the **Plugin cache directory** value from above
|
||||
as the delete path.
|
||||
|
||||
**Clear the stale cache:**
|
||||
```bash
|
||||
rm -rf "<plugin-root-path>/cache/compound-engineering-plugin/compound-engineering"
|
||||
rm -rf "<plugin-cache-directory>"
|
||||
```
|
||||
|
||||
Tell the user:
|
||||
|
||||
32
tests/skills/ce-update.test.ts
Normal file
32
tests/skills/ce-update.test.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { readFileSync } from "fs"
|
||||
import path from "path"
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
// Regression guard for https://github.com/EveryInc/compound-engineering-plugin/issues/556.
|
||||
//
|
||||
// `CLAUDE_PLUGIN_ROOT` is set by the Claude Code harness to the currently-loaded
|
||||
// plugin version directory (e.g. `~/.claude/plugins/cache/<marketplace>/compound-engineering/<version>`).
|
||||
// It is NOT the plugins cache root. Appending `/cache/<anything>/compound-engineering/`
|
||||
// onto it produces a path that never exists, which causes the cache-probe to fail
|
||||
// and emit the `__CE_UPDATE_CACHE_FAILED__` sentinel on every healthy install.
|
||||
//
|
||||
// This has regressed twice. Fail fast if the antipattern reappears.
|
||||
|
||||
describe("ce-update SKILL.md", () => {
|
||||
const skillPath = path.join(
|
||||
process.cwd(),
|
||||
"plugins/compound-engineering/skills/ce-update/SKILL.md",
|
||||
)
|
||||
const body = readFileSync(skillPath, "utf8")
|
||||
|
||||
test("does not append a /cache/<marketplace>/ suffix onto CLAUDE_PLUGIN_ROOT", () => {
|
||||
// Matches any `${CLAUDE_PLUGIN_ROOT}/cache/<segment>/` or `${CLAUDE_PLUGIN_ROOT}/cache/<segment>"`
|
||||
// pattern. The harness variable is already under `cache/<marketplace>/`,
|
||||
// so concatenating another `cache/...` segment is always wrong.
|
||||
const antiPattern = /\$\{CLAUDE_PLUGIN_ROOT\}\/cache\//
|
||||
expect(
|
||||
antiPattern.test(body),
|
||||
"ce-update/SKILL.md reintroduced the ${CLAUDE_PLUGIN_ROOT}/cache/... antipattern — derive the cache dir from dirname \"${CLAUDE_PLUGIN_ROOT}\" instead.",
|
||||
).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user