| name | resync-local-plugins |
| description | Use this skill after committing changes to this repo's plugins or skills, to re-pin the local-directory cyberplace marketplace so the installed plugins reflect the new HEAD. Triggers: 'resync the plugins', 'the marketplace is stale', 'my skill edit isn't loading', 'update the local plugins', after landing a commit under plugins/. |
| metadata | {"internal":true} |
Resync Local Plugins
This repo's cyberplace Claude Code marketplace is a directory source pointing at the local working tree. A directory marketplace snapshots at the committed git HEAD, not the working tree — so uncommitted plugin/skill edits never go live, and installed plugins stay pinned to whatever HEAD was current when they were last installed. Run this after committing plugin/skill changes to move the pin forward.
Not every plugin is a directory source. Each entry in .claude-plugin/marketplace.json declares its own source, and an npm-source plugin installs from the registry — this skill cannot move it to your local HEAD at all. Read the manifest before assuming a resync will help:
ROOT="$(git rev-parse --show-toplevel)"
node -e 'const m = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"))
for (const p of m.plugins)
console.log(p.name.padEnd(14), typeof p.source === "string" ? p.source : JSON.stringify(p.source))' \
"$ROOT/.claude-plugin/marketplace.json"
Anything not of the form ./plugins/<name> needs a release, not a resync. See the npm-source note below.
Preconditions
Steps
-
Re-snapshot the marketplace to current HEAD:
claude plugin marketplace update cyberplace
-
Re-pin every installed @cyberplace plugin. Plain install is idempotent and will NOT move the pin — you must uninstall then install:
for p in $(claude plugin list 2>/dev/null | grep -oE '[a-z-]+@cyberplace' | cut -d@ -f1 | sort -u); do
claude plugin uninstall "${p}@cyberplace"
claude plugin install "${p}@cyberplace"
done
-
Verify by content, not by metadata. Each @cyberplace key in installed_plugins.json maps to an array of install records across scopes — index 0 is usually a stale scope: "project" pin from an old cyberplace.worktrees/* checkout, so reading it reports failures that aren't real. Filter to scope: "user", then diff the installed tree against the source:
ROOT="$(git rev-parse --show-toplevel)"
HEAD=$(git -C "$ROOT" rev-parse --short=12 HEAD)
node -e '
const fs = require("fs")
const root = process.argv[1]
const inst = JSON.parse(fs.readFileSync(process.env.HOME + "/.claude/plugins/installed_plugins.json", "utf8"))
const mkt = JSON.parse(fs.readFileSync(root + "/.claude-plugin/marketplace.json", "utf8"))
const kind = Object.fromEntries(mkt.plugins.map(p => [p.name, typeof p.source === "string" ? "dir" : "npm"]))
for (const [key, recs] of Object.entries(inst.plugins)) {
if (!key.endsWith("@cyberplace")) continue
const name = key.slice(0, -"@cyberplace".length)
const rec = (recs || []).find(r => r.scope === "user")
if (!rec) { console.log([name, "-", "-", "NO-USER-INSTALL"].join("\t")); continue }
console.log([name, kind[name] || "?", (rec.gitCommitSha || "-").slice(0, 12), rec.installPath].join("\t"))
}' "$ROOT" | IFS=$ -r name knd sha ;
npm) pin= ;;
) [ = ] && pin= || pin= ;;
*) pin= ;;
n=$(diff -rq 2>/dev/null \
| grep \
| grep -vE | -l)
Notes
- Do not push or commit anything here — this only touches local plugin install state (
~/.claude/plugins/), never the repo.
- If a plugin fails to reinstall, report which one and stop; do not leave the set half-pinned.
npm-source plugins cannot be resynced
A plugin declared as {"source":"npm","package":"<pkg>"} installs from the registry, so no amount of marketplace update + reinstall will pick up local edits. Shipping a change to one means releasing it: changeset → version PR → release workflow → claude plugin uninstall/install <name>@cyberplace.
Two tells that you are looking at an npm-source plugin, both visible in installed_plugins.json:
- its user-scope record has no
gitCommitSha while every directory-sourced sibling has one
- its cache directory is named for the npm version (
…/sdd/0.0.0) rather than a commit sha
Do not trust the recorded version
Claude Code keys the cache directory on the version string, and reuses a directory whose name it already has — it has been observed writing 0.1.0 content into a directory still named 0.0.0, leaving version: "0.0.0" in the install record. The directory name and the version field are therefore both unreliable. The content diff in step 3 is the only honest check.