--- export interface PluginIncludedItem { kind: string; path: string; title?: string; detailUrl?: string | null; } export interface IncludedItemsProps { items: PluginIncludedItem[]; pluginPath: string; githubBase: string; } const { items, pluginPath, githubBase } = Astro.props as IncludedItemsProps; const KIND_LABELS: Record = { agent: "Agents", skill: "Skills", instruction: "Instructions", hook: "Hooks", prompt: "Commands", extension: "Extensions", }; const KIND_ORDER = ["agent", "skill", "instruction", "hook", "prompt", "extension"]; function githubHref(itemPath: string): string { const normalized = itemPath.replace(/^\.\/+/, "").replace(/\/+$/, ""); // Repo-root-relative paths (e.g. extensions/foo, plugins/foo) are used as-is; // plugin-relative paths are resolved against the plugin folder. const isRepoRelative = /^[a-z0-9._-]+\//i.test(normalized) && (normalized.startsWith("extensions/") || normalized.startsWith("plugins/") || normalized.startsWith("agents/") || normalized.startsWith("skills/") || normalized.startsWith("instructions/") || normalized.startsWith("hooks/")); const repoPath = isRepoRelative ? normalized : `${pluginPath}/${normalized}`; // GitHub uses /blob/ for files and /tree/ for directories; /tree// // returns 404. Detect files by a trailing extension on the last path segment. const lastSegment = repoPath.split("/").pop() ?? ""; const isFile = /\.[a-z0-9]+$/i.test(lastSegment); const base = isFile ? githubBase.replace("/tree/", "/blob/") : githubBase; return `${base}/${repoPath}`; } const grouped = KIND_ORDER.map((kind) => ({ kind, label: KIND_LABELS[kind] ?? kind, entries: (items ?? []).filter((item) => item.kind === kind), })).filter((group) => group.entries.length > 0); --- { grouped.length > 0 && (

Included items

This plugin bundles the following resources. Installing the plugin brings them all in together.

{grouped.map((group) => (

{group.label}

))}
) }