[FEATURE] Machine-readable provenance for installed plugins (version + source ref/SHA) so installs can be verified and pinned reproducibly
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
There is no documented, stable, machine-readable way to read, from an installed plugin, which version / git ref / commit SHA it was resolved from. claude plugin list --json reports enabled/name/marketplace/scope but not version; there is no CLAUDE_PLUGIN_VERSION/_REF/_SHA env var (only CLAUDE_PLUGIN_ROOT); no documented lockfile; and the only carrier of the version is the internal cache path ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/, which external tooling shouldn't parse.
Consequences:
- Can't verify provenance. A tool that must prove "the plugin actually running is the release this repo trusts" (CI gates, governance/merge tooling) has no runtime signal to check. The only workaround is to keep a parallel git checkout of the plugin and read git tag --points-at HEAD — forcing a redundant clone + cache maintenance surface, and that checkout's single HEAD collides with using it as a dev workspace.
- Can't audit reproducibility. You can't record or diff "which plugin version this repo/machine is pinned to" from the install itself.
- Compounded by version-keyed caching. claude plugin update skips when the version string is unchanged (no --force), so content can silently diverge from the version number — and there's no provenance field to detect the drift.
This is the same gap #15754 raises from the DX side, #43763/#52218 from the stale-lockfile side, and #33302/#33653 from the pinning side — facets of one missing capability: verifiable, pinnable plugin provenance.
Proposed Solution
Expose an installed plugin's resolved provenance in a documented, stable, machine-readable form — primarily:
- Add version (and, for git sources, ref + sha) to each entry of claude plugin list --json; and/or
- a documented lockfile / installed_plugins.json (project- and/or user-scope) recording each plugin's resolved version + source ref/sha + marketplace, kept in sync on install/update (the mechanism #43763/#52218 expect); and/or
- runtime env vars alongside CLAUDE_PLUGIN_ROOT: CLAUDE_PLUGIN_VERSION, CLAUDE_PLUGIN_REF, CLAUDE_PLUGIN_SHA.
Ideal UX — a governance/CI tool asserts provenance with no git clone and no cache-path parsing:
claude plugin list --json | jq -e '.[] | select(.name=="my-plugin") | .sha == "'"$PINNED_SHA"'"'
(Complementary, and enabled by the same data — noted for context, not part of the core ask: consumer-side claude plugin install <name>@<version> (#33302/#33653), and claude plugin update --force / SHA-addressed caching (#45542/#29512).)
Alternative Solutions
- Parallel git checkout + git tag --points-at HEAD — today's workaround; works but forces a second maintenance surface, and the checkout's HEAD conflicts with development use.
- Parsing the cache directory path — undocumented/internal, breaks on layout changes, carries no source ref/SHA.
- Cache plugin.json version — carries version but not the source ref/SHA, and can't catch version-unchanged-but-content-changed.
- Publisher-side ref/sha in marketplace.json — makes the fetch declarative but gives no post-install readback.
Priority
Medium - Would be very helpful
Feature Category
Developer tools/SDK
Use Case Example
A team ships an engineering-governance plugin (merge gates, CI checks). Each consuming repo pins the plugin version it trusts, and CI must prove the running plugin is that version before enforcing. 1) repo pins my-plugin@v1.2.3; 2) CI reads the installed provenance (plugin list --json / lockfile / env); 3) the gate passes iff installed sha/version == the pin — no separate clone, no cache-path parsing. Today step 2 requires a redundant git checkout of the plugin, which also can't double as a dev workspace (its HEAD must sit exactly at the pinned tag).
Additional Context
- Related open issues this unifies around verification: #15754 (provenance unknown), #33302 (install @version), #33653 (SHA pin), #43763 & #52218 (installed_plugins.json lockfile staleness), #45542 & #29512 (cache not refreshed on unchanged version).
- Current readback gap: claude plugin list --json = {enabled,name,marketplace,scope} (no version); no CLAUDE_PLUGIN_VERSION/_REF/_SHA; cache path is the sole version carrier.
- Environment: claude --version = 1.21459.3.