MCPcopy Create free account
hub / github.com/WJX20/claude-code / installPluginsForHeadless

Function installPluginsForHeadless

src/utils/plugins/headlessPluginInstall.ts:43–174  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

41 * @returns true if any plugins were installed (caller should refresh MCP)
42 */
43export async function installPluginsForHeadless(): Promise<boolean> {
44 const zipCacheMode = isPluginZipCacheEnabled()
45 logForDebugging(
46 `installPluginsForHeadless: starting${zipCacheMode ? ' (zip cache mode)' : ''}`,
47 )
48
49 // Register seed marketplaces (CLAUDE_CODE_PLUGIN_SEED_DIR) before diffing.
50 // Idempotent; no-op if seed not configured. Without this, findMissingMarketplaces
51 // would see seed entries as missing → clone → defeats seed's purpose.
52 //
53 // If registration changed state, clear caches so the early plugin-load pass
54 // (which runs during CLI startup before this function) doesn't keep stale
55 // "marketplace not found" results. Without this clear, a first-boot headless
56 // run with a seed-cached plugin would show 0 plugin commands/agents/skills
57 // in the init message even though the seed has everything.
58 const seedChanged = await registerSeedMarketplaces()
59 if (seedChanged) {
60 clearMarketplacesCache()
61 clearPluginCache('headlessPluginInstall: seed marketplaces registered')
62 }
63
64 // Ensure zip cache directory structure exists
65 if (zipCacheMode) {
66 await getFsImplementation().mkdir(getZipCacheMarketplacesDir())
67 await getFsImplementation().mkdir(getZipCachePluginsDir())
68 }
69
70 // Declared now includes an implicit claude-plugins-official entry when any
71 // enabled plugin references it (see getDeclaredMarketplaces). This routes
72 // the official marketplace through the same reconciler path as any other —
73 // which composes correctly with CLAUDE_CODE_PLUGIN_SEED_DIR: seed registers
74 // it in known_marketplaces.json, reconciler diff sees it as upToDate, no clone.
75 const declaredCount = Object.keys(getDeclaredMarketplaces()).length
76
77 const metrics = {
78 marketplaces_installed: 0,
79 delisted_count: 0,
80 }
81
82 // Initialize from seedChanged so the caller (print.ts) calls
83 // refreshPluginState() → clearCommandsCache/clearAgentDefinitionsCache
84 // when seed registration added marketplaces. Without this, the caller
85 // only refreshes when an actual plugin install happened.
86 let pluginsChanged = seedChanged
87
88 try {
89 if (declaredCount === 0) {
90 logForDebugging('installPluginsForHeadless: no marketplaces declared')
91 } else {
92 // Reconcile declared marketplaces (settings intent + implicit official)
93 // with materialized state. Zip cache: skip unsupported source types.
94 const reconcileResult = await withDiagnosticsTiming(
95 'headless_marketplace_reconcile',
96 () =>
97 reconcileMarketplaces({
98 skip: zipCacheMode
99 ? (_name, source) =>
100 !isMarketplaceSourceSupportedByZipCache(source)

Callers 1

Calls 15

isPluginZipCacheEnabledFunction · 0.85
logForDebuggingFunction · 0.85
registerSeedMarketplacesFunction · 0.85
clearMarketplacesCacheFunction · 0.85
clearPluginCacheFunction · 0.85
getFsImplementationFunction · 0.85
getZipCachePluginsDirFunction · 0.85
getDeclaredMarketplacesFunction · 0.85
withDiagnosticsTimingFunction · 0.85
reconcileMarketplacesFunction · 0.85

Tested by

no test coverage detected