@nospt/plugin-dev-ai-hub-common 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -185,10 +185,14 @@ interface ResourceListResponse {
185
185
  type BodyShape = 'markdown' | 'json';
186
186
  declare function getBodyShape(type: ResourceType): BodyShape;
187
187
  /**
188
- * Whether the detail panel shows a "Content" section for this type. `plugin`
189
- * and `marketplace` resources are containers — their body is the raw manifest
190
- * JSON and their real value is the relationship graph, not the file — so the
191
- * Content block is suppressed for them and kept for every other type.
188
+ * Whether the detail panel shows a "Content" section for this type.
189
+ *
190
+ * Suppressed for exactly the JSON-bodied types (`getBodyShape` === 'json'):
191
+ * `plugin` and `marketplace` are containers whose value is the relationship
192
+ * graph rather than the manifest, and an `mcp-config`'s body is a settings
193
+ * fragment to merge — in all three the raw JSON buries the panel and is
194
+ * better reached through Copy, Download or the install dialog, which is
195
+ * where it is actionable. The markdown types keep their rendered body.
192
196
  */
193
197
  declare function showsContentSection(type: ResourceType): boolean;
194
198
  /**
@@ -271,6 +275,17 @@ interface ResourceDeepLink {
271
275
  /** Host application name; render as "Add in <label>" / "Install in <label>". */
272
276
  label: string;
273
277
  href: string;
278
+ /**
279
+ * The framework token this launcher installs into, for the `tool` column on
280
+ * the `install` event it records (ADR-0011). Not 1:1 with `label`:
281
+ * `github-copilot` offers both "VS Code" and "VS Code Insiders".
282
+ *
283
+ * Optional only to keep this published interface additive — every link the
284
+ * generators below build sets it, which `resources.test.ts` pins. A caller
285
+ * reading a generated link can rely on it; one constructing its own shape
286
+ * is not forced to supply it.
287
+ */
288
+ framework?: string;
274
289
  }
275
290
  interface MarketplaceAddCommand {
276
291
  framework: string;
@@ -119,7 +119,7 @@ function getBodyShape(type) {
119
119
  return type === "mcp-config" || type === "plugin" || type === "marketplace" ? "json" : "markdown";
120
120
  }
121
121
  function showsContentSection(type) {
122
- return type !== "plugin" && type !== "marketplace";
122
+ return getBodyShape(type) !== "json";
123
123
  }
124
124
  function hasDownloadableArtifact(_type) {
125
125
  return true;
@@ -254,7 +254,8 @@ function getMarketplaceAddCommands(frameworks, repoSlug) {
254
254
  deepLinks: [
255
255
  {
256
256
  label: "Claude",
257
- href: `claude-cli://open?q=${encodeURIComponent(command)}`
257
+ href: `claude-cli://open?q=${encodeURIComponent(command)}`,
258
+ framework
258
259
  }
259
260
  ]
260
261
  });
@@ -266,11 +267,13 @@ function getMarketplaceAddCommands(frameworks, repoSlug) {
266
267
  deepLinks: [
267
268
  {
268
269
  label: "VS Code",
269
- href: `vscode://chat-plugin/add-marketplace?ref=${ref}`
270
+ href: `vscode://chat-plugin/add-marketplace?ref=${ref}`,
271
+ framework
270
272
  },
271
273
  {
272
274
  label: "VS Code Insiders",
273
- href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`
275
+ href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`,
276
+ framework
274
277
  }
275
278
  ]
276
279
  });
@@ -302,7 +305,8 @@ function getRawGithubFileUrl(sourceLocation) {
302
305
  function claudePromptLink(prompt) {
303
306
  return {
304
307
  label: "Claude",
305
- href: `claude-cli://open?q=${encodeURIComponent(prompt)}`
308
+ href: `claude-cli://open?q=${encodeURIComponent(prompt)}`,
309
+ framework: "claude-code"
306
310
  };
307
311
  }
308
312
  function cursorPromptLink(prompt) {
@@ -310,7 +314,8 @@ function cursorPromptLink(prompt) {
310
314
  label: "Cursor",
311
315
  href: `cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(
312
316
  prompt
313
- )}`
317
+ )}`,
318
+ framework: "cursor"
314
319
  };
315
320
  }
316
321
  const AGENT_LINK_CAPABLE_FRAMEWORKS = [
@@ -387,11 +392,13 @@ function getAgentInstallLinks(sourceLocation, name, frameworks = []) {
387
392
  } else if (framework === "github-copilot") {
388
393
  links.push({
389
394
  label: "VS Code",
390
- href: `vscode:chat-agent/install?url=${encodedRawUrl}`
395
+ href: `vscode:chat-agent/install?url=${encodedRawUrl}`,
396
+ framework
391
397
  });
392
398
  links.push({
393
399
  label: "VS Code Insiders",
394
- href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`
400
+ href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`,
401
+ framework
395
402
  });
396
403
  }
397
404
  }
@@ -439,15 +446,24 @@ function getMcpInstallLinks(frameworks, resourceName, bodyContent) {
439
446
  const prompt = encodeURIComponent(
440
447
  `Install this MCP config by running: claude mcp add-json ${serverName} '${configJson}'`
441
448
  );
442
- links.push({ label: "Claude", href: `claude-cli://open?q=${prompt}` });
449
+ links.push({
450
+ label: "Claude",
451
+ href: `claude-cli://open?q=${prompt}`,
452
+ framework
453
+ });
443
454
  } else if (framework === "github-copilot") {
444
455
  const vsConfig = encodeURIComponent(
445
456
  JSON.stringify({ name: serverName, ...config })
446
457
  );
447
- links.push({ label: "VS Code", href: `vscode:mcp/install?${vsConfig}` });
458
+ links.push({
459
+ label: "VS Code",
460
+ href: `vscode:mcp/install?${vsConfig}`,
461
+ framework
462
+ });
448
463
  links.push({
449
464
  label: "VS Code Insiders",
450
- href: `vscode-insiders:mcp/install?${vsConfig}`
465
+ href: `vscode-insiders:mcp/install?${vsConfig}`,
466
+ framework
451
467
  });
452
468
  } else if (framework === "cursor") {
453
469
  try {
@@ -455,7 +471,8 @@ function getMcpInstallLinks(frameworks, resourceName, bodyContent) {
455
471
  label: "Cursor",
456
472
  href: `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(
457
473
  serverName
458
- )}&config=${btoa(configJson)}`
474
+ )}&config=${btoa(configJson)}`,
475
+ framework
459
476
  });
460
477
  } catch {
461
478
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resources.cjs.js","sources":["../src/resources.ts"],"sourcesContent":["/**\n * The v2 read contract: AiResource vocabulary, the flat ResourceSummary\n * returned by the backend, and the shared framework resolver (ADR-0003).\n *\n * The legacy asset-model types in `types.ts` coexist with this module until\n * the legacy silo is deleted (slice 8, issue #34).\n */\n\n/** The six canonical `spec.type` values of an AiResource (ADR-0003, ADR-0010). */\nexport const RESOURCE_TYPES = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n 'plugin',\n 'marketplace',\n] as const;\n\nexport type ResourceType = (typeof RESOURCE_TYPES)[number];\n\nexport function isResourceType(value: unknown): value is ResourceType {\n return (\n typeof value === 'string' &&\n (RESOURCE_TYPES as readonly string[]).includes(value)\n );\n}\n\n/**\n * The AiResource types a `plugin` may contain — restricted to the component\n * kinds a real Claude Code / GitHub Copilot plugin manifest allows: `skills`,\n * `agents`, `hooks` and `mcpServers` (schemastore `claude-code-plugin` /\n * `claude-code-marketplace.json`). A plugin's other manifest components\n * (`commands`, `lspServers`, `outputStyles`, `themes`, `channels`, `monitors`)\n * have no AiResource type, so they are never members here; other plugins are\n * `dependencies`, not containment.\n */\nexport const PLUGIN_MEMBER_TYPES: readonly ResourceType[] = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n];\n\n/**\n * The AiResource types a `marketplace` may contain: `plugin` only. A real\n * `.claude-plugin/marketplace.json` `plugins` array lists plugins and nothing\n * else (required by the schema Claude Code and Copilot CLI both read).\n */\nexport const MARKETPLACE_MEMBER_TYPES: readonly ResourceType[] = ['plugin'];\n\n/**\n * Whether a container of `parent` type renders a child of `child` type as a\n * member — restricted to what a real Claude/Copilot plugin or marketplace\n * manifest permits (ADR-0010/0015). A `marketplace` contains `plugin` only; a\n * `plugin` contains the component types DevAI Hub models. Any other pairing is\n * silently not rendered. This is a rendering convention, not catalog\n * validation (upstream `allowedKinds` is the broad `[\"AiResource\"]`).\n */\nexport function isRenderableContainment(\n parent: ResourceType,\n child: ResourceType,\n): boolean {\n if (parent === 'marketplace') return MARKETPLACE_MEMBER_TYPES.includes(child);\n if (parent === 'plugin') return PLUGIN_MEMBER_TYPES.includes(child);\n return false;\n}\n\n/** Annotation prefix owned by DevAI Hub. */\nexport const DEVAIHUB_ANNOTATION_PREFIX = 'devaihub.io';\n\n/** Comma-separated compatible-framework tokens (non-skill read path, ADR-0003). */\nexport const ANNOTATION_COMPATIBLE_FRAMEWORKS = `${DEVAIHUB_ANNOTATION_PREFIX}/compatible-frameworks`;\n\n/** Optional free-form help text shown on the detail panel. */\nexport const ANNOTATION_HELP = `${DEVAIHUB_ANNOTATION_PREFIX}/help`;\n\n/** Known framework tokens; unknown tokens pass through as-is (ADR-0003). */\nexport const KNOWN_FRAMEWORKS = [\n 'github-copilot',\n 'claude-code',\n 'cursor',\n 'google-gemini',\n 'opencode',\n 'all',\n] as const;\n\nexport type FrameworkToken = (typeof KNOWN_FRAMEWORKS)[number];\n\nconst FRAMEWORK_ALIASES: Record<string, FrameworkToken> = {\n copilot: 'github-copilot',\n claude: 'claude-code',\n gemini: 'google-gemini',\n};\n\n/**\n * Normalise a framework token: trim, lowercase, resolve aliases\n * (`claude` → `claude-code`). Unknown tokens pass through literally.\n */\nexport function normalizeFramework(token: string): string {\n const t = token.trim().toLowerCase();\n return FRAMEWORK_ALIASES[t] ?? t;\n}\n\n/**\n * Narrow a resource's frameworks to a deployment-level allow-list\n * (`devAiHub.frameworks.allowed`). Omitting the config key (`allowed` is\n * `undefined`) permits every framework; an explicit empty list permits none,\n * so a deployment can suppress framework exposure entirely. Both sides are\n * normalised (aliases resolved) before comparison.\n *\n * The `all` wildcard is expanded to the allow-list itself before filtering, so\n * a resource that declares `all` degrades to \"every permitted host\" rather\n * than disappearing entirely.\n */\nexport function filterAllowedFrameworks(\n frameworks: string[],\n allowed: string[] | undefined,\n): string[] {\n if (allowed === undefined) {\n return frameworks;\n }\n const permitted = new Set(\n allowed.map(normalizeFramework).filter(f => f && f !== 'all'),\n );\n const expanded = frameworks.flatMap(f =>\n normalizeFramework(f) === 'all' ? [...permitted] : [normalizeFramework(f)],\n );\n return Array.from(new Set(expanded)).filter(f => permitted.has(f));\n}\n\n/**\n * The minimal structural shape of an AiResource entity that the read helpers\n * need. A real `@backstage/catalog-model` `Entity` satisfies it, but keeping\n * the shape local means `-common` (and therefore the frontend) never depends\n * on catalog packages (architecture.md).\n */\nexport interface AiResourceEntityLike {\n metadata: {\n annotations?: Record<string, string>;\n };\n spec?: {\n type?: unknown;\n /** Native compatible-frameworks field, honoured regardless of `spec.type`. */\n agents?: unknown;\n [key: string]: unknown;\n };\n}\n\n/**\n * Resolve the compatible frameworks of an AiResource (ADR-0003):\n * 1. a non-empty `spec.agents` → the native field, for any `spec.type`;\n * 2. otherwise → the `devaihub.io/compatible-frameworks` annotation\n * (also the fallback when `spec.agents` is empty/absent);\n * 3. otherwise → `[]`.\n * Tokens are normalised; unknown tokens pass through. `spec.agents` is\n * preferred over the annotation because it lives on the entity itself\n * rather than in an annotation namespace we may retire.\n */\nexport function getFrameworks(entity: AiResourceEntityLike): string[] {\n if (Array.isArray(entity.spec?.agents)) {\n const agents = entity.spec.agents\n .filter((a): a is string => typeof a === 'string')\n .map(normalizeFramework)\n .filter(Boolean);\n if (agents.length > 0) {\n return dedupe(agents);\n }\n }\n\n const raw = entity.metadata.annotations?.[ANNOTATION_COMPATIBLE_FRAMEWORKS];\n if (!raw) {\n return [];\n }\n return dedupe(raw.split(',').map(normalizeFramework).filter(Boolean));\n}\n\nfunction dedupe(tokens: string[]): string[] {\n return Array.from(new Set(tokens));\n}\n\n/**\n * Icon identifiers for the type→card registry. `-common` is imported by the\n * backend, so it stays React-free: the frontend maps these identifiers to\n * actual icon components.\n */\nexport type ResourceTypeIcon =\n | 'tools'\n | 'robot'\n | 'flash'\n | 'plug'\n | 'puzzle'\n | 'store';\n\nexport interface ResourceTypeInfo {\n type: ResourceType;\n /** Singular display label, e.g. \"Skill\" (card type caption). */\n label: string;\n /** Plural display label, e.g. \"Skills\" (stat tiles, filters). */\n pluralLabel: string;\n /** Icon identifier resolved to a component by the frontend. */\n icon: ResourceTypeIcon;\n /**\n * Colour role name; the frontend resolves it to the plugin-owned\n * `--devaihub-type-<role>` custom properties (ADR-0008, NOS palette).\n */\n colorRole: ResourceType;\n}\n\n/**\n * The typed type→card registry (ADR-0003/ADR-0008): producer vocabulary and\n * consumer rendering share this single source, so they cannot drift. Adding\n * a type is one entry here plus one CSS token pair in the frontend.\n */\nexport const RESOURCE_TYPE_REGISTRY: Record<ResourceType, ResourceTypeInfo> = {\n skill: {\n type: 'skill',\n label: 'Skill',\n pluralLabel: 'Skills',\n icon: 'tools',\n colorRole: 'skill',\n },\n agent: {\n type: 'agent',\n label: 'Agent',\n pluralLabel: 'Agents',\n icon: 'robot',\n colorRole: 'agent',\n },\n hook: {\n type: 'hook',\n label: 'Hook',\n pluralLabel: 'Hooks',\n icon: 'flash',\n colorRole: 'hook',\n },\n 'mcp-config': {\n type: 'mcp-config',\n label: 'MCP Config',\n pluralLabel: 'MCP Configs',\n icon: 'plug',\n colorRole: 'mcp-config',\n },\n plugin: {\n type: 'plugin',\n label: 'Plugin',\n pluralLabel: 'Plugins',\n icon: 'puzzle',\n colorRole: 'plugin',\n },\n marketplace: {\n type: 'marketplace',\n label: 'Marketplace',\n pluralLabel: 'Marketplaces',\n icon: 'store',\n colorRole: 'marketplace',\n },\n};\n\n/**\n * The flat contract the backend returns to the frontend. The frontend never\n * sees a raw Backstage `Entity` — only this (architecture.md).\n */\nexport interface ResourceSummary {\n entityRef: string;\n name: string;\n title?: string;\n description?: string;\n tags: string[];\n type: ResourceType;\n lifecycle: string;\n owner?: string;\n sourceLocation?: string;\n frameworks: string[];\n /**\n * True when an active deployment allow-list (`devAiHub.frameworks.allowed`)\n * left this resource with no frameworks — either it removed the ones the\n * author declared, or the allow-list itself is empty. Lets the UI tell\n * \"author declared nothing\" (`frameworks: []`, show neutral install\n * defaults) apart from \"the deployment suppressed them\" (hide generated\n * install paths and launchers; Copy/Download still work).\n */\n frameworksRestricted?: boolean;\n /**\n * The framework set that install-path and launcher generation must be\n * scoped to, when it differs from `frameworks`. Only set for a resource\n * that declared no frameworks (which otherwise means \"every host\") under an\n * active allow-list: it holds the allow-list itself, so a bare resource\n * cannot produce launchers for tools the deployment disallowed. Kept\n * separate from `frameworks` so this inferred set never renders as a\n * compatibility badge the author never claimed.\n */\n installFrameworks?: string[];\n version?: string;\n kind: string;\n /**\n * Entity refs this container declares as its contents — a `marketplace`'s\n * `spec.plugins`, a `plugin`'s `spec.skills`; empty for leaf types. Filtered\n * to the caller's visible set and to the child types the container renders\n * (ADR-0015). The frontend resolves each ref against the summaries it holds.\n */\n children: string[];\n /**\n * Entity refs of the containers that declare this resource — the in-memory\n * inverse of `children`, built over the same caller-visible catalog read\n * (ADR-0015). A resource can belong to several containers.\n */\n parents: string[];\n /** Count of `children` (their declared, caller-visible members). */\n childCount?: number;\n helpText?: string;\n annotations: Record<string, string>;\n}\n\nexport interface ResourceListResponse {\n items: ResourceSummary[];\n}\n\n/**\n * The shape of a resource's body (CONTEXT.md: bodies are type-shaped, not\n * uniformly markdown). Drives detail-panel rendering: markdown is rendered,\n * json is pretty-printed and syntax-highlighted (ADR-0014). `plugin` and\n * `marketplace` bodies are the real Claude Code `plugin.json` /\n * `marketplace.json` manifest — not a hand-authored pointer doc, superseding\n * ADR-0010's markdown-only body for `marketplace`.\n */\nexport type BodyShape = 'markdown' | 'json';\n\nexport function getBodyShape(type: ResourceType): BodyShape {\n return type === 'mcp-config' || type === 'plugin' || type === 'marketplace'\n ? 'json'\n : 'markdown';\n}\n\n/**\n * Whether the detail panel shows a \"Content\" section for this type. `plugin`\n * and `marketplace` resources are containers — their body is the raw manifest\n * JSON and their real value is the relationship graph, not the file — so the\n * Content block is suppressed for them and kept for every other type.\n */\nexport function showsContentSection(type: ResourceType): boolean {\n return type !== 'plugin' && type !== 'marketplace';\n}\n\n/**\n * Whether downloading the body delivers the artifact itself. True for every\n * type: `plugin`/`marketplace` bodies are now the real manifest JSON, so a\n * download delivers that file itself rather than a non-actionable pointer\n * doc (ADR-0014, superseding ADR-0009's amendment and ADR-0010 on this\n * point).\n */\nexport function hasDownloadableArtifact(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * Whether copying the body to the clipboard is a meaningful install action.\n * True for every type: `marketplace`'s body is now the real manifest JSON,\n * not an instructions doc, so copying it is no longer a misrepresentation\n * (ADR-0014, superseding ADR-0010 on this point).\n */\nexport function hasCopyableBody(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * How a body reaches its install path.\n *\n * `drop-in` — the body becomes the file (or directory) at `path`, which is\n * the resource's own; writing it touches nothing else.\n *\n * `merge` — `path` is a shared settings file the user already owns, and the\n * body is a fragment to merge into it. The distinction is not cosmetic:\n * following a `merge` target as if it were a `drop-in` overwrites the user's\n * existing configuration, so the UI must never present the two identically.\n */\nexport type InstallMode = 'drop-in' | 'merge';\n\nexport interface ResourceInstallTarget {\n /** Workspace-relative path. */\n path: string;\n mode: InstallMode;\n}\n\n/**\n * The concrete framework tokens a body can be installed into — `all` is a\n * wildcard over these, never a destination itself.\n */\nexport const INSTALLABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'google-gemini',\n 'cursor',\n 'opencode',\n] as const;\n\n/**\n * Convention table: (type, framework) → where the body goes and how.\n * A missing entry means the pair has no filesystem convention — `plugin` and\n * `marketplace` bodies are pointers that install through their framework\n * (ADR-0009, ADR-0010), so they carry install commands rather than paths.\n */\nconst INSTALL_PATHS: Record<\n ResourceType,\n Record<string, { mode: InstallMode; path: (name: string) => string }>\n> = {\n // Every host reads a skill as a self-contained directory. Copilot also\n // picks up `.claude/skills`, but each tool's own path is what we recommend;\n // `.agents/skills` is the cross-tool alias Copilot and Gemini both honour,\n // which makes it the right answer for an unrecognised framework.\n skill: {\n 'claude-code': { mode: 'drop-in', path: name => `.claude/skills/${name}/` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/skills/${name}/`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/skills/${name}/`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/skills/${name}/` },\n opencode: { mode: 'drop-in', path: name => `.opencode/skills/${name}/` },\n default: { mode: 'drop-in', path: name => `.agents/skills/${name}/` },\n },\n agent: {\n 'claude-code': {\n mode: 'drop-in',\n path: name => `.claude/agents/${name}.md`,\n },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/agents/${name}.agent.md`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/agents/${name}.md`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/rules/${name}.mdc` },\n opencode: { mode: 'drop-in', path: name => `.opencode/agents/${name}.md` },\n default: { mode: 'drop-in', path: name => `.ai/agents/${name}.md` },\n },\n // All four hosts have a hook system, but only Copilot gives each hook its\n // own file; the rest register hooks inside a shared settings document.\n // OpenCode has no declarative hook config at all — hooks are exclusively\n // JS/TS plugin modules — so it deliberately carries no entry here, the same\n // way it carries no `default`.\n hook: {\n 'claude-code': { mode: 'merge', path: () => `.claude/settings.json` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/hooks/${name}.json`,\n },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/hooks.json` },\n },\n 'mcp-config': {\n 'claude-code': { mode: 'merge', path: () => `.mcp.json` },\n 'github-copilot': { mode: 'merge', path: () => `.vscode/mcp.json` },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/mcp.json` },\n opencode: { mode: 'merge', path: () => `opencode.json` },\n },\n plugin: {},\n marketplace: {},\n};\n\n/**\n * Where a resource's body installs for one framework, and whether that path\n * is the body's own file or a settings file to merge into. Returns\n * `undefined` when the (type, framework) pair has no filesystem convention.\n */\nexport function getResourceInstallTarget(\n type: ResourceType,\n framework: string,\n name: string,\n): ResourceInstallTarget | undefined {\n const conventions = INSTALL_PATHS[type];\n const entry =\n conventions[normalizeFramework(framework)] ?? conventions.default;\n return entry ? { path: entry.path(name), mode: entry.mode } : undefined;\n}\n\n/**\n * The frameworks to show install paths for. An empty list means the resource\n * declared no compatibility and gets the neutral `default` convention; `all`\n * is a wildcard and expands to every installable host, so it never collapses\n * to a single row — or, for types with no `default`, to no rows at all.\n */\nexport function expandInstallFrameworks(frameworks: string[]): string[] {\n const normalized = dedupe(frameworks.map(normalizeFramework));\n if (normalized.length === 0) {\n return ['default'];\n }\n if (normalized.includes('all')) {\n return [...INSTALLABLE_FRAMEWORKS];\n }\n return normalized;\n}\n\n/*\n * Marketplace journey helpers (ADR-0010). A marketplace installs in two\n * steps — register the catalog with the AI tool, then install plugins from\n * it. Both Claude Code and Copilot CLI read the same\n * `.claude-plugin/marketplace.json` format and address the repo as\n * `owner/repo`; the plugin derives that slug from the mandatory\n * `source-location` (the producer contract requires the body doc to live\n * inside the marketplace repo). No derivation → the UI falls back to the\n * rendered body, which stays canonical.\n */\n\n/** Frameworks with a marketplace-add concept (Cursor/Gemini have none). */\nexport const MARKETPLACE_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n] as const;\n\n/**\n * Narrow a resource's declared frameworks to those a given journey can\n * actually launch. A deep link is a claim about compatibility: offering\n * \"Install in Claude\" for a resource that never declared `claude-code` tells\n * the user something untrue about the resource.\n *\n * `all` and an empty list both mean \"unrestricted\" and expand to every\n * capable host. Anything else is intersected, so a declared framework with no\n * handler (Gemini) — or an unknown token — yields no links rather than\n * silently widening to every host.\n */\nexport function resolveCapableFrameworks(\n declared: string[],\n capable: readonly string[],\n): string[] {\n const normalized = dedupe(declared.map(normalizeFramework));\n if (normalized.length === 0 || normalized.includes('all')) {\n return [...capable];\n }\n return normalized.filter(f => capable.includes(f));\n}\n\n/**\n * Derive the `owner/repo` slug from a marketplace's `source-location`.\n * GitHub URLs only — other hosts return `undefined` (body-only fallback).\n */\nexport function getMarketplaceRepoSlug(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo] = url.pathname.split('/').filter(Boolean);\n if (!owner || !repo) {\n return undefined;\n }\n return `${owner}/${repo.replace(/\\.git$/, '')}`;\n}\n\n/**\n * A one-click launcher for an add command: a custom-scheme URL that opens\n * the host app with the command pre-filled in the prompt box. The host\n * never auto-executes it — the user reviews and presses Enter — and an\n * unregistered scheme is a harmless no-op, so these complement the copy\n * button rather than replace it.\n */\nexport interface ResourceDeepLink {\n /** Host application name; render as \"Add in <label>\" / \"Install in <label>\". */\n label: string;\n href: string;\n}\n\nexport interface MarketplaceAddCommand {\n framework: string;\n command: string;\n deepLinks: ResourceDeepLink[];\n}\n\n/**\n * The copyable marketplace-add command per compatible framework. `all`,\n * unknown-only, or empty framework lists expand to every capable framework;\n * frameworks without a marketplace concept produce no row.\n *\n * Claude Code carries a single deep link on the CLI's documented\n * `claude-cli://open?q=` handler, which opens a Claude Code terminal\n * session with the command pre-filled regardless of the user's editor.\n *\n * Copilot's CLI still has no URI scheme, but Copilot's *other* surface does:\n * VS Code 1.113 ships `vscode://chat-plugin/add-marketplace?ref=`, which\n * accepts a plain or base64 `owner/repo` and always shows a confirmation\n * dialog before registering the marketplace (it also dedupes against the\n * user's existing entries). So the Copilot row keeps its terminal command for\n * CLI users *and* gains editor launchers, mirroring how `mcp-config` already\n * maps `github-copilot` onto the VS Code stable/Insiders pair.\n */\nexport function getMarketplaceAddCommands(\n frameworks: string[],\n repoSlug: string,\n): MarketplaceAddCommand[] {\n const capable = resolveCapableFrameworks(\n frameworks,\n MARKETPLACE_CAPABLE_FRAMEWORKS,\n );\n const commands: MarketplaceAddCommand[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const command = `/plugin marketplace add ${repoSlug}`;\n commands.push({\n framework,\n command,\n deepLinks: [\n {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(command)}`,\n },\n ],\n });\n } else if (framework === 'github-copilot') {\n const ref = encodeURIComponent(repoSlug);\n commands.push({\n framework,\n command: `copilot plugin marketplace add ${repoSlug}`,\n deepLinks: [\n {\n label: 'VS Code',\n href: `vscode://chat-plugin/add-marketplace?ref=${ref}`,\n },\n {\n label: 'VS Code Insiders',\n href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`,\n },\n ],\n });\n }\n }\n return commands;\n}\n\n/** The raw-file URL for a GitHub blob `source-location`; else `undefined`. */\nfunction getRawGithubFileUrl(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo, marker, ref, ...path] = url.pathname\n .split('/')\n .filter(Boolean);\n if (!owner || !repo || marker !== 'blob' || !ref || path.length === 0) {\n return undefined;\n }\n return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path.join(\n '/',\n )}`;\n}\n\n/**\n * A launcher that opens an agent with an install prompt pre-filled. Neither\n * host auto-executes: the user reads the prompt and presses Enter. Claude Code\n * and Cursor are the two tools exposing a generic prompt URI — Copilot's\n * editor has purpose-built routes but no prompt handler, and Gemini has\n * neither (its `gemini.google.com/app?prompt=` link drives the web app, which\n * cannot write to a local workspace).\n *\n * Cursor truncates a deeplink at the first raw `&`, so the prompt must be\n * percent-encoded — `encodeURIComponent` handles that. Cursor also caps the\n * URL at ~8000 characters; these prompts are a URL plus a path, far under it.\n */\nfunction claudePromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(prompt)}`,\n };\n}\n\nfunction cursorPromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(\n prompt,\n )}`,\n };\n}\n\n/**\n * Frameworks with a one-click agent-install handler (Gemini has none).\n * OpenCode's Desktop app does register a real `opencode://` scheme, but every\n * route (`open-project`, `new-session`) requires an absolute local\n * `directory` param a web page cannot supply, so it's excluded here too.\n */\nexport const AGENT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/** Hosts reachable by a generic prompt URI — the only route for skill/hook. */\nexport const PROMPT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'cursor',\n] as const;\n\n/**\n * The plain http(s) URL behind a `source-location`. Unlike\n * `getRawGithubFileUrl` this accepts **directory (`tree`) URLs**, which is what\n * a multi-file `skill` actually carries — a raw-file URL would resolve to\n * `undefined` for exactly the type that most needs a launcher.\n */\nfunction getSourceUrl(sourceLocation: string | undefined): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n try {\n const url = new URL(sourceLocation.replace(/^url:/, ''));\n return url.protocol === 'http:' || url.protocol === 'https:'\n ? url.toString()\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * One-click launchers for the types with no purpose-built install route —\n * `skill` and `hook`. Both ride the generic prompt handlers, so the prompt\n * names the *host's own* install path and respects its install mode: a `merge`\n * target is asked to be merged into, never overwritten, so a launcher can\n * never cost the user their existing settings.\n *\n * Returns `[]` when the source URL cannot be derived, or for hosts with no\n * prompt route (Copilot, Gemini) — the path list and copy/download remain.\n */\nexport function getPromptInstallLinks(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n return getInstallSteps(type, sourceLocation, name, frameworks)\n .map(step => step.link)\n .filter((link): link is ResourceDeepLink => !!link);\n}\n\n/**\n * Everything a single host needs to install a resource: where the body goes,\n * the prompt that puts it there, and a launcher when the host has a URI to\n * launch.\n *\n * `link` is absent for hosts with no prompt route — Copilot and Gemini. That\n * absence is why the step carries `prompt` as text: a host without a URI is\n * not a host without an install path, and the same instruction that a launcher\n * would pre-fill can be pasted into that agent by hand. Every declared\n * framework therefore gets something actionable, not a bare path.\n */\nexport interface ResourceInstallStep {\n framework: string;\n target: ResourceInstallTarget;\n /** The install instruction, ready to run in that host's agent. */\n prompt: string;\n /** One-click launcher, where the host exposes a generic prompt URI. */\n link?: ResourceDeepLink;\n}\n\nexport function getInstallSteps(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceInstallStep[] {\n const sourceUrl = getSourceUrl(sourceLocation);\n if (!sourceUrl) {\n return [];\n }\n const label = RESOURCE_TYPE_REGISTRY[type].label.toLowerCase();\n const steps: ResourceInstallStep[] = [];\n for (const framework of expandInstallFrameworks(frameworks)) {\n const target = getResourceInstallTarget(type, framework, name);\n if (!target) {\n continue;\n }\n const prompt =\n target.mode === 'merge'\n ? `Install the \"${name}\" ${label}: fetch ${sourceUrl} and merge it into ${target.path}, keeping my existing settings intact.`\n : `Install the \"${name}\" ${label}: fetch ${sourceUrl} and save it to ${target.path}.`;\n let link: ResourceDeepLink | undefined;\n if (framework === 'claude-code') {\n link = claudePromptLink(prompt);\n } else if (framework === 'cursor') {\n link = cursorPromptLink(prompt);\n }\n steps.push({ framework, target, prompt, link });\n }\n return steps;\n}\n\n/** Frameworks with a one-click MCP-install handler (Gemini has none). */\nexport const MCP_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/**\n * One-click install links for an `agent` resource, restricted to the\n * resource's own compatible frameworks. VS Code stable and Insiders use the\n * native `chat-agent/install?url=` handler (the mechanism behind\n * awesome-copilot's Install buttons) and are Copilot's hosts, so they follow\n * `github-copilot`: VS Code downloads the file at `url` and asks the user\n * where to save it. Claude Code uses the `claude-cli://open?q=` handler with\n * an install prompt pre-filled to the convention path — reviewed and sent by\n * the user, never auto-executed. All derive from the GitHub blob URL in\n * `source-location`; anything else returns `[]` (copy/download fallback).\n */\nexport function getAgentInstallLinks(\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n const rawUrl = getRawGithubFileUrl(sourceLocation);\n if (!rawUrl) {\n return [];\n }\n const capable = resolveCapableFrameworks(\n frameworks,\n AGENT_LINK_CAPABLE_FRAMEWORKS,\n );\n const encodedRawUrl = encodeURIComponent(rawUrl);\n const installPrompt = (framework: string) => {\n const target = getResourceInstallTarget('agent', framework, name);\n return `Install this agent: fetch ${rawUrl} and save it to ${target?.path}`;\n };\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n links.push(claudePromptLink(installPrompt('claude-code')));\n } else if (framework === 'cursor') {\n links.push(cursorPromptLink(installPrompt('cursor')));\n } else if (framework === 'github-copilot') {\n links.push({\n label: 'VS Code',\n href: `vscode:chat-agent/install?url=${encodedRawUrl}`,\n });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`,\n });\n }\n }\n return links;\n}\n\n/**\n * One-click install links for an `mcp-config` resource, derived from its body —\n * the canonical `.mcp.json` snippet (spec §3.4). VS Code carries a native\n * `vscode:mcp/install?{json}` handler (Insiders scheme twin), Cursor a\n * documented `cursor://anysphere.cursor-deeplink/mcp/install` one, and\n * Claude Code gets a `claude-cli://open?q=` prompt around\n * `claude mcp add-json` — pre-filled, reviewed, never auto-executed.\n * Hosts follow the resource's own compatible frameworks: `all`/empty expand\n * to every capable host, anything else is intersected, so an mcp-config\n * declaring only Gemini (no handler) gets no links rather than every host's.\n * A missing, unparseable, or multi-server body returns `[]` (copy fallback) —\n * the body stays canonical, links are conveniences derived from it.\n */\nexport function getMcpInstallLinks(\n frameworks: string[],\n resourceName: string,\n bodyContent: string | undefined,\n): ResourceDeepLink[] {\n if (!bodyContent) {\n return [];\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(bodyContent);\n } catch {\n return [];\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return [];\n }\n\n let serverName = resourceName;\n let config: Record<string, unknown>;\n const servers = (parsed as { mcpServers?: unknown }).mcpServers;\n if (servers !== undefined) {\n if (typeof servers !== 'object' || servers === null) {\n return [];\n }\n const entries = Object.entries(servers as Record<string, unknown>);\n if (\n entries.length !== 1 ||\n typeof entries[0][1] !== 'object' ||\n entries[0][1] === null\n ) {\n return [];\n }\n serverName = entries[0][0];\n config = entries[0][1] as Record<string, unknown>;\n } else if ('command' in parsed || 'url' in parsed) {\n config = parsed as Record<string, unknown>;\n } else {\n return [];\n }\n\n const capable = resolveCapableFrameworks(\n frameworks,\n MCP_LINK_CAPABLE_FRAMEWORKS,\n );\n\n const configJson = JSON.stringify(config);\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const prompt = encodeURIComponent(\n `Install this MCP config by running: claude mcp add-json ${serverName} '${configJson}'`,\n );\n links.push({ label: 'Claude', href: `claude-cli://open?q=${prompt}` });\n } else if (framework === 'github-copilot') {\n const vsConfig = encodeURIComponent(\n JSON.stringify({ name: serverName, ...config }),\n );\n links.push({ label: 'VS Code', href: `vscode:mcp/install?${vsConfig}` });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:mcp/install?${vsConfig}`,\n });\n } else if (framework === 'cursor') {\n try {\n links.push({\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(\n serverName,\n )}&config=${btoa(configJson)}`,\n });\n } catch {\n // btoa rejects non-Latin1 config content — skip the Cursor link.\n }\n }\n }\n return links;\n}\n\n/**\n * Step two of the journey: how to install a plugin from the marketplace.\n * `marketplaceName` is the entity name, which the producer contract requires\n * to equal the `name` in `marketplace.json`.\n */\nexport function getMarketplaceInstallTemplate(marketplaceName: string): string {\n return `/plugin install <plugin>@${marketplaceName}`;\n}\n\n/**\n * The `.claude/settings.json` snippet a team commits so collaborators are\n * prompted to install the marketplace automatically (Claude Code).\n */\nexport function getMarketplaceTeamSnippet(\n marketplaceName: string,\n repoSlug: string,\n): string {\n return JSON.stringify(\n {\n extraKnownMarketplaces: {\n [marketplaceName]: { source: { source: 'github', repo: repoSlug } },\n },\n },\n null,\n 2,\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resources.cjs.js","sources":["../src/resources.ts"],"sourcesContent":["/**\n * The v2 read contract: AiResource vocabulary, the flat ResourceSummary\n * returned by the backend, and the shared framework resolver (ADR-0003).\n *\n * The legacy asset-model types in `types.ts` coexist with this module until\n * the legacy silo is deleted (slice 8, issue #34).\n */\n\n/** The six canonical `spec.type` values of an AiResource (ADR-0003, ADR-0010). */\nexport const RESOURCE_TYPES = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n 'plugin',\n 'marketplace',\n] as const;\n\nexport type ResourceType = (typeof RESOURCE_TYPES)[number];\n\nexport function isResourceType(value: unknown): value is ResourceType {\n return (\n typeof value === 'string' &&\n (RESOURCE_TYPES as readonly string[]).includes(value)\n );\n}\n\n/**\n * The AiResource types a `plugin` may contain — restricted to the component\n * kinds a real Claude Code / GitHub Copilot plugin manifest allows: `skills`,\n * `agents`, `hooks` and `mcpServers` (schemastore `claude-code-plugin` /\n * `claude-code-marketplace.json`). A plugin's other manifest components\n * (`commands`, `lspServers`, `outputStyles`, `themes`, `channels`, `monitors`)\n * have no AiResource type, so they are never members here; other plugins are\n * `dependencies`, not containment.\n */\nexport const PLUGIN_MEMBER_TYPES: readonly ResourceType[] = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n];\n\n/**\n * The AiResource types a `marketplace` may contain: `plugin` only. A real\n * `.claude-plugin/marketplace.json` `plugins` array lists plugins and nothing\n * else (required by the schema Claude Code and Copilot CLI both read).\n */\nexport const MARKETPLACE_MEMBER_TYPES: readonly ResourceType[] = ['plugin'];\n\n/**\n * Whether a container of `parent` type renders a child of `child` type as a\n * member — restricted to what a real Claude/Copilot plugin or marketplace\n * manifest permits (ADR-0010/0015). A `marketplace` contains `plugin` only; a\n * `plugin` contains the component types DevAI Hub models. Any other pairing is\n * silently not rendered. This is a rendering convention, not catalog\n * validation (upstream `allowedKinds` is the broad `[\"AiResource\"]`).\n */\nexport function isRenderableContainment(\n parent: ResourceType,\n child: ResourceType,\n): boolean {\n if (parent === 'marketplace') return MARKETPLACE_MEMBER_TYPES.includes(child);\n if (parent === 'plugin') return PLUGIN_MEMBER_TYPES.includes(child);\n return false;\n}\n\n/** Annotation prefix owned by DevAI Hub. */\nexport const DEVAIHUB_ANNOTATION_PREFIX = 'devaihub.io';\n\n/** Comma-separated compatible-framework tokens (non-skill read path, ADR-0003). */\nexport const ANNOTATION_COMPATIBLE_FRAMEWORKS = `${DEVAIHUB_ANNOTATION_PREFIX}/compatible-frameworks`;\n\n/** Optional free-form help text shown on the detail panel. */\nexport const ANNOTATION_HELP = `${DEVAIHUB_ANNOTATION_PREFIX}/help`;\n\n/** Known framework tokens; unknown tokens pass through as-is (ADR-0003). */\nexport const KNOWN_FRAMEWORKS = [\n 'github-copilot',\n 'claude-code',\n 'cursor',\n 'google-gemini',\n 'opencode',\n 'all',\n] as const;\n\nexport type FrameworkToken = (typeof KNOWN_FRAMEWORKS)[number];\n\nconst FRAMEWORK_ALIASES: Record<string, FrameworkToken> = {\n copilot: 'github-copilot',\n claude: 'claude-code',\n gemini: 'google-gemini',\n};\n\n/**\n * Normalise a framework token: trim, lowercase, resolve aliases\n * (`claude` → `claude-code`). Unknown tokens pass through literally.\n */\nexport function normalizeFramework(token: string): string {\n const t = token.trim().toLowerCase();\n return FRAMEWORK_ALIASES[t] ?? t;\n}\n\n/**\n * Narrow a resource's frameworks to a deployment-level allow-list\n * (`devAiHub.frameworks.allowed`). Omitting the config key (`allowed` is\n * `undefined`) permits every framework; an explicit empty list permits none,\n * so a deployment can suppress framework exposure entirely. Both sides are\n * normalised (aliases resolved) before comparison.\n *\n * The `all` wildcard is expanded to the allow-list itself before filtering, so\n * a resource that declares `all` degrades to \"every permitted host\" rather\n * than disappearing entirely.\n */\nexport function filterAllowedFrameworks(\n frameworks: string[],\n allowed: string[] | undefined,\n): string[] {\n if (allowed === undefined) {\n return frameworks;\n }\n const permitted = new Set(\n allowed.map(normalizeFramework).filter(f => f && f !== 'all'),\n );\n const expanded = frameworks.flatMap(f =>\n normalizeFramework(f) === 'all' ? [...permitted] : [normalizeFramework(f)],\n );\n return Array.from(new Set(expanded)).filter(f => permitted.has(f));\n}\n\n/**\n * The minimal structural shape of an AiResource entity that the read helpers\n * need. A real `@backstage/catalog-model` `Entity` satisfies it, but keeping\n * the shape local means `-common` (and therefore the frontend) never depends\n * on catalog packages (architecture.md).\n */\nexport interface AiResourceEntityLike {\n metadata: {\n annotations?: Record<string, string>;\n };\n spec?: {\n type?: unknown;\n /** Native compatible-frameworks field, honoured regardless of `spec.type`. */\n agents?: unknown;\n [key: string]: unknown;\n };\n}\n\n/**\n * Resolve the compatible frameworks of an AiResource (ADR-0003):\n * 1. a non-empty `spec.agents` → the native field, for any `spec.type`;\n * 2. otherwise → the `devaihub.io/compatible-frameworks` annotation\n * (also the fallback when `spec.agents` is empty/absent);\n * 3. otherwise → `[]`.\n * Tokens are normalised; unknown tokens pass through. `spec.agents` is\n * preferred over the annotation because it lives on the entity itself\n * rather than in an annotation namespace we may retire.\n */\nexport function getFrameworks(entity: AiResourceEntityLike): string[] {\n if (Array.isArray(entity.spec?.agents)) {\n const agents = entity.spec.agents\n .filter((a): a is string => typeof a === 'string')\n .map(normalizeFramework)\n .filter(Boolean);\n if (agents.length > 0) {\n return dedupe(agents);\n }\n }\n\n const raw = entity.metadata.annotations?.[ANNOTATION_COMPATIBLE_FRAMEWORKS];\n if (!raw) {\n return [];\n }\n return dedupe(raw.split(',').map(normalizeFramework).filter(Boolean));\n}\n\nfunction dedupe(tokens: string[]): string[] {\n return Array.from(new Set(tokens));\n}\n\n/**\n * Icon identifiers for the type→card registry. `-common` is imported by the\n * backend, so it stays React-free: the frontend maps these identifiers to\n * actual icon components.\n */\nexport type ResourceTypeIcon =\n | 'tools'\n | 'robot'\n | 'flash'\n | 'plug'\n | 'puzzle'\n | 'store';\n\nexport interface ResourceTypeInfo {\n type: ResourceType;\n /** Singular display label, e.g. \"Skill\" (card type caption). */\n label: string;\n /** Plural display label, e.g. \"Skills\" (stat tiles, filters). */\n pluralLabel: string;\n /** Icon identifier resolved to a component by the frontend. */\n icon: ResourceTypeIcon;\n /**\n * Colour role name; the frontend resolves it to the plugin-owned\n * `--devaihub-type-<role>` custom properties (ADR-0008, NOS palette).\n */\n colorRole: ResourceType;\n}\n\n/**\n * The typed type→card registry (ADR-0003/ADR-0008): producer vocabulary and\n * consumer rendering share this single source, so they cannot drift. Adding\n * a type is one entry here plus one CSS token pair in the frontend.\n */\nexport const RESOURCE_TYPE_REGISTRY: Record<ResourceType, ResourceTypeInfo> = {\n skill: {\n type: 'skill',\n label: 'Skill',\n pluralLabel: 'Skills',\n icon: 'tools',\n colorRole: 'skill',\n },\n agent: {\n type: 'agent',\n label: 'Agent',\n pluralLabel: 'Agents',\n icon: 'robot',\n colorRole: 'agent',\n },\n hook: {\n type: 'hook',\n label: 'Hook',\n pluralLabel: 'Hooks',\n icon: 'flash',\n colorRole: 'hook',\n },\n 'mcp-config': {\n type: 'mcp-config',\n label: 'MCP Config',\n pluralLabel: 'MCP Configs',\n icon: 'plug',\n colorRole: 'mcp-config',\n },\n plugin: {\n type: 'plugin',\n label: 'Plugin',\n pluralLabel: 'Plugins',\n icon: 'puzzle',\n colorRole: 'plugin',\n },\n marketplace: {\n type: 'marketplace',\n label: 'Marketplace',\n pluralLabel: 'Marketplaces',\n icon: 'store',\n colorRole: 'marketplace',\n },\n};\n\n/**\n * The flat contract the backend returns to the frontend. The frontend never\n * sees a raw Backstage `Entity` — only this (architecture.md).\n */\nexport interface ResourceSummary {\n entityRef: string;\n name: string;\n title?: string;\n description?: string;\n tags: string[];\n type: ResourceType;\n lifecycle: string;\n owner?: string;\n sourceLocation?: string;\n frameworks: string[];\n /**\n * True when an active deployment allow-list (`devAiHub.frameworks.allowed`)\n * left this resource with no frameworks — either it removed the ones the\n * author declared, or the allow-list itself is empty. Lets the UI tell\n * \"author declared nothing\" (`frameworks: []`, show neutral install\n * defaults) apart from \"the deployment suppressed them\" (hide generated\n * install paths and launchers; Copy/Download still work).\n */\n frameworksRestricted?: boolean;\n /**\n * The framework set that install-path and launcher generation must be\n * scoped to, when it differs from `frameworks`. Only set for a resource\n * that declared no frameworks (which otherwise means \"every host\") under an\n * active allow-list: it holds the allow-list itself, so a bare resource\n * cannot produce launchers for tools the deployment disallowed. Kept\n * separate from `frameworks` so this inferred set never renders as a\n * compatibility badge the author never claimed.\n */\n installFrameworks?: string[];\n version?: string;\n kind: string;\n /**\n * Entity refs this container declares as its contents — a `marketplace`'s\n * `spec.plugins`, a `plugin`'s `spec.skills`; empty for leaf types. Filtered\n * to the caller's visible set and to the child types the container renders\n * (ADR-0015). The frontend resolves each ref against the summaries it holds.\n */\n children: string[];\n /**\n * Entity refs of the containers that declare this resource — the in-memory\n * inverse of `children`, built over the same caller-visible catalog read\n * (ADR-0015). A resource can belong to several containers.\n */\n parents: string[];\n /** Count of `children` (their declared, caller-visible members). */\n childCount?: number;\n helpText?: string;\n annotations: Record<string, string>;\n}\n\nexport interface ResourceListResponse {\n items: ResourceSummary[];\n}\n\n/**\n * The shape of a resource's body (CONTEXT.md: bodies are type-shaped, not\n * uniformly markdown). Drives detail-panel rendering: markdown is rendered,\n * json is pretty-printed and syntax-highlighted (ADR-0014). `plugin` and\n * `marketplace` bodies are the real Claude Code `plugin.json` /\n * `marketplace.json` manifest — not a hand-authored pointer doc, superseding\n * ADR-0010's markdown-only body for `marketplace`.\n */\nexport type BodyShape = 'markdown' | 'json';\n\nexport function getBodyShape(type: ResourceType): BodyShape {\n return type === 'mcp-config' || type === 'plugin' || type === 'marketplace'\n ? 'json'\n : 'markdown';\n}\n\n/**\n * Whether the detail panel shows a \"Content\" section for this type.\n *\n * Suppressed for exactly the JSON-bodied types (`getBodyShape` === 'json'):\n * `plugin` and `marketplace` are containers whose value is the relationship\n * graph rather than the manifest, and an `mcp-config`'s body is a settings\n * fragment to merge — in all three the raw JSON buries the panel and is\n * better reached through Copy, Download or the install dialog, which is\n * where it is actionable. The markdown types keep their rendered body.\n */\nexport function showsContentSection(type: ResourceType): boolean {\n return getBodyShape(type) !== 'json';\n}\n\n/**\n * Whether downloading the body delivers the artifact itself. True for every\n * type: `plugin`/`marketplace` bodies are now the real manifest JSON, so a\n * download delivers that file itself rather than a non-actionable pointer\n * doc (ADR-0014, superseding ADR-0009's amendment and ADR-0010 on this\n * point).\n */\nexport function hasDownloadableArtifact(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * Whether copying the body to the clipboard is a meaningful install action.\n * True for every type: `marketplace`'s body is now the real manifest JSON,\n * not an instructions doc, so copying it is no longer a misrepresentation\n * (ADR-0014, superseding ADR-0010 on this point).\n */\nexport function hasCopyableBody(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * How a body reaches its install path.\n *\n * `drop-in` — the body becomes the file (or directory) at `path`, which is\n * the resource's own; writing it touches nothing else.\n *\n * `merge` — `path` is a shared settings file the user already owns, and the\n * body is a fragment to merge into it. The distinction is not cosmetic:\n * following a `merge` target as if it were a `drop-in` overwrites the user's\n * existing configuration, so the UI must never present the two identically.\n */\nexport type InstallMode = 'drop-in' | 'merge';\n\nexport interface ResourceInstallTarget {\n /** Workspace-relative path. */\n path: string;\n mode: InstallMode;\n}\n\n/**\n * The concrete framework tokens a body can be installed into — `all` is a\n * wildcard over these, never a destination itself.\n */\nexport const INSTALLABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'google-gemini',\n 'cursor',\n 'opencode',\n] as const;\n\n/**\n * Convention table: (type, framework) → where the body goes and how.\n * A missing entry means the pair has no filesystem convention — `plugin` and\n * `marketplace` bodies are pointers that install through their framework\n * (ADR-0009, ADR-0010), so they carry install commands rather than paths.\n */\nconst INSTALL_PATHS: Record<\n ResourceType,\n Record<string, { mode: InstallMode; path: (name: string) => string }>\n> = {\n // Every host reads a skill as a self-contained directory. Copilot also\n // picks up `.claude/skills`, but each tool's own path is what we recommend;\n // `.agents/skills` is the cross-tool alias Copilot and Gemini both honour,\n // which makes it the right answer for an unrecognised framework.\n skill: {\n 'claude-code': { mode: 'drop-in', path: name => `.claude/skills/${name}/` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/skills/${name}/`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/skills/${name}/`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/skills/${name}/` },\n opencode: { mode: 'drop-in', path: name => `.opencode/skills/${name}/` },\n default: { mode: 'drop-in', path: name => `.agents/skills/${name}/` },\n },\n agent: {\n 'claude-code': {\n mode: 'drop-in',\n path: name => `.claude/agents/${name}.md`,\n },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/agents/${name}.agent.md`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/agents/${name}.md`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/rules/${name}.mdc` },\n opencode: { mode: 'drop-in', path: name => `.opencode/agents/${name}.md` },\n default: { mode: 'drop-in', path: name => `.ai/agents/${name}.md` },\n },\n // All four hosts have a hook system, but only Copilot gives each hook its\n // own file; the rest register hooks inside a shared settings document.\n // OpenCode has no declarative hook config at all — hooks are exclusively\n // JS/TS plugin modules — so it deliberately carries no entry here, the same\n // way it carries no `default`.\n hook: {\n 'claude-code': { mode: 'merge', path: () => `.claude/settings.json` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/hooks/${name}.json`,\n },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/hooks.json` },\n },\n 'mcp-config': {\n 'claude-code': { mode: 'merge', path: () => `.mcp.json` },\n 'github-copilot': { mode: 'merge', path: () => `.vscode/mcp.json` },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/mcp.json` },\n opencode: { mode: 'merge', path: () => `opencode.json` },\n },\n plugin: {},\n marketplace: {},\n};\n\n/**\n * Where a resource's body installs for one framework, and whether that path\n * is the body's own file or a settings file to merge into. Returns\n * `undefined` when the (type, framework) pair has no filesystem convention.\n */\nexport function getResourceInstallTarget(\n type: ResourceType,\n framework: string,\n name: string,\n): ResourceInstallTarget | undefined {\n const conventions = INSTALL_PATHS[type];\n const entry =\n conventions[normalizeFramework(framework)] ?? conventions.default;\n return entry ? { path: entry.path(name), mode: entry.mode } : undefined;\n}\n\n/**\n * The frameworks to show install paths for. An empty list means the resource\n * declared no compatibility and gets the neutral `default` convention; `all`\n * is a wildcard and expands to every installable host, so it never collapses\n * to a single row — or, for types with no `default`, to no rows at all.\n */\nexport function expandInstallFrameworks(frameworks: string[]): string[] {\n const normalized = dedupe(frameworks.map(normalizeFramework));\n if (normalized.length === 0) {\n return ['default'];\n }\n if (normalized.includes('all')) {\n return [...INSTALLABLE_FRAMEWORKS];\n }\n return normalized;\n}\n\n/*\n * Marketplace journey helpers (ADR-0010). A marketplace installs in two\n * steps — register the catalog with the AI tool, then install plugins from\n * it. Both Claude Code and Copilot CLI read the same\n * `.claude-plugin/marketplace.json` format and address the repo as\n * `owner/repo`; the plugin derives that slug from the mandatory\n * `source-location` (the producer contract requires the body doc to live\n * inside the marketplace repo). No derivation → the UI falls back to the\n * rendered body, which stays canonical.\n */\n\n/** Frameworks with a marketplace-add concept (Cursor/Gemini have none). */\nexport const MARKETPLACE_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n] as const;\n\n/**\n * Narrow a resource's declared frameworks to those a given journey can\n * actually launch. A deep link is a claim about compatibility: offering\n * \"Install in Claude\" for a resource that never declared `claude-code` tells\n * the user something untrue about the resource.\n *\n * `all` and an empty list both mean \"unrestricted\" and expand to every\n * capable host. Anything else is intersected, so a declared framework with no\n * handler (Gemini) — or an unknown token — yields no links rather than\n * silently widening to every host.\n */\nexport function resolveCapableFrameworks(\n declared: string[],\n capable: readonly string[],\n): string[] {\n const normalized = dedupe(declared.map(normalizeFramework));\n if (normalized.length === 0 || normalized.includes('all')) {\n return [...capable];\n }\n return normalized.filter(f => capable.includes(f));\n}\n\n/**\n * Derive the `owner/repo` slug from a marketplace's `source-location`.\n * GitHub URLs only — other hosts return `undefined` (body-only fallback).\n */\nexport function getMarketplaceRepoSlug(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo] = url.pathname.split('/').filter(Boolean);\n if (!owner || !repo) {\n return undefined;\n }\n return `${owner}/${repo.replace(/\\.git$/, '')}`;\n}\n\n/**\n * A one-click launcher for an add command: a custom-scheme URL that opens\n * the host app with the command pre-filled in the prompt box. The host\n * never auto-executes it — the user reviews and presses Enter — and an\n * unregistered scheme is a harmless no-op, so these complement the copy\n * button rather than replace it.\n */\nexport interface ResourceDeepLink {\n /** Host application name; render as \"Add in <label>\" / \"Install in <label>\". */\n label: string;\n href: string;\n /**\n * The framework token this launcher installs into, for the `tool` column on\n * the `install` event it records (ADR-0011). Not 1:1 with `label`:\n * `github-copilot` offers both \"VS Code\" and \"VS Code Insiders\".\n *\n * Optional only to keep this published interface additive — every link the\n * generators below build sets it, which `resources.test.ts` pins. A caller\n * reading a generated link can rely on it; one constructing its own shape\n * is not forced to supply it.\n */\n framework?: string;\n}\n\nexport interface MarketplaceAddCommand {\n framework: string;\n command: string;\n deepLinks: ResourceDeepLink[];\n}\n\n/**\n * The copyable marketplace-add command per compatible framework. `all`,\n * unknown-only, or empty framework lists expand to every capable framework;\n * frameworks without a marketplace concept produce no row.\n *\n * Claude Code carries a single deep link on the CLI's documented\n * `claude-cli://open?q=` handler, which opens a Claude Code terminal\n * session with the command pre-filled regardless of the user's editor.\n *\n * Copilot's CLI still has no URI scheme, but Copilot's *other* surface does:\n * VS Code 1.113 ships `vscode://chat-plugin/add-marketplace?ref=`, which\n * accepts a plain or base64 `owner/repo` and always shows a confirmation\n * dialog before registering the marketplace (it also dedupes against the\n * user's existing entries). So the Copilot row keeps its terminal command for\n * CLI users *and* gains editor launchers, mirroring how `mcp-config` already\n * maps `github-copilot` onto the VS Code stable/Insiders pair.\n */\nexport function getMarketplaceAddCommands(\n frameworks: string[],\n repoSlug: string,\n): MarketplaceAddCommand[] {\n const capable = resolveCapableFrameworks(\n frameworks,\n MARKETPLACE_CAPABLE_FRAMEWORKS,\n );\n const commands: MarketplaceAddCommand[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const command = `/plugin marketplace add ${repoSlug}`;\n commands.push({\n framework,\n command,\n deepLinks: [\n {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(command)}`,\n framework,\n },\n ],\n });\n } else if (framework === 'github-copilot') {\n const ref = encodeURIComponent(repoSlug);\n commands.push({\n framework,\n command: `copilot plugin marketplace add ${repoSlug}`,\n deepLinks: [\n {\n label: 'VS Code',\n href: `vscode://chat-plugin/add-marketplace?ref=${ref}`,\n framework,\n },\n {\n label: 'VS Code Insiders',\n href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`,\n framework,\n },\n ],\n });\n }\n }\n return commands;\n}\n\n/** The raw-file URL for a GitHub blob `source-location`; else `undefined`. */\nfunction getRawGithubFileUrl(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo, marker, ref, ...path] = url.pathname\n .split('/')\n .filter(Boolean);\n if (!owner || !repo || marker !== 'blob' || !ref || path.length === 0) {\n return undefined;\n }\n return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path.join(\n '/',\n )}`;\n}\n\n/**\n * A launcher that opens an agent with an install prompt pre-filled. Neither\n * host auto-executes: the user reads the prompt and presses Enter. Claude Code\n * and Cursor are the two tools exposing a generic prompt URI — Copilot's\n * editor has purpose-built routes but no prompt handler, and Gemini has\n * neither (its `gemini.google.com/app?prompt=` link drives the web app, which\n * cannot write to a local workspace).\n *\n * Cursor truncates a deeplink at the first raw `&`, so the prompt must be\n * percent-encoded — `encodeURIComponent` handles that. Cursor also caps the\n * URL at ~8000 characters; these prompts are a URL plus a path, far under it.\n */\nfunction claudePromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(prompt)}`,\n framework: 'claude-code',\n };\n}\n\nfunction cursorPromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(\n prompt,\n )}`,\n framework: 'cursor',\n };\n}\n\n/**\n * Frameworks with a one-click agent-install handler (Gemini has none).\n * OpenCode's Desktop app does register a real `opencode://` scheme, but every\n * route (`open-project`, `new-session`) requires an absolute local\n * `directory` param a web page cannot supply, so it's excluded here too.\n */\nexport const AGENT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/** Hosts reachable by a generic prompt URI — the only route for skill/hook. */\nexport const PROMPT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'cursor',\n] as const;\n\n/**\n * The plain http(s) URL behind a `source-location`. Unlike\n * `getRawGithubFileUrl` this accepts **directory (`tree`) URLs**, which is what\n * a multi-file `skill` actually carries — a raw-file URL would resolve to\n * `undefined` for exactly the type that most needs a launcher.\n */\nfunction getSourceUrl(sourceLocation: string | undefined): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n try {\n const url = new URL(sourceLocation.replace(/^url:/, ''));\n return url.protocol === 'http:' || url.protocol === 'https:'\n ? url.toString()\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * One-click launchers for the types with no purpose-built install route —\n * `skill` and `hook`. Both ride the generic prompt handlers, so the prompt\n * names the *host's own* install path and respects its install mode: a `merge`\n * target is asked to be merged into, never overwritten, so a launcher can\n * never cost the user their existing settings.\n *\n * Returns `[]` when the source URL cannot be derived, or for hosts with no\n * prompt route (Copilot, Gemini) — the path list and copy/download remain.\n */\nexport function getPromptInstallLinks(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n return getInstallSteps(type, sourceLocation, name, frameworks)\n .map(step => step.link)\n .filter((link): link is ResourceDeepLink => !!link);\n}\n\n/**\n * Everything a single host needs to install a resource: where the body goes,\n * the prompt that puts it there, and a launcher when the host has a URI to\n * launch.\n *\n * `link` is absent for hosts with no prompt route — Copilot and Gemini. That\n * absence is why the step carries `prompt` as text: a host without a URI is\n * not a host without an install path, and the same instruction that a launcher\n * would pre-fill can be pasted into that agent by hand. Every declared\n * framework therefore gets something actionable, not a bare path.\n */\nexport interface ResourceInstallStep {\n framework: string;\n target: ResourceInstallTarget;\n /** The install instruction, ready to run in that host's agent. */\n prompt: string;\n /** One-click launcher, where the host exposes a generic prompt URI. */\n link?: ResourceDeepLink;\n}\n\nexport function getInstallSteps(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceInstallStep[] {\n const sourceUrl = getSourceUrl(sourceLocation);\n if (!sourceUrl) {\n return [];\n }\n const label = RESOURCE_TYPE_REGISTRY[type].label.toLowerCase();\n const steps: ResourceInstallStep[] = [];\n for (const framework of expandInstallFrameworks(frameworks)) {\n const target = getResourceInstallTarget(type, framework, name);\n if (!target) {\n continue;\n }\n const prompt =\n target.mode === 'merge'\n ? `Install the \"${name}\" ${label}: fetch ${sourceUrl} and merge it into ${target.path}, keeping my existing settings intact.`\n : `Install the \"${name}\" ${label}: fetch ${sourceUrl} and save it to ${target.path}.`;\n let link: ResourceDeepLink | undefined;\n if (framework === 'claude-code') {\n link = claudePromptLink(prompt);\n } else if (framework === 'cursor') {\n link = cursorPromptLink(prompt);\n }\n steps.push({ framework, target, prompt, link });\n }\n return steps;\n}\n\n/** Frameworks with a one-click MCP-install handler (Gemini has none). */\nexport const MCP_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/**\n * One-click install links for an `agent` resource, restricted to the\n * resource's own compatible frameworks. VS Code stable and Insiders use the\n * native `chat-agent/install?url=` handler (the mechanism behind\n * awesome-copilot's Install buttons) and are Copilot's hosts, so they follow\n * `github-copilot`: VS Code downloads the file at `url` and asks the user\n * where to save it. Claude Code uses the `claude-cli://open?q=` handler with\n * an install prompt pre-filled to the convention path — reviewed and sent by\n * the user, never auto-executed. All derive from the GitHub blob URL in\n * `source-location`; anything else returns `[]` (copy/download fallback).\n */\nexport function getAgentInstallLinks(\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n const rawUrl = getRawGithubFileUrl(sourceLocation);\n if (!rawUrl) {\n return [];\n }\n const capable = resolveCapableFrameworks(\n frameworks,\n AGENT_LINK_CAPABLE_FRAMEWORKS,\n );\n const encodedRawUrl = encodeURIComponent(rawUrl);\n const installPrompt = (framework: string) => {\n const target = getResourceInstallTarget('agent', framework, name);\n return `Install this agent: fetch ${rawUrl} and save it to ${target?.path}`;\n };\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n links.push(claudePromptLink(installPrompt('claude-code')));\n } else if (framework === 'cursor') {\n links.push(cursorPromptLink(installPrompt('cursor')));\n } else if (framework === 'github-copilot') {\n links.push({\n label: 'VS Code',\n href: `vscode:chat-agent/install?url=${encodedRawUrl}`,\n framework,\n });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`,\n framework,\n });\n }\n }\n return links;\n}\n\n/**\n * One-click install links for an `mcp-config` resource, derived from its body —\n * the canonical `.mcp.json` snippet (spec §3.4). VS Code carries a native\n * `vscode:mcp/install?{json}` handler (Insiders scheme twin), Cursor a\n * documented `cursor://anysphere.cursor-deeplink/mcp/install` one, and\n * Claude Code gets a `claude-cli://open?q=` prompt around\n * `claude mcp add-json` — pre-filled, reviewed, never auto-executed.\n * Hosts follow the resource's own compatible frameworks: `all`/empty expand\n * to every capable host, anything else is intersected, so an mcp-config\n * declaring only Gemini (no handler) gets no links rather than every host's.\n * A missing, unparseable, or multi-server body returns `[]` (copy fallback) —\n * the body stays canonical, links are conveniences derived from it.\n */\nexport function getMcpInstallLinks(\n frameworks: string[],\n resourceName: string,\n bodyContent: string | undefined,\n): ResourceDeepLink[] {\n if (!bodyContent) {\n return [];\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(bodyContent);\n } catch {\n return [];\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return [];\n }\n\n let serverName = resourceName;\n let config: Record<string, unknown>;\n const servers = (parsed as { mcpServers?: unknown }).mcpServers;\n if (servers !== undefined) {\n if (typeof servers !== 'object' || servers === null) {\n return [];\n }\n const entries = Object.entries(servers as Record<string, unknown>);\n if (\n entries.length !== 1 ||\n typeof entries[0][1] !== 'object' ||\n entries[0][1] === null\n ) {\n return [];\n }\n serverName = entries[0][0];\n config = entries[0][1] as Record<string, unknown>;\n } else if ('command' in parsed || 'url' in parsed) {\n config = parsed as Record<string, unknown>;\n } else {\n return [];\n }\n\n const capable = resolveCapableFrameworks(\n frameworks,\n MCP_LINK_CAPABLE_FRAMEWORKS,\n );\n\n const configJson = JSON.stringify(config);\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const prompt = encodeURIComponent(\n `Install this MCP config by running: claude mcp add-json ${serverName} '${configJson}'`,\n );\n links.push({\n label: 'Claude',\n href: `claude-cli://open?q=${prompt}`,\n framework,\n });\n } else if (framework === 'github-copilot') {\n const vsConfig = encodeURIComponent(\n JSON.stringify({ name: serverName, ...config }),\n );\n links.push({\n label: 'VS Code',\n href: `vscode:mcp/install?${vsConfig}`,\n framework,\n });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:mcp/install?${vsConfig}`,\n framework,\n });\n } else if (framework === 'cursor') {\n try {\n links.push({\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(\n serverName,\n )}&config=${btoa(configJson)}`,\n framework,\n });\n } catch {\n // btoa rejects non-Latin1 config content — skip the Cursor link.\n }\n }\n }\n return links;\n}\n\n/**\n * Step two of the journey: how to install a plugin from the marketplace.\n * `marketplaceName` is the entity name, which the producer contract requires\n * to equal the `name` in `marketplace.json`.\n */\nexport function getMarketplaceInstallTemplate(marketplaceName: string): string {\n return `/plugin install <plugin>@${marketplaceName}`;\n}\n\n/**\n * The `.claude/settings.json` snippet a team commits so collaborators are\n * prompted to install the marketplace automatically (Claude Code).\n */\nexport function getMarketplaceTeamSnippet(\n marketplaceName: string,\n repoSlug: string,\n): string {\n return JSON.stringify(\n {\n extraKnownMarketplaces: {\n [marketplaceName]: { source: { source: 'github', repo: repoSlug } },\n },\n },\n null,\n 2,\n );\n}\n"],"names":[],"mappings":"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"}
@@ -117,7 +117,7 @@ function getBodyShape(type) {
117
117
  return type === "mcp-config" || type === "plugin" || type === "marketplace" ? "json" : "markdown";
118
118
  }
119
119
  function showsContentSection(type) {
120
- return type !== "plugin" && type !== "marketplace";
120
+ return getBodyShape(type) !== "json";
121
121
  }
122
122
  function hasDownloadableArtifact(_type) {
123
123
  return true;
@@ -252,7 +252,8 @@ function getMarketplaceAddCommands(frameworks, repoSlug) {
252
252
  deepLinks: [
253
253
  {
254
254
  label: "Claude",
255
- href: `claude-cli://open?q=${encodeURIComponent(command)}`
255
+ href: `claude-cli://open?q=${encodeURIComponent(command)}`,
256
+ framework
256
257
  }
257
258
  ]
258
259
  });
@@ -264,11 +265,13 @@ function getMarketplaceAddCommands(frameworks, repoSlug) {
264
265
  deepLinks: [
265
266
  {
266
267
  label: "VS Code",
267
- href: `vscode://chat-plugin/add-marketplace?ref=${ref}`
268
+ href: `vscode://chat-plugin/add-marketplace?ref=${ref}`,
269
+ framework
268
270
  },
269
271
  {
270
272
  label: "VS Code Insiders",
271
- href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`
273
+ href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`,
274
+ framework
272
275
  }
273
276
  ]
274
277
  });
@@ -300,7 +303,8 @@ function getRawGithubFileUrl(sourceLocation) {
300
303
  function claudePromptLink(prompt) {
301
304
  return {
302
305
  label: "Claude",
303
- href: `claude-cli://open?q=${encodeURIComponent(prompt)}`
306
+ href: `claude-cli://open?q=${encodeURIComponent(prompt)}`,
307
+ framework: "claude-code"
304
308
  };
305
309
  }
306
310
  function cursorPromptLink(prompt) {
@@ -308,7 +312,8 @@ function cursorPromptLink(prompt) {
308
312
  label: "Cursor",
309
313
  href: `cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(
310
314
  prompt
311
- )}`
315
+ )}`,
316
+ framework: "cursor"
312
317
  };
313
318
  }
314
319
  const AGENT_LINK_CAPABLE_FRAMEWORKS = [
@@ -385,11 +390,13 @@ function getAgentInstallLinks(sourceLocation, name, frameworks = []) {
385
390
  } else if (framework === "github-copilot") {
386
391
  links.push({
387
392
  label: "VS Code",
388
- href: `vscode:chat-agent/install?url=${encodedRawUrl}`
393
+ href: `vscode:chat-agent/install?url=${encodedRawUrl}`,
394
+ framework
389
395
  });
390
396
  links.push({
391
397
  label: "VS Code Insiders",
392
- href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`
398
+ href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`,
399
+ framework
393
400
  });
394
401
  }
395
402
  }
@@ -437,15 +444,24 @@ function getMcpInstallLinks(frameworks, resourceName, bodyContent) {
437
444
  const prompt = encodeURIComponent(
438
445
  `Install this MCP config by running: claude mcp add-json ${serverName} '${configJson}'`
439
446
  );
440
- links.push({ label: "Claude", href: `claude-cli://open?q=${prompt}` });
447
+ links.push({
448
+ label: "Claude",
449
+ href: `claude-cli://open?q=${prompt}`,
450
+ framework
451
+ });
441
452
  } else if (framework === "github-copilot") {
442
453
  const vsConfig = encodeURIComponent(
443
454
  JSON.stringify({ name: serverName, ...config })
444
455
  );
445
- links.push({ label: "VS Code", href: `vscode:mcp/install?${vsConfig}` });
456
+ links.push({
457
+ label: "VS Code",
458
+ href: `vscode:mcp/install?${vsConfig}`,
459
+ framework
460
+ });
446
461
  links.push({
447
462
  label: "VS Code Insiders",
448
- href: `vscode-insiders:mcp/install?${vsConfig}`
463
+ href: `vscode-insiders:mcp/install?${vsConfig}`,
464
+ framework
449
465
  });
450
466
  } else if (framework === "cursor") {
451
467
  try {
@@ -453,7 +469,8 @@ function getMcpInstallLinks(frameworks, resourceName, bodyContent) {
453
469
  label: "Cursor",
454
470
  href: `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(
455
471
  serverName
456
- )}&config=${btoa(configJson)}`
472
+ )}&config=${btoa(configJson)}`,
473
+ framework
457
474
  });
458
475
  } catch {
459
476
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resources.esm.js","sources":["../src/resources.ts"],"sourcesContent":["/**\n * The v2 read contract: AiResource vocabulary, the flat ResourceSummary\n * returned by the backend, and the shared framework resolver (ADR-0003).\n *\n * The legacy asset-model types in `types.ts` coexist with this module until\n * the legacy silo is deleted (slice 8, issue #34).\n */\n\n/** The six canonical `spec.type` values of an AiResource (ADR-0003, ADR-0010). */\nexport const RESOURCE_TYPES = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n 'plugin',\n 'marketplace',\n] as const;\n\nexport type ResourceType = (typeof RESOURCE_TYPES)[number];\n\nexport function isResourceType(value: unknown): value is ResourceType {\n return (\n typeof value === 'string' &&\n (RESOURCE_TYPES as readonly string[]).includes(value)\n );\n}\n\n/**\n * The AiResource types a `plugin` may contain — restricted to the component\n * kinds a real Claude Code / GitHub Copilot plugin manifest allows: `skills`,\n * `agents`, `hooks` and `mcpServers` (schemastore `claude-code-plugin` /\n * `claude-code-marketplace.json`). A plugin's other manifest components\n * (`commands`, `lspServers`, `outputStyles`, `themes`, `channels`, `monitors`)\n * have no AiResource type, so they are never members here; other plugins are\n * `dependencies`, not containment.\n */\nexport const PLUGIN_MEMBER_TYPES: readonly ResourceType[] = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n];\n\n/**\n * The AiResource types a `marketplace` may contain: `plugin` only. A real\n * `.claude-plugin/marketplace.json` `plugins` array lists plugins and nothing\n * else (required by the schema Claude Code and Copilot CLI both read).\n */\nexport const MARKETPLACE_MEMBER_TYPES: readonly ResourceType[] = ['plugin'];\n\n/**\n * Whether a container of `parent` type renders a child of `child` type as a\n * member — restricted to what a real Claude/Copilot plugin or marketplace\n * manifest permits (ADR-0010/0015). A `marketplace` contains `plugin` only; a\n * `plugin` contains the component types DevAI Hub models. Any other pairing is\n * silently not rendered. This is a rendering convention, not catalog\n * validation (upstream `allowedKinds` is the broad `[\"AiResource\"]`).\n */\nexport function isRenderableContainment(\n parent: ResourceType,\n child: ResourceType,\n): boolean {\n if (parent === 'marketplace') return MARKETPLACE_MEMBER_TYPES.includes(child);\n if (parent === 'plugin') return PLUGIN_MEMBER_TYPES.includes(child);\n return false;\n}\n\n/** Annotation prefix owned by DevAI Hub. */\nexport const DEVAIHUB_ANNOTATION_PREFIX = 'devaihub.io';\n\n/** Comma-separated compatible-framework tokens (non-skill read path, ADR-0003). */\nexport const ANNOTATION_COMPATIBLE_FRAMEWORKS = `${DEVAIHUB_ANNOTATION_PREFIX}/compatible-frameworks`;\n\n/** Optional free-form help text shown on the detail panel. */\nexport const ANNOTATION_HELP = `${DEVAIHUB_ANNOTATION_PREFIX}/help`;\n\n/** Known framework tokens; unknown tokens pass through as-is (ADR-0003). */\nexport const KNOWN_FRAMEWORKS = [\n 'github-copilot',\n 'claude-code',\n 'cursor',\n 'google-gemini',\n 'opencode',\n 'all',\n] as const;\n\nexport type FrameworkToken = (typeof KNOWN_FRAMEWORKS)[number];\n\nconst FRAMEWORK_ALIASES: Record<string, FrameworkToken> = {\n copilot: 'github-copilot',\n claude: 'claude-code',\n gemini: 'google-gemini',\n};\n\n/**\n * Normalise a framework token: trim, lowercase, resolve aliases\n * (`claude` → `claude-code`). Unknown tokens pass through literally.\n */\nexport function normalizeFramework(token: string): string {\n const t = token.trim().toLowerCase();\n return FRAMEWORK_ALIASES[t] ?? t;\n}\n\n/**\n * Narrow a resource's frameworks to a deployment-level allow-list\n * (`devAiHub.frameworks.allowed`). Omitting the config key (`allowed` is\n * `undefined`) permits every framework; an explicit empty list permits none,\n * so a deployment can suppress framework exposure entirely. Both sides are\n * normalised (aliases resolved) before comparison.\n *\n * The `all` wildcard is expanded to the allow-list itself before filtering, so\n * a resource that declares `all` degrades to \"every permitted host\" rather\n * than disappearing entirely.\n */\nexport function filterAllowedFrameworks(\n frameworks: string[],\n allowed: string[] | undefined,\n): string[] {\n if (allowed === undefined) {\n return frameworks;\n }\n const permitted = new Set(\n allowed.map(normalizeFramework).filter(f => f && f !== 'all'),\n );\n const expanded = frameworks.flatMap(f =>\n normalizeFramework(f) === 'all' ? [...permitted] : [normalizeFramework(f)],\n );\n return Array.from(new Set(expanded)).filter(f => permitted.has(f));\n}\n\n/**\n * The minimal structural shape of an AiResource entity that the read helpers\n * need. A real `@backstage/catalog-model` `Entity` satisfies it, but keeping\n * the shape local means `-common` (and therefore the frontend) never depends\n * on catalog packages (architecture.md).\n */\nexport interface AiResourceEntityLike {\n metadata: {\n annotations?: Record<string, string>;\n };\n spec?: {\n type?: unknown;\n /** Native compatible-frameworks field, honoured regardless of `spec.type`. */\n agents?: unknown;\n [key: string]: unknown;\n };\n}\n\n/**\n * Resolve the compatible frameworks of an AiResource (ADR-0003):\n * 1. a non-empty `spec.agents` → the native field, for any `spec.type`;\n * 2. otherwise → the `devaihub.io/compatible-frameworks` annotation\n * (also the fallback when `spec.agents` is empty/absent);\n * 3. otherwise → `[]`.\n * Tokens are normalised; unknown tokens pass through. `spec.agents` is\n * preferred over the annotation because it lives on the entity itself\n * rather than in an annotation namespace we may retire.\n */\nexport function getFrameworks(entity: AiResourceEntityLike): string[] {\n if (Array.isArray(entity.spec?.agents)) {\n const agents = entity.spec.agents\n .filter((a): a is string => typeof a === 'string')\n .map(normalizeFramework)\n .filter(Boolean);\n if (agents.length > 0) {\n return dedupe(agents);\n }\n }\n\n const raw = entity.metadata.annotations?.[ANNOTATION_COMPATIBLE_FRAMEWORKS];\n if (!raw) {\n return [];\n }\n return dedupe(raw.split(',').map(normalizeFramework).filter(Boolean));\n}\n\nfunction dedupe(tokens: string[]): string[] {\n return Array.from(new Set(tokens));\n}\n\n/**\n * Icon identifiers for the type→card registry. `-common` is imported by the\n * backend, so it stays React-free: the frontend maps these identifiers to\n * actual icon components.\n */\nexport type ResourceTypeIcon =\n | 'tools'\n | 'robot'\n | 'flash'\n | 'plug'\n | 'puzzle'\n | 'store';\n\nexport interface ResourceTypeInfo {\n type: ResourceType;\n /** Singular display label, e.g. \"Skill\" (card type caption). */\n label: string;\n /** Plural display label, e.g. \"Skills\" (stat tiles, filters). */\n pluralLabel: string;\n /** Icon identifier resolved to a component by the frontend. */\n icon: ResourceTypeIcon;\n /**\n * Colour role name; the frontend resolves it to the plugin-owned\n * `--devaihub-type-<role>` custom properties (ADR-0008, NOS palette).\n */\n colorRole: ResourceType;\n}\n\n/**\n * The typed type→card registry (ADR-0003/ADR-0008): producer vocabulary and\n * consumer rendering share this single source, so they cannot drift. Adding\n * a type is one entry here plus one CSS token pair in the frontend.\n */\nexport const RESOURCE_TYPE_REGISTRY: Record<ResourceType, ResourceTypeInfo> = {\n skill: {\n type: 'skill',\n label: 'Skill',\n pluralLabel: 'Skills',\n icon: 'tools',\n colorRole: 'skill',\n },\n agent: {\n type: 'agent',\n label: 'Agent',\n pluralLabel: 'Agents',\n icon: 'robot',\n colorRole: 'agent',\n },\n hook: {\n type: 'hook',\n label: 'Hook',\n pluralLabel: 'Hooks',\n icon: 'flash',\n colorRole: 'hook',\n },\n 'mcp-config': {\n type: 'mcp-config',\n label: 'MCP Config',\n pluralLabel: 'MCP Configs',\n icon: 'plug',\n colorRole: 'mcp-config',\n },\n plugin: {\n type: 'plugin',\n label: 'Plugin',\n pluralLabel: 'Plugins',\n icon: 'puzzle',\n colorRole: 'plugin',\n },\n marketplace: {\n type: 'marketplace',\n label: 'Marketplace',\n pluralLabel: 'Marketplaces',\n icon: 'store',\n colorRole: 'marketplace',\n },\n};\n\n/**\n * The flat contract the backend returns to the frontend. The frontend never\n * sees a raw Backstage `Entity` — only this (architecture.md).\n */\nexport interface ResourceSummary {\n entityRef: string;\n name: string;\n title?: string;\n description?: string;\n tags: string[];\n type: ResourceType;\n lifecycle: string;\n owner?: string;\n sourceLocation?: string;\n frameworks: string[];\n /**\n * True when an active deployment allow-list (`devAiHub.frameworks.allowed`)\n * left this resource with no frameworks — either it removed the ones the\n * author declared, or the allow-list itself is empty. Lets the UI tell\n * \"author declared nothing\" (`frameworks: []`, show neutral install\n * defaults) apart from \"the deployment suppressed them\" (hide generated\n * install paths and launchers; Copy/Download still work).\n */\n frameworksRestricted?: boolean;\n /**\n * The framework set that install-path and launcher generation must be\n * scoped to, when it differs from `frameworks`. Only set for a resource\n * that declared no frameworks (which otherwise means \"every host\") under an\n * active allow-list: it holds the allow-list itself, so a bare resource\n * cannot produce launchers for tools the deployment disallowed. Kept\n * separate from `frameworks` so this inferred set never renders as a\n * compatibility badge the author never claimed.\n */\n installFrameworks?: string[];\n version?: string;\n kind: string;\n /**\n * Entity refs this container declares as its contents — a `marketplace`'s\n * `spec.plugins`, a `plugin`'s `spec.skills`; empty for leaf types. Filtered\n * to the caller's visible set and to the child types the container renders\n * (ADR-0015). The frontend resolves each ref against the summaries it holds.\n */\n children: string[];\n /**\n * Entity refs of the containers that declare this resource — the in-memory\n * inverse of `children`, built over the same caller-visible catalog read\n * (ADR-0015). A resource can belong to several containers.\n */\n parents: string[];\n /** Count of `children` (their declared, caller-visible members). */\n childCount?: number;\n helpText?: string;\n annotations: Record<string, string>;\n}\n\nexport interface ResourceListResponse {\n items: ResourceSummary[];\n}\n\n/**\n * The shape of a resource's body (CONTEXT.md: bodies are type-shaped, not\n * uniformly markdown). Drives detail-panel rendering: markdown is rendered,\n * json is pretty-printed and syntax-highlighted (ADR-0014). `plugin` and\n * `marketplace` bodies are the real Claude Code `plugin.json` /\n * `marketplace.json` manifest — not a hand-authored pointer doc, superseding\n * ADR-0010's markdown-only body for `marketplace`.\n */\nexport type BodyShape = 'markdown' | 'json';\n\nexport function getBodyShape(type: ResourceType): BodyShape {\n return type === 'mcp-config' || type === 'plugin' || type === 'marketplace'\n ? 'json'\n : 'markdown';\n}\n\n/**\n * Whether the detail panel shows a \"Content\" section for this type. `plugin`\n * and `marketplace` resources are containers — their body is the raw manifest\n * JSON and their real value is the relationship graph, not the file — so the\n * Content block is suppressed for them and kept for every other type.\n */\nexport function showsContentSection(type: ResourceType): boolean {\n return type !== 'plugin' && type !== 'marketplace';\n}\n\n/**\n * Whether downloading the body delivers the artifact itself. True for every\n * type: `plugin`/`marketplace` bodies are now the real manifest JSON, so a\n * download delivers that file itself rather than a non-actionable pointer\n * doc (ADR-0014, superseding ADR-0009's amendment and ADR-0010 on this\n * point).\n */\nexport function hasDownloadableArtifact(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * Whether copying the body to the clipboard is a meaningful install action.\n * True for every type: `marketplace`'s body is now the real manifest JSON,\n * not an instructions doc, so copying it is no longer a misrepresentation\n * (ADR-0014, superseding ADR-0010 on this point).\n */\nexport function hasCopyableBody(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * How a body reaches its install path.\n *\n * `drop-in` — the body becomes the file (or directory) at `path`, which is\n * the resource's own; writing it touches nothing else.\n *\n * `merge` — `path` is a shared settings file the user already owns, and the\n * body is a fragment to merge into it. The distinction is not cosmetic:\n * following a `merge` target as if it were a `drop-in` overwrites the user's\n * existing configuration, so the UI must never present the two identically.\n */\nexport type InstallMode = 'drop-in' | 'merge';\n\nexport interface ResourceInstallTarget {\n /** Workspace-relative path. */\n path: string;\n mode: InstallMode;\n}\n\n/**\n * The concrete framework tokens a body can be installed into — `all` is a\n * wildcard over these, never a destination itself.\n */\nexport const INSTALLABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'google-gemini',\n 'cursor',\n 'opencode',\n] as const;\n\n/**\n * Convention table: (type, framework) → where the body goes and how.\n * A missing entry means the pair has no filesystem convention — `plugin` and\n * `marketplace` bodies are pointers that install through their framework\n * (ADR-0009, ADR-0010), so they carry install commands rather than paths.\n */\nconst INSTALL_PATHS: Record<\n ResourceType,\n Record<string, { mode: InstallMode; path: (name: string) => string }>\n> = {\n // Every host reads a skill as a self-contained directory. Copilot also\n // picks up `.claude/skills`, but each tool's own path is what we recommend;\n // `.agents/skills` is the cross-tool alias Copilot and Gemini both honour,\n // which makes it the right answer for an unrecognised framework.\n skill: {\n 'claude-code': { mode: 'drop-in', path: name => `.claude/skills/${name}/` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/skills/${name}/`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/skills/${name}/`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/skills/${name}/` },\n opencode: { mode: 'drop-in', path: name => `.opencode/skills/${name}/` },\n default: { mode: 'drop-in', path: name => `.agents/skills/${name}/` },\n },\n agent: {\n 'claude-code': {\n mode: 'drop-in',\n path: name => `.claude/agents/${name}.md`,\n },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/agents/${name}.agent.md`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/agents/${name}.md`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/rules/${name}.mdc` },\n opencode: { mode: 'drop-in', path: name => `.opencode/agents/${name}.md` },\n default: { mode: 'drop-in', path: name => `.ai/agents/${name}.md` },\n },\n // All four hosts have a hook system, but only Copilot gives each hook its\n // own file; the rest register hooks inside a shared settings document.\n // OpenCode has no declarative hook config at all — hooks are exclusively\n // JS/TS plugin modules — so it deliberately carries no entry here, the same\n // way it carries no `default`.\n hook: {\n 'claude-code': { mode: 'merge', path: () => `.claude/settings.json` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/hooks/${name}.json`,\n },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/hooks.json` },\n },\n 'mcp-config': {\n 'claude-code': { mode: 'merge', path: () => `.mcp.json` },\n 'github-copilot': { mode: 'merge', path: () => `.vscode/mcp.json` },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/mcp.json` },\n opencode: { mode: 'merge', path: () => `opencode.json` },\n },\n plugin: {},\n marketplace: {},\n};\n\n/**\n * Where a resource's body installs for one framework, and whether that path\n * is the body's own file or a settings file to merge into. Returns\n * `undefined` when the (type, framework) pair has no filesystem convention.\n */\nexport function getResourceInstallTarget(\n type: ResourceType,\n framework: string,\n name: string,\n): ResourceInstallTarget | undefined {\n const conventions = INSTALL_PATHS[type];\n const entry =\n conventions[normalizeFramework(framework)] ?? conventions.default;\n return entry ? { path: entry.path(name), mode: entry.mode } : undefined;\n}\n\n/**\n * The frameworks to show install paths for. An empty list means the resource\n * declared no compatibility and gets the neutral `default` convention; `all`\n * is a wildcard and expands to every installable host, so it never collapses\n * to a single row — or, for types with no `default`, to no rows at all.\n */\nexport function expandInstallFrameworks(frameworks: string[]): string[] {\n const normalized = dedupe(frameworks.map(normalizeFramework));\n if (normalized.length === 0) {\n return ['default'];\n }\n if (normalized.includes('all')) {\n return [...INSTALLABLE_FRAMEWORKS];\n }\n return normalized;\n}\n\n/*\n * Marketplace journey helpers (ADR-0010). A marketplace installs in two\n * steps — register the catalog with the AI tool, then install plugins from\n * it. Both Claude Code and Copilot CLI read the same\n * `.claude-plugin/marketplace.json` format and address the repo as\n * `owner/repo`; the plugin derives that slug from the mandatory\n * `source-location` (the producer contract requires the body doc to live\n * inside the marketplace repo). No derivation → the UI falls back to the\n * rendered body, which stays canonical.\n */\n\n/** Frameworks with a marketplace-add concept (Cursor/Gemini have none). */\nexport const MARKETPLACE_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n] as const;\n\n/**\n * Narrow a resource's declared frameworks to those a given journey can\n * actually launch. A deep link is a claim about compatibility: offering\n * \"Install in Claude\" for a resource that never declared `claude-code` tells\n * the user something untrue about the resource.\n *\n * `all` and an empty list both mean \"unrestricted\" and expand to every\n * capable host. Anything else is intersected, so a declared framework with no\n * handler (Gemini) — or an unknown token — yields no links rather than\n * silently widening to every host.\n */\nexport function resolveCapableFrameworks(\n declared: string[],\n capable: readonly string[],\n): string[] {\n const normalized = dedupe(declared.map(normalizeFramework));\n if (normalized.length === 0 || normalized.includes('all')) {\n return [...capable];\n }\n return normalized.filter(f => capable.includes(f));\n}\n\n/**\n * Derive the `owner/repo` slug from a marketplace's `source-location`.\n * GitHub URLs only — other hosts return `undefined` (body-only fallback).\n */\nexport function getMarketplaceRepoSlug(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo] = url.pathname.split('/').filter(Boolean);\n if (!owner || !repo) {\n return undefined;\n }\n return `${owner}/${repo.replace(/\\.git$/, '')}`;\n}\n\n/**\n * A one-click launcher for an add command: a custom-scheme URL that opens\n * the host app with the command pre-filled in the prompt box. The host\n * never auto-executes it — the user reviews and presses Enter — and an\n * unregistered scheme is a harmless no-op, so these complement the copy\n * button rather than replace it.\n */\nexport interface ResourceDeepLink {\n /** Host application name; render as \"Add in <label>\" / \"Install in <label>\". */\n label: string;\n href: string;\n}\n\nexport interface MarketplaceAddCommand {\n framework: string;\n command: string;\n deepLinks: ResourceDeepLink[];\n}\n\n/**\n * The copyable marketplace-add command per compatible framework. `all`,\n * unknown-only, or empty framework lists expand to every capable framework;\n * frameworks without a marketplace concept produce no row.\n *\n * Claude Code carries a single deep link on the CLI's documented\n * `claude-cli://open?q=` handler, which opens a Claude Code terminal\n * session with the command pre-filled regardless of the user's editor.\n *\n * Copilot's CLI still has no URI scheme, but Copilot's *other* surface does:\n * VS Code 1.113 ships `vscode://chat-plugin/add-marketplace?ref=`, which\n * accepts a plain or base64 `owner/repo` and always shows a confirmation\n * dialog before registering the marketplace (it also dedupes against the\n * user's existing entries). So the Copilot row keeps its terminal command for\n * CLI users *and* gains editor launchers, mirroring how `mcp-config` already\n * maps `github-copilot` onto the VS Code stable/Insiders pair.\n */\nexport function getMarketplaceAddCommands(\n frameworks: string[],\n repoSlug: string,\n): MarketplaceAddCommand[] {\n const capable = resolveCapableFrameworks(\n frameworks,\n MARKETPLACE_CAPABLE_FRAMEWORKS,\n );\n const commands: MarketplaceAddCommand[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const command = `/plugin marketplace add ${repoSlug}`;\n commands.push({\n framework,\n command,\n deepLinks: [\n {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(command)}`,\n },\n ],\n });\n } else if (framework === 'github-copilot') {\n const ref = encodeURIComponent(repoSlug);\n commands.push({\n framework,\n command: `copilot plugin marketplace add ${repoSlug}`,\n deepLinks: [\n {\n label: 'VS Code',\n href: `vscode://chat-plugin/add-marketplace?ref=${ref}`,\n },\n {\n label: 'VS Code Insiders',\n href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`,\n },\n ],\n });\n }\n }\n return commands;\n}\n\n/** The raw-file URL for a GitHub blob `source-location`; else `undefined`. */\nfunction getRawGithubFileUrl(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo, marker, ref, ...path] = url.pathname\n .split('/')\n .filter(Boolean);\n if (!owner || !repo || marker !== 'blob' || !ref || path.length === 0) {\n return undefined;\n }\n return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path.join(\n '/',\n )}`;\n}\n\n/**\n * A launcher that opens an agent with an install prompt pre-filled. Neither\n * host auto-executes: the user reads the prompt and presses Enter. Claude Code\n * and Cursor are the two tools exposing a generic prompt URI — Copilot's\n * editor has purpose-built routes but no prompt handler, and Gemini has\n * neither (its `gemini.google.com/app?prompt=` link drives the web app, which\n * cannot write to a local workspace).\n *\n * Cursor truncates a deeplink at the first raw `&`, so the prompt must be\n * percent-encoded — `encodeURIComponent` handles that. Cursor also caps the\n * URL at ~8000 characters; these prompts are a URL plus a path, far under it.\n */\nfunction claudePromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(prompt)}`,\n };\n}\n\nfunction cursorPromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(\n prompt,\n )}`,\n };\n}\n\n/**\n * Frameworks with a one-click agent-install handler (Gemini has none).\n * OpenCode's Desktop app does register a real `opencode://` scheme, but every\n * route (`open-project`, `new-session`) requires an absolute local\n * `directory` param a web page cannot supply, so it's excluded here too.\n */\nexport const AGENT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/** Hosts reachable by a generic prompt URI — the only route for skill/hook. */\nexport const PROMPT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'cursor',\n] as const;\n\n/**\n * The plain http(s) URL behind a `source-location`. Unlike\n * `getRawGithubFileUrl` this accepts **directory (`tree`) URLs**, which is what\n * a multi-file `skill` actually carries — a raw-file URL would resolve to\n * `undefined` for exactly the type that most needs a launcher.\n */\nfunction getSourceUrl(sourceLocation: string | undefined): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n try {\n const url = new URL(sourceLocation.replace(/^url:/, ''));\n return url.protocol === 'http:' || url.protocol === 'https:'\n ? url.toString()\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * One-click launchers for the types with no purpose-built install route —\n * `skill` and `hook`. Both ride the generic prompt handlers, so the prompt\n * names the *host's own* install path and respects its install mode: a `merge`\n * target is asked to be merged into, never overwritten, so a launcher can\n * never cost the user their existing settings.\n *\n * Returns `[]` when the source URL cannot be derived, or for hosts with no\n * prompt route (Copilot, Gemini) — the path list and copy/download remain.\n */\nexport function getPromptInstallLinks(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n return getInstallSteps(type, sourceLocation, name, frameworks)\n .map(step => step.link)\n .filter((link): link is ResourceDeepLink => !!link);\n}\n\n/**\n * Everything a single host needs to install a resource: where the body goes,\n * the prompt that puts it there, and a launcher when the host has a URI to\n * launch.\n *\n * `link` is absent for hosts with no prompt route — Copilot and Gemini. That\n * absence is why the step carries `prompt` as text: a host without a URI is\n * not a host without an install path, and the same instruction that a launcher\n * would pre-fill can be pasted into that agent by hand. Every declared\n * framework therefore gets something actionable, not a bare path.\n */\nexport interface ResourceInstallStep {\n framework: string;\n target: ResourceInstallTarget;\n /** The install instruction, ready to run in that host's agent. */\n prompt: string;\n /** One-click launcher, where the host exposes a generic prompt URI. */\n link?: ResourceDeepLink;\n}\n\nexport function getInstallSteps(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceInstallStep[] {\n const sourceUrl = getSourceUrl(sourceLocation);\n if (!sourceUrl) {\n return [];\n }\n const label = RESOURCE_TYPE_REGISTRY[type].label.toLowerCase();\n const steps: ResourceInstallStep[] = [];\n for (const framework of expandInstallFrameworks(frameworks)) {\n const target = getResourceInstallTarget(type, framework, name);\n if (!target) {\n continue;\n }\n const prompt =\n target.mode === 'merge'\n ? `Install the \"${name}\" ${label}: fetch ${sourceUrl} and merge it into ${target.path}, keeping my existing settings intact.`\n : `Install the \"${name}\" ${label}: fetch ${sourceUrl} and save it to ${target.path}.`;\n let link: ResourceDeepLink | undefined;\n if (framework === 'claude-code') {\n link = claudePromptLink(prompt);\n } else if (framework === 'cursor') {\n link = cursorPromptLink(prompt);\n }\n steps.push({ framework, target, prompt, link });\n }\n return steps;\n}\n\n/** Frameworks with a one-click MCP-install handler (Gemini has none). */\nexport const MCP_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/**\n * One-click install links for an `agent` resource, restricted to the\n * resource's own compatible frameworks. VS Code stable and Insiders use the\n * native `chat-agent/install?url=` handler (the mechanism behind\n * awesome-copilot's Install buttons) and are Copilot's hosts, so they follow\n * `github-copilot`: VS Code downloads the file at `url` and asks the user\n * where to save it. Claude Code uses the `claude-cli://open?q=` handler with\n * an install prompt pre-filled to the convention path — reviewed and sent by\n * the user, never auto-executed. All derive from the GitHub blob URL in\n * `source-location`; anything else returns `[]` (copy/download fallback).\n */\nexport function getAgentInstallLinks(\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n const rawUrl = getRawGithubFileUrl(sourceLocation);\n if (!rawUrl) {\n return [];\n }\n const capable = resolveCapableFrameworks(\n frameworks,\n AGENT_LINK_CAPABLE_FRAMEWORKS,\n );\n const encodedRawUrl = encodeURIComponent(rawUrl);\n const installPrompt = (framework: string) => {\n const target = getResourceInstallTarget('agent', framework, name);\n return `Install this agent: fetch ${rawUrl} and save it to ${target?.path}`;\n };\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n links.push(claudePromptLink(installPrompt('claude-code')));\n } else if (framework === 'cursor') {\n links.push(cursorPromptLink(installPrompt('cursor')));\n } else if (framework === 'github-copilot') {\n links.push({\n label: 'VS Code',\n href: `vscode:chat-agent/install?url=${encodedRawUrl}`,\n });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`,\n });\n }\n }\n return links;\n}\n\n/**\n * One-click install links for an `mcp-config` resource, derived from its body —\n * the canonical `.mcp.json` snippet (spec §3.4). VS Code carries a native\n * `vscode:mcp/install?{json}` handler (Insiders scheme twin), Cursor a\n * documented `cursor://anysphere.cursor-deeplink/mcp/install` one, and\n * Claude Code gets a `claude-cli://open?q=` prompt around\n * `claude mcp add-json` — pre-filled, reviewed, never auto-executed.\n * Hosts follow the resource's own compatible frameworks: `all`/empty expand\n * to every capable host, anything else is intersected, so an mcp-config\n * declaring only Gemini (no handler) gets no links rather than every host's.\n * A missing, unparseable, or multi-server body returns `[]` (copy fallback) —\n * the body stays canonical, links are conveniences derived from it.\n */\nexport function getMcpInstallLinks(\n frameworks: string[],\n resourceName: string,\n bodyContent: string | undefined,\n): ResourceDeepLink[] {\n if (!bodyContent) {\n return [];\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(bodyContent);\n } catch {\n return [];\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return [];\n }\n\n let serverName = resourceName;\n let config: Record<string, unknown>;\n const servers = (parsed as { mcpServers?: unknown }).mcpServers;\n if (servers !== undefined) {\n if (typeof servers !== 'object' || servers === null) {\n return [];\n }\n const entries = Object.entries(servers as Record<string, unknown>);\n if (\n entries.length !== 1 ||\n typeof entries[0][1] !== 'object' ||\n entries[0][1] === null\n ) {\n return [];\n }\n serverName = entries[0][0];\n config = entries[0][1] as Record<string, unknown>;\n } else if ('command' in parsed || 'url' in parsed) {\n config = parsed as Record<string, unknown>;\n } else {\n return [];\n }\n\n const capable = resolveCapableFrameworks(\n frameworks,\n MCP_LINK_CAPABLE_FRAMEWORKS,\n );\n\n const configJson = JSON.stringify(config);\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const prompt = encodeURIComponent(\n `Install this MCP config by running: claude mcp add-json ${serverName} '${configJson}'`,\n );\n links.push({ label: 'Claude', href: `claude-cli://open?q=${prompt}` });\n } else if (framework === 'github-copilot') {\n const vsConfig = encodeURIComponent(\n JSON.stringify({ name: serverName, ...config }),\n );\n links.push({ label: 'VS Code', href: `vscode:mcp/install?${vsConfig}` });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:mcp/install?${vsConfig}`,\n });\n } else if (framework === 'cursor') {\n try {\n links.push({\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(\n serverName,\n )}&config=${btoa(configJson)}`,\n });\n } catch {\n // btoa rejects non-Latin1 config content — skip the Cursor link.\n }\n }\n }\n return links;\n}\n\n/**\n * Step two of the journey: how to install a plugin from the marketplace.\n * `marketplaceName` is the entity name, which the producer contract requires\n * to equal the `name` in `marketplace.json`.\n */\nexport function getMarketplaceInstallTemplate(marketplaceName: string): string {\n return `/plugin install <plugin>@${marketplaceName}`;\n}\n\n/**\n * The `.claude/settings.json` snippet a team commits so collaborators are\n * prompted to install the marketplace automatically (Claude Code).\n */\nexport function getMarketplaceTeamSnippet(\n marketplaceName: string,\n repoSlug: string,\n): string {\n return JSON.stringify(\n {\n extraKnownMarketplaces: {\n [marketplaceName]: { source: { source: 'github', repo: repoSlug } },\n },\n },\n null,\n 2,\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resources.esm.js","sources":["../src/resources.ts"],"sourcesContent":["/**\n * The v2 read contract: AiResource vocabulary, the flat ResourceSummary\n * returned by the backend, and the shared framework resolver (ADR-0003).\n *\n * The legacy asset-model types in `types.ts` coexist with this module until\n * the legacy silo is deleted (slice 8, issue #34).\n */\n\n/** The six canonical `spec.type` values of an AiResource (ADR-0003, ADR-0010). */\nexport const RESOURCE_TYPES = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n 'plugin',\n 'marketplace',\n] as const;\n\nexport type ResourceType = (typeof RESOURCE_TYPES)[number];\n\nexport function isResourceType(value: unknown): value is ResourceType {\n return (\n typeof value === 'string' &&\n (RESOURCE_TYPES as readonly string[]).includes(value)\n );\n}\n\n/**\n * The AiResource types a `plugin` may contain — restricted to the component\n * kinds a real Claude Code / GitHub Copilot plugin manifest allows: `skills`,\n * `agents`, `hooks` and `mcpServers` (schemastore `claude-code-plugin` /\n * `claude-code-marketplace.json`). A plugin's other manifest components\n * (`commands`, `lspServers`, `outputStyles`, `themes`, `channels`, `monitors`)\n * have no AiResource type, so they are never members here; other plugins are\n * `dependencies`, not containment.\n */\nexport const PLUGIN_MEMBER_TYPES: readonly ResourceType[] = [\n 'skill',\n 'agent',\n 'hook',\n 'mcp-config',\n];\n\n/**\n * The AiResource types a `marketplace` may contain: `plugin` only. A real\n * `.claude-plugin/marketplace.json` `plugins` array lists plugins and nothing\n * else (required by the schema Claude Code and Copilot CLI both read).\n */\nexport const MARKETPLACE_MEMBER_TYPES: readonly ResourceType[] = ['plugin'];\n\n/**\n * Whether a container of `parent` type renders a child of `child` type as a\n * member — restricted to what a real Claude/Copilot plugin or marketplace\n * manifest permits (ADR-0010/0015). A `marketplace` contains `plugin` only; a\n * `plugin` contains the component types DevAI Hub models. Any other pairing is\n * silently not rendered. This is a rendering convention, not catalog\n * validation (upstream `allowedKinds` is the broad `[\"AiResource\"]`).\n */\nexport function isRenderableContainment(\n parent: ResourceType,\n child: ResourceType,\n): boolean {\n if (parent === 'marketplace') return MARKETPLACE_MEMBER_TYPES.includes(child);\n if (parent === 'plugin') return PLUGIN_MEMBER_TYPES.includes(child);\n return false;\n}\n\n/** Annotation prefix owned by DevAI Hub. */\nexport const DEVAIHUB_ANNOTATION_PREFIX = 'devaihub.io';\n\n/** Comma-separated compatible-framework tokens (non-skill read path, ADR-0003). */\nexport const ANNOTATION_COMPATIBLE_FRAMEWORKS = `${DEVAIHUB_ANNOTATION_PREFIX}/compatible-frameworks`;\n\n/** Optional free-form help text shown on the detail panel. */\nexport const ANNOTATION_HELP = `${DEVAIHUB_ANNOTATION_PREFIX}/help`;\n\n/** Known framework tokens; unknown tokens pass through as-is (ADR-0003). */\nexport const KNOWN_FRAMEWORKS = [\n 'github-copilot',\n 'claude-code',\n 'cursor',\n 'google-gemini',\n 'opencode',\n 'all',\n] as const;\n\nexport type FrameworkToken = (typeof KNOWN_FRAMEWORKS)[number];\n\nconst FRAMEWORK_ALIASES: Record<string, FrameworkToken> = {\n copilot: 'github-copilot',\n claude: 'claude-code',\n gemini: 'google-gemini',\n};\n\n/**\n * Normalise a framework token: trim, lowercase, resolve aliases\n * (`claude` → `claude-code`). Unknown tokens pass through literally.\n */\nexport function normalizeFramework(token: string): string {\n const t = token.trim().toLowerCase();\n return FRAMEWORK_ALIASES[t] ?? t;\n}\n\n/**\n * Narrow a resource's frameworks to a deployment-level allow-list\n * (`devAiHub.frameworks.allowed`). Omitting the config key (`allowed` is\n * `undefined`) permits every framework; an explicit empty list permits none,\n * so a deployment can suppress framework exposure entirely. Both sides are\n * normalised (aliases resolved) before comparison.\n *\n * The `all` wildcard is expanded to the allow-list itself before filtering, so\n * a resource that declares `all` degrades to \"every permitted host\" rather\n * than disappearing entirely.\n */\nexport function filterAllowedFrameworks(\n frameworks: string[],\n allowed: string[] | undefined,\n): string[] {\n if (allowed === undefined) {\n return frameworks;\n }\n const permitted = new Set(\n allowed.map(normalizeFramework).filter(f => f && f !== 'all'),\n );\n const expanded = frameworks.flatMap(f =>\n normalizeFramework(f) === 'all' ? [...permitted] : [normalizeFramework(f)],\n );\n return Array.from(new Set(expanded)).filter(f => permitted.has(f));\n}\n\n/**\n * The minimal structural shape of an AiResource entity that the read helpers\n * need. A real `@backstage/catalog-model` `Entity` satisfies it, but keeping\n * the shape local means `-common` (and therefore the frontend) never depends\n * on catalog packages (architecture.md).\n */\nexport interface AiResourceEntityLike {\n metadata: {\n annotations?: Record<string, string>;\n };\n spec?: {\n type?: unknown;\n /** Native compatible-frameworks field, honoured regardless of `spec.type`. */\n agents?: unknown;\n [key: string]: unknown;\n };\n}\n\n/**\n * Resolve the compatible frameworks of an AiResource (ADR-0003):\n * 1. a non-empty `spec.agents` → the native field, for any `spec.type`;\n * 2. otherwise → the `devaihub.io/compatible-frameworks` annotation\n * (also the fallback when `spec.agents` is empty/absent);\n * 3. otherwise → `[]`.\n * Tokens are normalised; unknown tokens pass through. `spec.agents` is\n * preferred over the annotation because it lives on the entity itself\n * rather than in an annotation namespace we may retire.\n */\nexport function getFrameworks(entity: AiResourceEntityLike): string[] {\n if (Array.isArray(entity.spec?.agents)) {\n const agents = entity.spec.agents\n .filter((a): a is string => typeof a === 'string')\n .map(normalizeFramework)\n .filter(Boolean);\n if (agents.length > 0) {\n return dedupe(agents);\n }\n }\n\n const raw = entity.metadata.annotations?.[ANNOTATION_COMPATIBLE_FRAMEWORKS];\n if (!raw) {\n return [];\n }\n return dedupe(raw.split(',').map(normalizeFramework).filter(Boolean));\n}\n\nfunction dedupe(tokens: string[]): string[] {\n return Array.from(new Set(tokens));\n}\n\n/**\n * Icon identifiers for the type→card registry. `-common` is imported by the\n * backend, so it stays React-free: the frontend maps these identifiers to\n * actual icon components.\n */\nexport type ResourceTypeIcon =\n | 'tools'\n | 'robot'\n | 'flash'\n | 'plug'\n | 'puzzle'\n | 'store';\n\nexport interface ResourceTypeInfo {\n type: ResourceType;\n /** Singular display label, e.g. \"Skill\" (card type caption). */\n label: string;\n /** Plural display label, e.g. \"Skills\" (stat tiles, filters). */\n pluralLabel: string;\n /** Icon identifier resolved to a component by the frontend. */\n icon: ResourceTypeIcon;\n /**\n * Colour role name; the frontend resolves it to the plugin-owned\n * `--devaihub-type-<role>` custom properties (ADR-0008, NOS palette).\n */\n colorRole: ResourceType;\n}\n\n/**\n * The typed type→card registry (ADR-0003/ADR-0008): producer vocabulary and\n * consumer rendering share this single source, so they cannot drift. Adding\n * a type is one entry here plus one CSS token pair in the frontend.\n */\nexport const RESOURCE_TYPE_REGISTRY: Record<ResourceType, ResourceTypeInfo> = {\n skill: {\n type: 'skill',\n label: 'Skill',\n pluralLabel: 'Skills',\n icon: 'tools',\n colorRole: 'skill',\n },\n agent: {\n type: 'agent',\n label: 'Agent',\n pluralLabel: 'Agents',\n icon: 'robot',\n colorRole: 'agent',\n },\n hook: {\n type: 'hook',\n label: 'Hook',\n pluralLabel: 'Hooks',\n icon: 'flash',\n colorRole: 'hook',\n },\n 'mcp-config': {\n type: 'mcp-config',\n label: 'MCP Config',\n pluralLabel: 'MCP Configs',\n icon: 'plug',\n colorRole: 'mcp-config',\n },\n plugin: {\n type: 'plugin',\n label: 'Plugin',\n pluralLabel: 'Plugins',\n icon: 'puzzle',\n colorRole: 'plugin',\n },\n marketplace: {\n type: 'marketplace',\n label: 'Marketplace',\n pluralLabel: 'Marketplaces',\n icon: 'store',\n colorRole: 'marketplace',\n },\n};\n\n/**\n * The flat contract the backend returns to the frontend. The frontend never\n * sees a raw Backstage `Entity` — only this (architecture.md).\n */\nexport interface ResourceSummary {\n entityRef: string;\n name: string;\n title?: string;\n description?: string;\n tags: string[];\n type: ResourceType;\n lifecycle: string;\n owner?: string;\n sourceLocation?: string;\n frameworks: string[];\n /**\n * True when an active deployment allow-list (`devAiHub.frameworks.allowed`)\n * left this resource with no frameworks — either it removed the ones the\n * author declared, or the allow-list itself is empty. Lets the UI tell\n * \"author declared nothing\" (`frameworks: []`, show neutral install\n * defaults) apart from \"the deployment suppressed them\" (hide generated\n * install paths and launchers; Copy/Download still work).\n */\n frameworksRestricted?: boolean;\n /**\n * The framework set that install-path and launcher generation must be\n * scoped to, when it differs from `frameworks`. Only set for a resource\n * that declared no frameworks (which otherwise means \"every host\") under an\n * active allow-list: it holds the allow-list itself, so a bare resource\n * cannot produce launchers for tools the deployment disallowed. Kept\n * separate from `frameworks` so this inferred set never renders as a\n * compatibility badge the author never claimed.\n */\n installFrameworks?: string[];\n version?: string;\n kind: string;\n /**\n * Entity refs this container declares as its contents — a `marketplace`'s\n * `spec.plugins`, a `plugin`'s `spec.skills`; empty for leaf types. Filtered\n * to the caller's visible set and to the child types the container renders\n * (ADR-0015). The frontend resolves each ref against the summaries it holds.\n */\n children: string[];\n /**\n * Entity refs of the containers that declare this resource — the in-memory\n * inverse of `children`, built over the same caller-visible catalog read\n * (ADR-0015). A resource can belong to several containers.\n */\n parents: string[];\n /** Count of `children` (their declared, caller-visible members). */\n childCount?: number;\n helpText?: string;\n annotations: Record<string, string>;\n}\n\nexport interface ResourceListResponse {\n items: ResourceSummary[];\n}\n\n/**\n * The shape of a resource's body (CONTEXT.md: bodies are type-shaped, not\n * uniformly markdown). Drives detail-panel rendering: markdown is rendered,\n * json is pretty-printed and syntax-highlighted (ADR-0014). `plugin` and\n * `marketplace` bodies are the real Claude Code `plugin.json` /\n * `marketplace.json` manifest — not a hand-authored pointer doc, superseding\n * ADR-0010's markdown-only body for `marketplace`.\n */\nexport type BodyShape = 'markdown' | 'json';\n\nexport function getBodyShape(type: ResourceType): BodyShape {\n return type === 'mcp-config' || type === 'plugin' || type === 'marketplace'\n ? 'json'\n : 'markdown';\n}\n\n/**\n * Whether the detail panel shows a \"Content\" section for this type.\n *\n * Suppressed for exactly the JSON-bodied types (`getBodyShape` === 'json'):\n * `plugin` and `marketplace` are containers whose value is the relationship\n * graph rather than the manifest, and an `mcp-config`'s body is a settings\n * fragment to merge — in all three the raw JSON buries the panel and is\n * better reached through Copy, Download or the install dialog, which is\n * where it is actionable. The markdown types keep their rendered body.\n */\nexport function showsContentSection(type: ResourceType): boolean {\n return getBodyShape(type) !== 'json';\n}\n\n/**\n * Whether downloading the body delivers the artifact itself. True for every\n * type: `plugin`/`marketplace` bodies are now the real manifest JSON, so a\n * download delivers that file itself rather than a non-actionable pointer\n * doc (ADR-0014, superseding ADR-0009's amendment and ADR-0010 on this\n * point).\n */\nexport function hasDownloadableArtifact(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * Whether copying the body to the clipboard is a meaningful install action.\n * True for every type: `marketplace`'s body is now the real manifest JSON,\n * not an instructions doc, so copying it is no longer a misrepresentation\n * (ADR-0014, superseding ADR-0010 on this point).\n */\nexport function hasCopyableBody(_type: ResourceType): boolean {\n return true;\n}\n\n/**\n * How a body reaches its install path.\n *\n * `drop-in` — the body becomes the file (or directory) at `path`, which is\n * the resource's own; writing it touches nothing else.\n *\n * `merge` — `path` is a shared settings file the user already owns, and the\n * body is a fragment to merge into it. The distinction is not cosmetic:\n * following a `merge` target as if it were a `drop-in` overwrites the user's\n * existing configuration, so the UI must never present the two identically.\n */\nexport type InstallMode = 'drop-in' | 'merge';\n\nexport interface ResourceInstallTarget {\n /** Workspace-relative path. */\n path: string;\n mode: InstallMode;\n}\n\n/**\n * The concrete framework tokens a body can be installed into — `all` is a\n * wildcard over these, never a destination itself.\n */\nexport const INSTALLABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'google-gemini',\n 'cursor',\n 'opencode',\n] as const;\n\n/**\n * Convention table: (type, framework) → where the body goes and how.\n * A missing entry means the pair has no filesystem convention — `plugin` and\n * `marketplace` bodies are pointers that install through their framework\n * (ADR-0009, ADR-0010), so they carry install commands rather than paths.\n */\nconst INSTALL_PATHS: Record<\n ResourceType,\n Record<string, { mode: InstallMode; path: (name: string) => string }>\n> = {\n // Every host reads a skill as a self-contained directory. Copilot also\n // picks up `.claude/skills`, but each tool's own path is what we recommend;\n // `.agents/skills` is the cross-tool alias Copilot and Gemini both honour,\n // which makes it the right answer for an unrecognised framework.\n skill: {\n 'claude-code': { mode: 'drop-in', path: name => `.claude/skills/${name}/` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/skills/${name}/`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/skills/${name}/`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/skills/${name}/` },\n opencode: { mode: 'drop-in', path: name => `.opencode/skills/${name}/` },\n default: { mode: 'drop-in', path: name => `.agents/skills/${name}/` },\n },\n agent: {\n 'claude-code': {\n mode: 'drop-in',\n path: name => `.claude/agents/${name}.md`,\n },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/agents/${name}.agent.md`,\n },\n 'google-gemini': {\n mode: 'drop-in',\n path: name => `.gemini/agents/${name}.md`,\n },\n cursor: { mode: 'drop-in', path: name => `.cursor/rules/${name}.mdc` },\n opencode: { mode: 'drop-in', path: name => `.opencode/agents/${name}.md` },\n default: { mode: 'drop-in', path: name => `.ai/agents/${name}.md` },\n },\n // All four hosts have a hook system, but only Copilot gives each hook its\n // own file; the rest register hooks inside a shared settings document.\n // OpenCode has no declarative hook config at all — hooks are exclusively\n // JS/TS plugin modules — so it deliberately carries no entry here, the same\n // way it carries no `default`.\n hook: {\n 'claude-code': { mode: 'merge', path: () => `.claude/settings.json` },\n 'github-copilot': {\n mode: 'drop-in',\n path: name => `.github/hooks/${name}.json`,\n },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/hooks.json` },\n },\n 'mcp-config': {\n 'claude-code': { mode: 'merge', path: () => `.mcp.json` },\n 'github-copilot': { mode: 'merge', path: () => `.vscode/mcp.json` },\n 'google-gemini': { mode: 'merge', path: () => `.gemini/settings.json` },\n cursor: { mode: 'merge', path: () => `.cursor/mcp.json` },\n opencode: { mode: 'merge', path: () => `opencode.json` },\n },\n plugin: {},\n marketplace: {},\n};\n\n/**\n * Where a resource's body installs for one framework, and whether that path\n * is the body's own file or a settings file to merge into. Returns\n * `undefined` when the (type, framework) pair has no filesystem convention.\n */\nexport function getResourceInstallTarget(\n type: ResourceType,\n framework: string,\n name: string,\n): ResourceInstallTarget | undefined {\n const conventions = INSTALL_PATHS[type];\n const entry =\n conventions[normalizeFramework(framework)] ?? conventions.default;\n return entry ? { path: entry.path(name), mode: entry.mode } : undefined;\n}\n\n/**\n * The frameworks to show install paths for. An empty list means the resource\n * declared no compatibility and gets the neutral `default` convention; `all`\n * is a wildcard and expands to every installable host, so it never collapses\n * to a single row — or, for types with no `default`, to no rows at all.\n */\nexport function expandInstallFrameworks(frameworks: string[]): string[] {\n const normalized = dedupe(frameworks.map(normalizeFramework));\n if (normalized.length === 0) {\n return ['default'];\n }\n if (normalized.includes('all')) {\n return [...INSTALLABLE_FRAMEWORKS];\n }\n return normalized;\n}\n\n/*\n * Marketplace journey helpers (ADR-0010). A marketplace installs in two\n * steps — register the catalog with the AI tool, then install plugins from\n * it. Both Claude Code and Copilot CLI read the same\n * `.claude-plugin/marketplace.json` format and address the repo as\n * `owner/repo`; the plugin derives that slug from the mandatory\n * `source-location` (the producer contract requires the body doc to live\n * inside the marketplace repo). No derivation → the UI falls back to the\n * rendered body, which stays canonical.\n */\n\n/** Frameworks with a marketplace-add concept (Cursor/Gemini have none). */\nexport const MARKETPLACE_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n] as const;\n\n/**\n * Narrow a resource's declared frameworks to those a given journey can\n * actually launch. A deep link is a claim about compatibility: offering\n * \"Install in Claude\" for a resource that never declared `claude-code` tells\n * the user something untrue about the resource.\n *\n * `all` and an empty list both mean \"unrestricted\" and expand to every\n * capable host. Anything else is intersected, so a declared framework with no\n * handler (Gemini) — or an unknown token — yields no links rather than\n * silently widening to every host.\n */\nexport function resolveCapableFrameworks(\n declared: string[],\n capable: readonly string[],\n): string[] {\n const normalized = dedupe(declared.map(normalizeFramework));\n if (normalized.length === 0 || normalized.includes('all')) {\n return [...capable];\n }\n return normalized.filter(f => capable.includes(f));\n}\n\n/**\n * Derive the `owner/repo` slug from a marketplace's `source-location`.\n * GitHub URLs only — other hosts return `undefined` (body-only fallback).\n */\nexport function getMarketplaceRepoSlug(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo] = url.pathname.split('/').filter(Boolean);\n if (!owner || !repo) {\n return undefined;\n }\n return `${owner}/${repo.replace(/\\.git$/, '')}`;\n}\n\n/**\n * A one-click launcher for an add command: a custom-scheme URL that opens\n * the host app with the command pre-filled in the prompt box. The host\n * never auto-executes it — the user reviews and presses Enter — and an\n * unregistered scheme is a harmless no-op, so these complement the copy\n * button rather than replace it.\n */\nexport interface ResourceDeepLink {\n /** Host application name; render as \"Add in <label>\" / \"Install in <label>\". */\n label: string;\n href: string;\n /**\n * The framework token this launcher installs into, for the `tool` column on\n * the `install` event it records (ADR-0011). Not 1:1 with `label`:\n * `github-copilot` offers both \"VS Code\" and \"VS Code Insiders\".\n *\n * Optional only to keep this published interface additive — every link the\n * generators below build sets it, which `resources.test.ts` pins. A caller\n * reading a generated link can rely on it; one constructing its own shape\n * is not forced to supply it.\n */\n framework?: string;\n}\n\nexport interface MarketplaceAddCommand {\n framework: string;\n command: string;\n deepLinks: ResourceDeepLink[];\n}\n\n/**\n * The copyable marketplace-add command per compatible framework. `all`,\n * unknown-only, or empty framework lists expand to every capable framework;\n * frameworks without a marketplace concept produce no row.\n *\n * Claude Code carries a single deep link on the CLI's documented\n * `claude-cli://open?q=` handler, which opens a Claude Code terminal\n * session with the command pre-filled regardless of the user's editor.\n *\n * Copilot's CLI still has no URI scheme, but Copilot's *other* surface does:\n * VS Code 1.113 ships `vscode://chat-plugin/add-marketplace?ref=`, which\n * accepts a plain or base64 `owner/repo` and always shows a confirmation\n * dialog before registering the marketplace (it also dedupes against the\n * user's existing entries). So the Copilot row keeps its terminal command for\n * CLI users *and* gains editor launchers, mirroring how `mcp-config` already\n * maps `github-copilot` onto the VS Code stable/Insiders pair.\n */\nexport function getMarketplaceAddCommands(\n frameworks: string[],\n repoSlug: string,\n): MarketplaceAddCommand[] {\n const capable = resolveCapableFrameworks(\n frameworks,\n MARKETPLACE_CAPABLE_FRAMEWORKS,\n );\n const commands: MarketplaceAddCommand[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const command = `/plugin marketplace add ${repoSlug}`;\n commands.push({\n framework,\n command,\n deepLinks: [\n {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(command)}`,\n framework,\n },\n ],\n });\n } else if (framework === 'github-copilot') {\n const ref = encodeURIComponent(repoSlug);\n commands.push({\n framework,\n command: `copilot plugin marketplace add ${repoSlug}`,\n deepLinks: [\n {\n label: 'VS Code',\n href: `vscode://chat-plugin/add-marketplace?ref=${ref}`,\n framework,\n },\n {\n label: 'VS Code Insiders',\n href: `vscode-insiders://chat-plugin/add-marketplace?ref=${ref}`,\n framework,\n },\n ],\n });\n }\n }\n return commands;\n}\n\n/** The raw-file URL for a GitHub blob `source-location`; else `undefined`. */\nfunction getRawGithubFileUrl(\n sourceLocation: string | undefined,\n): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n let url: URL;\n try {\n url = new URL(sourceLocation.replace(/^url:/, ''));\n } catch {\n return undefined;\n }\n if (url.hostname !== 'github.com') {\n return undefined;\n }\n const [owner, repo, marker, ref, ...path] = url.pathname\n .split('/')\n .filter(Boolean);\n if (!owner || !repo || marker !== 'blob' || !ref || path.length === 0) {\n return undefined;\n }\n return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path.join(\n '/',\n )}`;\n}\n\n/**\n * A launcher that opens an agent with an install prompt pre-filled. Neither\n * host auto-executes: the user reads the prompt and presses Enter. Claude Code\n * and Cursor are the two tools exposing a generic prompt URI — Copilot's\n * editor has purpose-built routes but no prompt handler, and Gemini has\n * neither (its `gemini.google.com/app?prompt=` link drives the web app, which\n * cannot write to a local workspace).\n *\n * Cursor truncates a deeplink at the first raw `&`, so the prompt must be\n * percent-encoded — `encodeURIComponent` handles that. Cursor also caps the\n * URL at ~8000 characters; these prompts are a URL plus a path, far under it.\n */\nfunction claudePromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Claude',\n href: `claude-cli://open?q=${encodeURIComponent(prompt)}`,\n framework: 'claude-code',\n };\n}\n\nfunction cursorPromptLink(prompt: string): ResourceDeepLink {\n return {\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(\n prompt,\n )}`,\n framework: 'cursor',\n };\n}\n\n/**\n * Frameworks with a one-click agent-install handler (Gemini has none).\n * OpenCode's Desktop app does register a real `opencode://` scheme, but every\n * route (`open-project`, `new-session`) requires an absolute local\n * `directory` param a web page cannot supply, so it's excluded here too.\n */\nexport const AGENT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/** Hosts reachable by a generic prompt URI — the only route for skill/hook. */\nexport const PROMPT_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'cursor',\n] as const;\n\n/**\n * The plain http(s) URL behind a `source-location`. Unlike\n * `getRawGithubFileUrl` this accepts **directory (`tree`) URLs**, which is what\n * a multi-file `skill` actually carries — a raw-file URL would resolve to\n * `undefined` for exactly the type that most needs a launcher.\n */\nfunction getSourceUrl(sourceLocation: string | undefined): string | undefined {\n if (!sourceLocation) {\n return undefined;\n }\n try {\n const url = new URL(sourceLocation.replace(/^url:/, ''));\n return url.protocol === 'http:' || url.protocol === 'https:'\n ? url.toString()\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * One-click launchers for the types with no purpose-built install route —\n * `skill` and `hook`. Both ride the generic prompt handlers, so the prompt\n * names the *host's own* install path and respects its install mode: a `merge`\n * target is asked to be merged into, never overwritten, so a launcher can\n * never cost the user their existing settings.\n *\n * Returns `[]` when the source URL cannot be derived, or for hosts with no\n * prompt route (Copilot, Gemini) — the path list and copy/download remain.\n */\nexport function getPromptInstallLinks(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n return getInstallSteps(type, sourceLocation, name, frameworks)\n .map(step => step.link)\n .filter((link): link is ResourceDeepLink => !!link);\n}\n\n/**\n * Everything a single host needs to install a resource: where the body goes,\n * the prompt that puts it there, and a launcher when the host has a URI to\n * launch.\n *\n * `link` is absent for hosts with no prompt route — Copilot and Gemini. That\n * absence is why the step carries `prompt` as text: a host without a URI is\n * not a host without an install path, and the same instruction that a launcher\n * would pre-fill can be pasted into that agent by hand. Every declared\n * framework therefore gets something actionable, not a bare path.\n */\nexport interface ResourceInstallStep {\n framework: string;\n target: ResourceInstallTarget;\n /** The install instruction, ready to run in that host's agent. */\n prompt: string;\n /** One-click launcher, where the host exposes a generic prompt URI. */\n link?: ResourceDeepLink;\n}\n\nexport function getInstallSteps(\n type: ResourceType,\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceInstallStep[] {\n const sourceUrl = getSourceUrl(sourceLocation);\n if (!sourceUrl) {\n return [];\n }\n const label = RESOURCE_TYPE_REGISTRY[type].label.toLowerCase();\n const steps: ResourceInstallStep[] = [];\n for (const framework of expandInstallFrameworks(frameworks)) {\n const target = getResourceInstallTarget(type, framework, name);\n if (!target) {\n continue;\n }\n const prompt =\n target.mode === 'merge'\n ? `Install the \"${name}\" ${label}: fetch ${sourceUrl} and merge it into ${target.path}, keeping my existing settings intact.`\n : `Install the \"${name}\" ${label}: fetch ${sourceUrl} and save it to ${target.path}.`;\n let link: ResourceDeepLink | undefined;\n if (framework === 'claude-code') {\n link = claudePromptLink(prompt);\n } else if (framework === 'cursor') {\n link = cursorPromptLink(prompt);\n }\n steps.push({ framework, target, prompt, link });\n }\n return steps;\n}\n\n/** Frameworks with a one-click MCP-install handler (Gemini has none). */\nexport const MCP_LINK_CAPABLE_FRAMEWORKS = [\n 'claude-code',\n 'github-copilot',\n 'cursor',\n] as const;\n\n/**\n * One-click install links for an `agent` resource, restricted to the\n * resource's own compatible frameworks. VS Code stable and Insiders use the\n * native `chat-agent/install?url=` handler (the mechanism behind\n * awesome-copilot's Install buttons) and are Copilot's hosts, so they follow\n * `github-copilot`: VS Code downloads the file at `url` and asks the user\n * where to save it. Claude Code uses the `claude-cli://open?q=` handler with\n * an install prompt pre-filled to the convention path — reviewed and sent by\n * the user, never auto-executed. All derive from the GitHub blob URL in\n * `source-location`; anything else returns `[]` (copy/download fallback).\n */\nexport function getAgentInstallLinks(\n sourceLocation: string | undefined,\n name: string,\n frameworks: string[] = [],\n): ResourceDeepLink[] {\n const rawUrl = getRawGithubFileUrl(sourceLocation);\n if (!rawUrl) {\n return [];\n }\n const capable = resolveCapableFrameworks(\n frameworks,\n AGENT_LINK_CAPABLE_FRAMEWORKS,\n );\n const encodedRawUrl = encodeURIComponent(rawUrl);\n const installPrompt = (framework: string) => {\n const target = getResourceInstallTarget('agent', framework, name);\n return `Install this agent: fetch ${rawUrl} and save it to ${target?.path}`;\n };\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n links.push(claudePromptLink(installPrompt('claude-code')));\n } else if (framework === 'cursor') {\n links.push(cursorPromptLink(installPrompt('cursor')));\n } else if (framework === 'github-copilot') {\n links.push({\n label: 'VS Code',\n href: `vscode:chat-agent/install?url=${encodedRawUrl}`,\n framework,\n });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:chat-agent/install?url=${encodedRawUrl}`,\n framework,\n });\n }\n }\n return links;\n}\n\n/**\n * One-click install links for an `mcp-config` resource, derived from its body —\n * the canonical `.mcp.json` snippet (spec §3.4). VS Code carries a native\n * `vscode:mcp/install?{json}` handler (Insiders scheme twin), Cursor a\n * documented `cursor://anysphere.cursor-deeplink/mcp/install` one, and\n * Claude Code gets a `claude-cli://open?q=` prompt around\n * `claude mcp add-json` — pre-filled, reviewed, never auto-executed.\n * Hosts follow the resource's own compatible frameworks: `all`/empty expand\n * to every capable host, anything else is intersected, so an mcp-config\n * declaring only Gemini (no handler) gets no links rather than every host's.\n * A missing, unparseable, or multi-server body returns `[]` (copy fallback) —\n * the body stays canonical, links are conveniences derived from it.\n */\nexport function getMcpInstallLinks(\n frameworks: string[],\n resourceName: string,\n bodyContent: string | undefined,\n): ResourceDeepLink[] {\n if (!bodyContent) {\n return [];\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(bodyContent);\n } catch {\n return [];\n }\n if (typeof parsed !== 'object' || parsed === null) {\n return [];\n }\n\n let serverName = resourceName;\n let config: Record<string, unknown>;\n const servers = (parsed as { mcpServers?: unknown }).mcpServers;\n if (servers !== undefined) {\n if (typeof servers !== 'object' || servers === null) {\n return [];\n }\n const entries = Object.entries(servers as Record<string, unknown>);\n if (\n entries.length !== 1 ||\n typeof entries[0][1] !== 'object' ||\n entries[0][1] === null\n ) {\n return [];\n }\n serverName = entries[0][0];\n config = entries[0][1] as Record<string, unknown>;\n } else if ('command' in parsed || 'url' in parsed) {\n config = parsed as Record<string, unknown>;\n } else {\n return [];\n }\n\n const capable = resolveCapableFrameworks(\n frameworks,\n MCP_LINK_CAPABLE_FRAMEWORKS,\n );\n\n const configJson = JSON.stringify(config);\n const links: ResourceDeepLink[] = [];\n for (const framework of capable) {\n if (framework === 'claude-code') {\n const prompt = encodeURIComponent(\n `Install this MCP config by running: claude mcp add-json ${serverName} '${configJson}'`,\n );\n links.push({\n label: 'Claude',\n href: `claude-cli://open?q=${prompt}`,\n framework,\n });\n } else if (framework === 'github-copilot') {\n const vsConfig = encodeURIComponent(\n JSON.stringify({ name: serverName, ...config }),\n );\n links.push({\n label: 'VS Code',\n href: `vscode:mcp/install?${vsConfig}`,\n framework,\n });\n links.push({\n label: 'VS Code Insiders',\n href: `vscode-insiders:mcp/install?${vsConfig}`,\n framework,\n });\n } else if (framework === 'cursor') {\n try {\n links.push({\n label: 'Cursor',\n href: `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(\n serverName,\n )}&config=${btoa(configJson)}`,\n framework,\n });\n } catch {\n // btoa rejects non-Latin1 config content — skip the Cursor link.\n }\n }\n }\n return links;\n}\n\n/**\n * Step two of the journey: how to install a plugin from the marketplace.\n * `marketplaceName` is the entity name, which the producer contract requires\n * to equal the `name` in `marketplace.json`.\n */\nexport function getMarketplaceInstallTemplate(marketplaceName: string): string {\n return `/plugin install <plugin>@${marketplaceName}`;\n}\n\n/**\n * The `.claude/settings.json` snippet a team commits so collaborators are\n * prompted to install the marketplace automatically (Claude Code).\n */\nexport function getMarketplaceTeamSnippet(\n marketplaceName: string,\n repoSlug: string,\n): string {\n return JSON.stringify(\n {\n extraKnownMarketplaces: {\n [marketplaceName]: { source: { source: 'github', repo: repoSlug } },\n },\n },\n null,\n 2,\n );\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nospt/plugin-dev-ai-hub-common",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Common types and schemas for Dev AI Hub",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.cjs.js",