Files
AgentHub/scripts
nntrivi2001 29a5da3e4e fix(bootstrap): stop a retired plugin build from downgrading the dashboard
`claude plugin update` unpacks the new version into a new cache directory and
says "Restart to apply changes". A Claude Code process that was already running
does not: it keeps loading the OLD directory, hooks and all.

Every session such a process opened then ran the OLD plugin-bootstrap, which saw
a state.json naming a different root, concluded the server had to be replaced,
repointed agenthub.service at itself and restarted the dashboard onto the
version the user had just upgraded away from. The dashboard reported the old
version and offered the update again — once per session, forever, while
installed_plugins.json and the cache both said the new version was installed.
A /agenthub-update from that session "succeeded" and changed nothing.

- `supersededBy()` reads Claude Code's installed_plugins.json. When a SIBLING
  cache directory is the recorded install, the bootstrap logs one line and
  returns "superseded" — no deps, no unit, no server. `--force` does not
  override it: the self-update chain forces from the NEW directory, which is
  never superseded, so the only --force refused is a deliberate downgrade.
  It cannot hand over either — the process it would replace is the one loading
  it — so the log line says to restart Claude Code.
- Matched by sibling directory, not by the <plugin>@<marketplace> key: correct
  without parsing a cache path or trusting a name. A phantom entry (installPath
  that no longer exists) is ignored rather than wedging the bootstrap shut.

Also adds `pruneRetiredPluginRoots()`, since the same updates leave a full copy
of the repo — node_modules and all — per release; twelve of them measured 1.7 GB
on one machine. It runs after writeState, so a bad prune cannot cost the record
of a good bootstrap, and it protects four independent readings of "in use":
this build, every installPath Claude Code records, the directory
agenthub.service names (`unitRoot`, foreign unit included), and any directory
still named in the user's settings.json hooks — that last one is the un-restarted
process above, seen from the other side. Plus the 2 newest survivors as a
rollback cushion, and nothing at all outside ~/.claude/plugins/cache/.
2026-09-15 10:30:00 +07:00
..