MCPcopy
hub / github.com/cursor/community-plugins / parseGitHubPlugin

Function parseGitHubPlugin

apps/cursor/src/lib/github-plugin/parse.ts:287–623  ·  view source on GitHub ↗
(
  url: string,
  options: {
    repoMeta?: GitHubRepoMeta | null;
    /**
     * Caller-controlled rate-limit retry budget for the underlying GitHub API
     * calls. Server actions should pass a small value (e.g. 3000) to stay
     * under their function timeout; bulk scripts can pass 30000 (the default).
     */
    maxWaitMs?: number;
  } = {},
)

Source from the content-addressed store, hash-verified

285}
286
287export async function parseGitHubPlugin(
288 url: string,
289 options: {
290 repoMeta?: GitHubRepoMeta | null;
291 /**
292 * Caller-controlled rate-limit retry budget for the underlying GitHub API
293 * calls. Server actions should pass a small value (e.g. 3000) to stay
294 * under their function timeout; bulk scripts can pass 30000 (the default).
295 */
296 maxWaitMs?: number;
297 } = {},
298): Promise<ParsedPlugin> {
299 const parsed = parseGitHubUrl(url);
300 if (!parsed) {
301 throw new GitHubParseError(
302 "Invalid GitHub URL. Expected format: https://github.com/owner/repo",
303 "invalid_url",
304 );
305 }
306
307 const { owner, repo } = parsed;
308 const fetchOpts: FetchOptions = { maxWaitMs: options.maxWaitMs };
309
310 const tree = await fetchGitHubTree(owner, repo, fetchOpts);
311 if (tree.length === 0) {
312 throw new GitHubParseError(
313 "Could not read repository. Make sure the repo exists, is public, and the URL is correct.",
314 "repo_unreadable",
315 );
316 }
317
318 const rootManifestPaths = [
319 ".plugin/plugin.json",
320 ".cursor-plugin/plugin.json",
321 ".claude-plugin/plugin.json",
322 ".cursor-plugin/marketplace.json",
323 ];
324 let manifest: Record<string, unknown> = {};
325 for (const mp of rootManifestPaths) {
326 const content = await fetchGitHubFile(owner, repo, mp);
327 if (content) {
328 try {
329 manifest = JSON.parse(content);
330 } catch {
331 // ignore invalid JSON
332 }
333 break;
334 }
335 }
336
337 // Discover sub-plugin dirs. Two patterns are supported:
338 // 1. Marketplace manifest lists `plugins[].source` (relative dirs).
339 // 2. Any `<dir>/.cursor-plugin/plugin.json` in the tree.
340 const subPluginDirs = new Set<string>();
341
342 if (Array.isArray(manifest.plugins)) {
343 for (const entry of manifest.plugins as Array<unknown>) {
344 if (entry && typeof entry === "object") {

Callers 2

mainFunction · 0.90

Calls 6

slugifyFunction · 0.90
parseGitHubUrlFunction · 0.85
fetchGitHubTreeFunction · 0.85
fetchGitHubFileFunction · 0.85
parseFrontmatterFunction · 0.85
fetchGitHubRepoMetaFunction · 0.85

Tested by

no test coverage detected