@nospt/plugin-dev-ai-hub-common 1.1.3 → 1.2.0-rc1

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.
@@ -43,18 +43,63 @@ function normalizeFramework(token) {
43
43
  const t = token.trim().toLowerCase();
44
44
  return FRAMEWORK_ALIASES[t] ?? t;
45
45
  }
46
- function filterAllowedFrameworks(frameworks, allowed) {
46
+ function filterAllowedFrameworks(frameworks, allowed, custom = []) {
47
47
  if (allowed === void 0) {
48
48
  return frameworks;
49
49
  }
50
50
  const permitted = new Set(
51
51
  allowed.map(normalizeFramework).filter((f) => f && f !== "all")
52
52
  );
53
+ const customTokens = new Set(custom.map(normalizeFramework));
54
+ const wildcard = [...permitted].filter((f) => !customTokens.has(f));
53
55
  const expanded = frameworks.flatMap(
54
- (f) => normalizeFramework(f) === "all" ? [...permitted] : [normalizeFramework(f)]
56
+ (f) => normalizeFramework(f) === "all" ? wildcard : [normalizeFramework(f)]
55
57
  );
56
58
  return Array.from(new Set(expanded)).filter((f) => permitted.has(f));
57
59
  }
60
+ function parseCustomFrameworks(raw) {
61
+ const frameworks = [];
62
+ const warnings = [];
63
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
64
+ return { frameworks, warnings };
65
+ }
66
+ const builtIn = new Set(KNOWN_FRAMEWORKS);
67
+ const seen = /* @__PURE__ */ new Set();
68
+ for (const [key, value] of Object.entries(raw)) {
69
+ const name = normalizeFramework(key);
70
+ if (!name) {
71
+ warnings.push(
72
+ `devAiHub.frameworks.custom: ignored an entry with an empty name`
73
+ );
74
+ continue;
75
+ }
76
+ if (builtIn.has(name)) {
77
+ warnings.push(
78
+ `devAiHub.frameworks.custom: ignored "${key}" \u2014 it resolves to the built-in framework "${name}"`
79
+ );
80
+ continue;
81
+ }
82
+ if (seen.has(name)) {
83
+ warnings.push(
84
+ `devAiHub.frameworks.custom: ignored duplicate entry "${key}" \u2014 "${name}" is already registered`
85
+ );
86
+ continue;
87
+ }
88
+ seen.add(name);
89
+ const entry = typeof value === "object" && value !== null ? value : {};
90
+ const displayName = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : name;
91
+ const icon = typeof entry.icon === "string" && entry.icon.trim() ? entry.icon.trim() : void 0;
92
+ frameworks.push({ name, displayName, icon });
93
+ }
94
+ return { frameworks, warnings };
95
+ }
96
+ function resolveAllowedFrameworks(allowed, custom) {
97
+ if (custom.length === 0) {
98
+ return allowed;
99
+ }
100
+ const base = allowed ?? KNOWN_FRAMEWORKS.filter((f) => f !== "all");
101
+ return dedupe([...base, ...custom].map(normalizeFramework).filter(Boolean));
102
+ }
58
103
  function getFrameworks(entity) {
59
104
  if (Array.isArray(entity.spec?.agents)) {
60
105
  const agents = entity.spec.agents.filter((a) => typeof a === "string").map(normalizeFramework).filter(Boolean);
@@ -526,6 +571,8 @@ exports.hasDownloadableArtifact = hasDownloadableArtifact;
526
571
  exports.isRenderableContainment = isRenderableContainment;
527
572
  exports.isResourceType = isResourceType;
528
573
  exports.normalizeFramework = normalizeFramework;
574
+ exports.parseCustomFrameworks = parseCustomFrameworks;
575
+ exports.resolveAllowedFrameworks = resolveAllowedFrameworks;
529
576
  exports.resolveCapableFrameworks = resolveCapableFrameworks;
530
577
  exports.showsContentSection = showsContentSection;
531
578
  //# sourceMappingURL=resources.cjs.js.map
@@ -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.\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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"}
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`, unioned with the `custom` keys). Omitting\n * the config key (`allowed` is `undefined`) permits every framework; an\n * explicit empty list permits none, so a deployment can suppress framework\n * exposure entirely. Both sides are normalised (aliases resolved) before\n * 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 — minus `custom`, which `all` never reaches\n * (ADR-0018): the wildcard means \"every host the plugin knows\", and claiming\n * an internal assistant on every universal resource would be an assertion the\n * author never made.\n */\nexport function filterAllowedFrameworks(\n frameworks: string[],\n allowed: string[] | undefined,\n custom: readonly string[] = [],\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 customTokens = new Set(custom.map(normalizeFramework));\n const wildcard = [...permitted].filter(f => !customTokens.has(f));\n const expanded = frameworks.flatMap(f =>\n normalizeFramework(f) === 'all' ? wildcard : [normalizeFramework(f)],\n );\n return Array.from(new Set(expanded)).filter(f => permitted.has(f));\n}\n\n/**\n * A deployment-registered framework (`devAiHub.frameworks.custom`) — a tool\n * the plugin does not ship knowledge of, named by the token its authors write\n * in `spec.agents` (ADR-0018).\n */\nexport interface CustomFramework {\n /** Normalised token matched against a resource's declared frameworks. */\n name: string;\n /** Display label. Falls back to `name` when the config omits `displayName`. */\n displayName: string;\n /** Browser-loadable icon source; `undefined` selects the neutral fallback. */\n icon?: string;\n}\n\n/** The raw per-token shape of `devAiHub.frameworks.custom`. */\nexport interface CustomFrameworkConfig {\n displayName?: string;\n icon?: string;\n}\n\nexport interface ParsedCustomFrameworks {\n frameworks: CustomFramework[];\n /**\n * Human-readable reasons entries were dropped. The backend logs these; the\n * frontend ignores them, but both run the same parse so they can never\n * disagree about which tokens exist.\n */\n warnings: string[];\n}\n\n/**\n * Parse `devAiHub.frameworks.custom` into the registry both sides read.\n *\n * Entries are dropped rather than rejected wholesale — one malformed entry\n * must not take out a deployment's whole framework registry — and each drop\n * carries a reason:\n *\n * - a key that normalises to empty has nothing to match against;\n * - a key that collides with a built-in token or alias (`claude-code`,\n * `claude`) is ignored so built-ins stay authoritative: custom frameworks\n * are install-suppressed, and letting one shadow `claude-code` would\n * silently strip Claude's install paths;\n * - a duplicate after normalisation keeps the first entry, since the second\n * would otherwise win non-deterministically.\n */\nexport function parseCustomFrameworks(raw: unknown): ParsedCustomFrameworks {\n const frameworks: CustomFramework[] = [];\n const warnings: string[] = [];\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n return { frameworks, warnings };\n }\n\n const builtIn = new Set<string>(KNOWN_FRAMEWORKS);\n const seen = new Set<string>();\n\n for (const [key, value] of Object.entries(raw)) {\n const name = normalizeFramework(key);\n if (!name) {\n warnings.push(\n `devAiHub.frameworks.custom: ignored an entry with an empty name`,\n );\n continue;\n }\n if (builtIn.has(name)) {\n warnings.push(\n `devAiHub.frameworks.custom: ignored \"${key}\" — it resolves to the built-in framework \"${name}\"`,\n );\n continue;\n }\n if (seen.has(name)) {\n warnings.push(\n `devAiHub.frameworks.custom: ignored duplicate entry \"${key}\" — \"${name}\" is already registered`,\n );\n continue;\n }\n seen.add(name);\n\n const entry = (\n typeof value === 'object' && value !== null ? value : {}\n ) as CustomFrameworkConfig;\n const displayName =\n typeof entry.displayName === 'string' && entry.displayName.trim()\n ? entry.displayName.trim()\n : name;\n const icon =\n typeof entry.icon === 'string' && entry.icon.trim()\n ? entry.icon.trim()\n : undefined;\n frameworks.push({ name, displayName, icon });\n }\n\n return { frameworks, warnings };\n}\n\n/**\n * The allow-list actually applied, once `custom` is taken into account\n * (ADR-0018). Registering a custom framework activates the gate even when\n * `allowed` is omitted — otherwise the deployment would have registered a\n * token that nothing distinguishes from the unknown tokens beside it.\n *\n * - no custom frameworks → `allowed` unchanged (including `undefined`);\n * - `allowed` set → `allowed` ∪ custom;\n * - `allowed` omitted → every built-in ∪ custom.\n */\nexport function resolveAllowedFrameworks(\n allowed: string[] | undefined,\n custom: readonly string[],\n): string[] | undefined {\n if (custom.length === 0) {\n return allowed;\n }\n const base =\n allowed ?? KNOWN_FRAMEWORKS.filter((f): f is FrameworkToken => f !== 'all');\n return dedupe([...base, ...custom].map(normalizeFramework).filter(Boolean));\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 this resource has no generated install path to offer — either\n * an active deployment allow-list (`devAiHub.frameworks.allowed`) left it\n * with no frameworks, or every framework it does have is a custom,\n * discovery-only one (ADR-0018). Lets the UI tell \"author declared nothing\"\n * (`frameworks: []`, show neutral install defaults) apart from \"nothing\n * installable remains\" (hide generated install paths and launchers;\n * 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`. Two cases set it: a\n * resource that declared no frameworks (which otherwise means \"every host\")\n * under an active allow-list, where it holds the allow-list itself so a\n * bare resource cannot produce launchers for disallowed tools; and a\n * resource carrying custom frameworks, where it holds the remainder once\n * those are removed (ADR-0018). Kept separate from `frameworks` so neither\n * inferred set renders as a 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 * True when the resource is recommended from either source: its catalog\n * tags carry `RECOMMENDED_TAG` (set by `toResourceSummary`, ADR-0020), or\n * the backend router overlaid it from the `curation` table (ADR-0017).\n * `undefined` and `false` both mean \"not recommended\"; only `true` renders\n * the badge.\n */\n recommended?: boolean;\n /**\n * True when `recommended` comes from the entity's own `RECOMMENDED_TAG`\n * (ADR-0020). Such a recommendation is owned by the resource's author, so\n * the backoffice cannot remove it — only removing the tag can.\n */\n recommendedByTag?: boolean;\n /**\n * True when a curator recommended this resource in the backoffice — a\n * `curation` row (ADR-0017). Can be true alongside `recommendedByTag`, for\n * a row recorded before the author added the tag; the backoffice then\n * offers to clear it, so that removing the tag really un-recommends.\n */\n recommendedByCuration?: boolean;\n /**\n * When the plugin first saw this resource, as an ISO timestamp — set by the\n * backend router from its own `resource_first_seen` table (ADR-0021), since\n * the catalog exposes no creation date. `undefined` for a resource that\n * already existed when tracking began; the \"Newest\" sort ranks those as\n * the oldest.\n */\n firstSeenAt?: string;\n /**\n * Set by the backend router from the `curation` table (ADR-0019), and only\n * ever present when the caller explicitly asked for excluded resources via\n * `?includeExcluded=true`. The default `GET /resources` response omits\n * excluded resources entirely rather than flagging them, so on the browse\n * page this field is always `undefined`.\n */\n excluded?: boolean;\n /** Who last excluded this resource, for the backoffice's exclusion list. */\n excludedBy?: string;\n /** When it was last excluded, ISO-8601. */\n excludedAt?: 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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"}
@@ -41,18 +41,63 @@ function normalizeFramework(token) {
41
41
  const t = token.trim().toLowerCase();
42
42
  return FRAMEWORK_ALIASES[t] ?? t;
43
43
  }
44
- function filterAllowedFrameworks(frameworks, allowed) {
44
+ function filterAllowedFrameworks(frameworks, allowed, custom = []) {
45
45
  if (allowed === void 0) {
46
46
  return frameworks;
47
47
  }
48
48
  const permitted = new Set(
49
49
  allowed.map(normalizeFramework).filter((f) => f && f !== "all")
50
50
  );
51
+ const customTokens = new Set(custom.map(normalizeFramework));
52
+ const wildcard = [...permitted].filter((f) => !customTokens.has(f));
51
53
  const expanded = frameworks.flatMap(
52
- (f) => normalizeFramework(f) === "all" ? [...permitted] : [normalizeFramework(f)]
54
+ (f) => normalizeFramework(f) === "all" ? wildcard : [normalizeFramework(f)]
53
55
  );
54
56
  return Array.from(new Set(expanded)).filter((f) => permitted.has(f));
55
57
  }
58
+ function parseCustomFrameworks(raw) {
59
+ const frameworks = [];
60
+ const warnings = [];
61
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
62
+ return { frameworks, warnings };
63
+ }
64
+ const builtIn = new Set(KNOWN_FRAMEWORKS);
65
+ const seen = /* @__PURE__ */ new Set();
66
+ for (const [key, value] of Object.entries(raw)) {
67
+ const name = normalizeFramework(key);
68
+ if (!name) {
69
+ warnings.push(
70
+ `devAiHub.frameworks.custom: ignored an entry with an empty name`
71
+ );
72
+ continue;
73
+ }
74
+ if (builtIn.has(name)) {
75
+ warnings.push(
76
+ `devAiHub.frameworks.custom: ignored "${key}" \u2014 it resolves to the built-in framework "${name}"`
77
+ );
78
+ continue;
79
+ }
80
+ if (seen.has(name)) {
81
+ warnings.push(
82
+ `devAiHub.frameworks.custom: ignored duplicate entry "${key}" \u2014 "${name}" is already registered`
83
+ );
84
+ continue;
85
+ }
86
+ seen.add(name);
87
+ const entry = typeof value === "object" && value !== null ? value : {};
88
+ const displayName = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : name;
89
+ const icon = typeof entry.icon === "string" && entry.icon.trim() ? entry.icon.trim() : void 0;
90
+ frameworks.push({ name, displayName, icon });
91
+ }
92
+ return { frameworks, warnings };
93
+ }
94
+ function resolveAllowedFrameworks(allowed, custom) {
95
+ if (custom.length === 0) {
96
+ return allowed;
97
+ }
98
+ const base = allowed ?? KNOWN_FRAMEWORKS.filter((f) => f !== "all");
99
+ return dedupe([...base, ...custom].map(normalizeFramework).filter(Boolean));
100
+ }
56
101
  function getFrameworks(entity) {
57
102
  if (Array.isArray(entity.spec?.agents)) {
58
103
  const agents = entity.spec.agents.filter((a) => typeof a === "string").map(normalizeFramework).filter(Boolean);
@@ -493,5 +538,5 @@ function getMarketplaceTeamSnippet(marketplaceName, repoSlug) {
493
538
  );
494
539
  }
495
540
 
496
- export { AGENT_LINK_CAPABLE_FRAMEWORKS, ANNOTATION_COMPATIBLE_FRAMEWORKS, ANNOTATION_HELP, DEVAIHUB_ANNOTATION_PREFIX, INSTALLABLE_FRAMEWORKS, KNOWN_FRAMEWORKS, MARKETPLACE_CAPABLE_FRAMEWORKS, MARKETPLACE_MEMBER_TYPES, MCP_LINK_CAPABLE_FRAMEWORKS, PLUGIN_MEMBER_TYPES, PROMPT_LINK_CAPABLE_FRAMEWORKS, RESOURCE_TYPES, RESOURCE_TYPE_REGISTRY, expandInstallFrameworks, filterAllowedFrameworks, getAgentInstallLinks, getBodyShape, getFrameworks, getInstallSteps, getMarketplaceAddCommands, getMarketplaceInstallTemplate, getMarketplaceRepoSlug, getMarketplaceTeamSnippet, getMcpInstallLinks, getPromptInstallLinks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, isRenderableContainment, isResourceType, normalizeFramework, resolveCapableFrameworks, showsContentSection };
541
+ export { AGENT_LINK_CAPABLE_FRAMEWORKS, ANNOTATION_COMPATIBLE_FRAMEWORKS, ANNOTATION_HELP, DEVAIHUB_ANNOTATION_PREFIX, INSTALLABLE_FRAMEWORKS, KNOWN_FRAMEWORKS, MARKETPLACE_CAPABLE_FRAMEWORKS, MARKETPLACE_MEMBER_TYPES, MCP_LINK_CAPABLE_FRAMEWORKS, PLUGIN_MEMBER_TYPES, PROMPT_LINK_CAPABLE_FRAMEWORKS, RESOURCE_TYPES, RESOURCE_TYPE_REGISTRY, expandInstallFrameworks, filterAllowedFrameworks, getAgentInstallLinks, getBodyShape, getFrameworks, getInstallSteps, getMarketplaceAddCommands, getMarketplaceInstallTemplate, getMarketplaceRepoSlug, getMarketplaceTeamSnippet, getMcpInstallLinks, getPromptInstallLinks, getResourceInstallTarget, hasCopyableBody, hasDownloadableArtifact, isRenderableContainment, isResourceType, normalizeFramework, parseCustomFrameworks, resolveAllowedFrameworks, resolveCapableFrameworks, showsContentSection };
497
542
  //# sourceMappingURL=resources.esm.js.map