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

Function cacheAndRegisterPlugin

src/utils/plugins/pluginInstallationHelpers.ts:128–226  ·  view source on GitHub ↗
(
  pluginId: string,
  entry: PluginMarketplaceEntry,
  scope: PluginScope = 'user',
  projectPath?: string,
  localSourcePath?: string,
)

Source from the content-addressed store, hash-verified

126 * @returns The installation path
127 */
128export async function cacheAndRegisterPlugin(
129 pluginId: string,
130 entry: PluginMarketplaceEntry,
131 scope: PluginScope = 'user',
132 projectPath?: string,
133 localSourcePath?: string,
134): Promise<string> {
135 // For local plugins, we need the resolved absolute path
136 // Cast to PluginSource since cachePlugin handles any string path at runtime
137 const source: PluginSource =
138 typeof entry.source === 'string' && localSourcePath
139 ? (localSourcePath as PluginSource)
140 : entry.source
141
142 const cacheResult = await cachePlugin(source, {
143 manifest: entry as PluginMarketplaceEntry,
144 })
145
146 // For local plugins, use the original source path for Git SHA calculation
147 // because the cached temp directory doesn't have .git (it's copied from a
148 // subdirectory of the marketplace git repo). For external plugins, use the
149 // cached path. For git-subdir sources, cachePlugin already captured the SHA
150 // before discarding the ephemeral clone (the extracted subdir has no .git).
151 const pathForGitSha = localSourcePath || cacheResult.path
152 const gitCommitSha =
153 cacheResult.gitCommitSha ?? (await getGitCommitSha(pathForGitSha))
154
155 const now = getCurrentTimestamp()
156 const version = await calculatePluginVersion(
157 pluginId,
158 entry.source,
159 cacheResult.manifest,
160 pathForGitSha,
161 entry.version,
162 cacheResult.gitCommitSha,
163 )
164
165 // Move the cached plugin to the versioned path: cache/marketplace/plugin/version/
166 const versionedPath = getVersionedCachePath(pluginId, version)
167 let finalPath = cacheResult.path
168
169 // Only move if the paths are different and plugin was cached to a different location
170 if (cacheResult.path !== versionedPath) {
171 // Create the versioned directory structure
172 await getFsImplementation().mkdir(dirname(versionedPath))
173
174 // Remove existing versioned path if present (force: no-op if missing)
175 await rm(versionedPath, { recursive: true, force: true })
176
177 // Check if versionedPath is a subdirectory of cacheResult.path
178 // This happens when marketplace name equals plugin name (e.g., "exa-mcp-server@exa-mcp-server")
179 // In this case, we can't directly rename because we'd be moving a directory into itself
180 const normalizedCachePath = cacheResult.path.endsWith(sep)
181 ? cacheResult.path
182 : cacheResult.path + sep
183 const isSubdirectory = versionedPath.startsWith(normalizedCachePath)
184
185 if (isSubdirectory) {

Callers 3

installSelectedPluginsFunction · 0.85
installResolvedPluginFunction · 0.85

Calls 13

cachePluginFunction · 0.85
getCurrentTimestampFunction · 0.85
calculatePluginVersionFunction · 0.85
getVersionedCachePathFunction · 0.85
getFsImplementationFunction · 0.85
rmFunction · 0.85
renameFunction · 0.85
isPluginZipCacheEnabledFunction · 0.85
getVersionedZipCachePathFunction · 0.85
addInstalledPluginFunction · 0.85
getGitCommitShaFunction · 0.70

Tested by

no test coverage detected