Skip to content

Commit ae7169d

Browse files
committed
chore(sync): cascade fleet template@cd61934
Auto-applied by socket-wheelhouse sync-scaffolding into vscode-socket-security. 3 file(s) touched: - .config/socket-registry-pins.json - scripts/install-claude-plugins.mts - scripts/test/install-claude-plugins.test.mts
1 parent 9c18bce commit ae7169d

3 files changed

Lines changed: 342 additions & 15 deletions

File tree

.config/socket-registry-pins.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/install-claude-plugins.mts

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ export interface MarketplaceManifest {
8888
* Parse the plugin's `installPath` to extract the SHA prefix it was
8989
* pinned to (12 chars). Returns `null` for directory installs,
9090
* version-tagged installs, or any path shape we don't recognize as
91-
* SHA-pinned. Used to detect drift between manifest pin and on-disk
92-
* install.
91+
* SHA-pinned. Claude Code uses this dir-name shape for ref-less pins;
92+
* version-tagged pins use a dir name like `1.0.1` instead — see
93+
* `lookupInstalledSha` for the authoritative source.
9394
*/
9495
export function extractInstalledSha(
9596
installPath: string | undefined,
@@ -100,6 +101,38 @@ export function extractInstalledSha(
100101
return m ? m[1] ?? null : null
101102
}
102103

104+
/**
105+
* Look up the installed `gitCommitSha` for a plugin from Claude Code's
106+
* own state file `~/.claude/plugins/installed_plugins.json`. This is
107+
* the authoritative record of which commit a plugin was installed
108+
* from, regardless of whether the cache dir is SHA-prefixed
109+
* (`9cb4fe40-deadbeef/`) or version-tagged (`1.0.1/`).
110+
*
111+
* Returns the full 40-char SHA, or `null` if the file/entry is missing
112+
* or the `gitCommitSha` field is absent (some plugin sources don't
113+
* carry it — directory installs, for example).
114+
*/
115+
export function lookupInstalledSha(
116+
installedPluginsJson: unknown,
117+
installId: string,
118+
): string | null {
119+
if (!installedPluginsJson || typeof installedPluginsJson !== 'object') {
120+
return null
121+
}
122+
const plugins = (installedPluginsJson as { plugins?: unknown }).plugins
123+
if (!plugins || typeof plugins !== 'object') return null
124+
const entries = (plugins as Record<string, unknown>)[installId]
125+
if (!Array.isArray(entries)) return null
126+
for (const entry of entries) {
127+
if (!entry || typeof entry !== 'object') continue
128+
const sha = (entry as { gitCommitSha?: unknown }).gitCommitSha
129+
if (typeof sha === 'string' && /^[0-9a-f]{40}$/.test(sha)) {
130+
return sha
131+
}
132+
}
133+
return null
134+
}
135+
103136
/**
104137
* Find an existing install of `pluginName` that came from a marketplace
105138
* *other than* ours. Plugin ids have the shape `<name>@<marketplace>`.
@@ -196,9 +229,13 @@ function listPlugins(): PluginListEntry[] {
196229
function ensureMarketplace(): MarketplaceListEntry {
197230
const existing = listMarketplaces().find(m => m.name === MARKETPLACE_NAME)
198231
if (existing) {
199-
logger.log(
200-
`Marketplace "${MARKETPLACE_NAME}" already added (source: ${existing.source}).`,
201-
)
232+
// Marketplace already added — but the local snapshot may be stale
233+
// relative to upstream. Pull a fresh copy so we read today's pinned
234+
// set, not whatever was committed when this machine first added the
235+
// marketplace. Cheap (Claude Code downloads a tarball snapshot, no
236+
// git clone) and idempotent.
237+
logger.log(`Marketplace "${MARKETPLACE_NAME}" already added; refreshing snapshot…`)
238+
runClaudeCli(['plugin', 'marketplace', 'update', MARKETPLACE_NAME])
202239
return existing
203240
}
204241
logger.log(`Adding marketplace "${MARKETPLACE_NAME}" from ${MARKETPLACE_URL}…`)
@@ -220,6 +257,29 @@ function ensureMarketplace(): MarketplaceListEntry {
220257
return added
221258
}
222259

260+
/**
261+
* Load `~/.claude/plugins/installed_plugins.json` — Claude Code's
262+
* authoritative state file for which commit each installed plugin came
263+
* from. Returns `null` if the file is absent or unparseable; the
264+
* reconciler falls back to path-prefix parsing in that case.
265+
*/
266+
function loadInstalledPluginsState(): unknown {
267+
const home = process.env['HOME'] ?? process.env['USERPROFILE']
268+
if (!home || !path.isAbsolute(home)) return null
269+
const stateFile = path.join(
270+
home,
271+
'.claude',
272+
'plugins',
273+
'installed_plugins.json',
274+
)
275+
if (!existsSync(stateFile)) return null
276+
try {
277+
return JSON.parse(readFileSync(stateFile, 'utf8'))
278+
} catch {
279+
return null
280+
}
281+
}
282+
223283
function loadMarketplaceManifest(
224284
marketplace: MarketplaceListEntry,
225285
): MarketplaceManifest {
@@ -255,14 +315,30 @@ function installPlugin(installId: string, pinDescription: string): void {
255315
runClaudeCli(['plugin', 'install', installId, '--scope', 'user'])
256316
}
257317

318+
/**
319+
* Resolve the installed SHA for a plugin. Prefer the authoritative
320+
* `gitCommitSha` field from `~/.claude/plugins/installed_plugins.json`;
321+
* fall back to parsing the cache dir name for ref-less SHA-prefix
322+
* installs. Returns the full 40-char SHA (or 12-char prefix from the
323+
* fallback path), or `null` if neither source resolves.
324+
*/
325+
function resolveInstalledSha(
326+
ours: PluginListEntry,
327+
state: unknown,
328+
): string | null {
329+
const fromState = lookupInstalledSha(state, ours.id)
330+
if (fromState) return fromState
331+
return extractInstalledSha(ours.installPath)
332+
}
333+
258334
/**
259335
* Reconcile a single plugin to the wheelhouse pin. Handles four cases:
260336
* foreign install (uninstall + install), missing (install), stale SHA
261337
* (uninstall + reinstall), and correct (no-op).
262338
*/
263339
function reconcilePlugin(plugin: MarketplacePlugin): void {
264340
const ourInstallId = `${plugin.name}@${MARKETPLACE_NAME}`
265-
const expectedShaPrefix = plugin.source.sha?.slice(0, 12) ?? null
341+
const expectedSha = plugin.source.sha ?? null
266342
const pinDescription =
267343
plugin.source.sha ?? plugin.source.ref ?? '<no ref>'
268344

@@ -281,23 +357,30 @@ function reconcilePlugin(plugin: MarketplacePlugin): void {
281357
plugins = listPlugins()
282358
}
283359

284-
// (2) Our install present? Check SHA.
360+
// (2) Our install present? Check SHA against installed_plugins.json's
361+
// gitCommitSha field (authoritative) with cache-dir-name parsing as
362+
// fallback. Both SHA forms can compare: the authoritative one is full
363+
// 40-char, the fallback is 12-char prefix, so compare on a shared
364+
// 12-char prefix.
285365
const ours = plugins.find(p => p.id === ourInstallId)
286366
if (ours) {
287-
const installedShaPrefix = extractInstalledSha(ours.installPath)
288-
if (!expectedShaPrefix) {
367+
if (!expectedSha) {
289368
// Manifest pin has no SHA — we can't drift-compare. Trust the
290369
// existing install.
291370
logger.log(`Plugin ${ourInstallId} already installed (manifest has no SHA to compare).`)
292371
return
293372
}
294-
if (installedShaPrefix === expectedShaPrefix) {
295-
logger.log(`Plugin ${ourInstallId} already installed at pinned SHA ${expectedShaPrefix}.`)
373+
const state = loadInstalledPluginsState()
374+
const installedSha = resolveInstalledSha(ours, state)
375+
const expectedPrefix = expectedSha.slice(0, 12)
376+
const installedPrefix = installedSha?.slice(0, 12) ?? null
377+
if (installedPrefix === expectedPrefix) {
378+
logger.log(`Plugin ${ourInstallId} already installed at pinned SHA ${expectedPrefix}.`)
296379
return
297380
}
298381
// Drift: our install is at a different SHA. Reinstall.
299382
logger.log(
300-
`Plugin ${ourInstallId} drift: installed at ${installedShaPrefix ?? '<unknown>'}, manifest pins ${expectedShaPrefix}. Reinstalling.`,
383+
`Plugin ${ourInstallId} drift: installed at ${installedPrefix ?? '<unknown>'}, manifest pins ${expectedPrefix}. Reinstalling.`,
301384
)
302385
uninstallPlugin(ourInstallId)
303386
installPlugin(ourInstallId, pinDescription)

0 commit comments

Comments
 (0)