{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAoB,MAAM,cAAc,CAAC;AAG/D,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAErH,8EAA8E;AAC9E,eAAO,MAAM,cAAc,EAAE,SAAS,mBAAmB,EAEZ,CAAC;AAE9C,oDAAoD;AACpD,wBAAgB,SAAS,CAAC,EAAE,EAAE,cAAc,GAAG,mBAAmB,GAAG,SAAS,CAE7E;AAED,gFAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,mBAAmB,GAAG,SAAS,CAElG;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAkCD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,gBAAgB,GAAG,IAAI,CAcrH;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,mBAAmB,EAAE,GAAG,WAAW,EAAE,CAWrG;AAED,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AACvC,YAAY,EACX,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,cAAc,GACd,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,IAAI;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC9B,CAkBA","sourcesContent":["/**\n * Plugin format registry.\n *\n * The single ordered list of every supported plugin format. Readers, the\n * marketplace parser, and `ProposePlugin` all go through here instead of\n * branching on a format, so adding or evolving a vendor layout is a one-file\n * change (write/adjust an adapter, add it to {@link PLUGIN_FORMATS}).\n *\n * Order is precedence order — native `.agents-plugin` first, then Claude, then\n * Copilot — so \"first match wins\" when a directory carries more than one format.\n */\n\nimport type { NormalizedPlugin } from \"../manifest.js\";\nimport { agentsFormat } from \"./agents.js\";\nimport { claudeFormat } from \"./claude.js\";\nimport { copilotFormat, copilotReadPaths } from \"./copilot.js\";\nimport { JSON_MANIFEST_READ_PATHS } from \"./jsonManifest.js\";\nimport { knownUnsupportedSurfaces, modelledManifestKeys } from \"./shared.js\";\nimport type { EmittedFile, MarketplacePlatform, PluginDraft, PluginFormatAdapter, PluginFormatId } from \"./types.js\";\n\n/** All registered formats, in precedence order (lower `precedence` first). */\nexport const PLUGIN_FORMATS: readonly PluginFormatAdapter[] = [agentsFormat, claudeFormat, copilotFormat]\n\t.slice()\n\t.sort((a, b) => a.precedence - b.precedence);\n\n/** Look up an adapter by its internal format id. */\nexport function getFormat(id: PluginFormatId): PluginFormatAdapter | undefined {\n\treturn PLUGIN_FORMATS.find((f) => f.id === id);\n}\n\n/** Look up an adapter by its public platform token (`\"github\"` → Copilot). */\nexport function getFormatByPlatform(platform: MarketplacePlatform): PluginFormatAdapter | undefined {\n\treturn PLUGIN_FORMATS.find((f) => f.platform === platform);\n}\n\n/**\n * Whether `root` carries an actual manifest file, in any supported format.\n * Cheap: existence only, no parse.\n */\nexport function hasAnyManifest(root: string): boolean {\n\treturn PLUGIN_FORMATS.some((format) => format.hasManifest(root));\n}\n\n/**\n * Whether the skill scanner should treat `root` as a plugin and stop descending.\n *\n * Keyed on the **manifest**, deliberately not on `detectPlugin`. Claude's\n * manifest-optional rule counts a bare `SKILL.md` as enough to make a directory a\n * plugin — true inside a plugins directory, catastrophic inside a skills\n * directory, where it describes every plain skill folder there is. Using\n * `detectPlugin` here would make the scanner skip all of them.\n */\nexport function isPluginRoot(root: string): boolean {\n\treturn hasAnyManifest(root);\n}\n\n/**\n * Formats that recognize `root`, manifest-bearing ones first.\n *\n * The two-pass order matters. Claude's manifest-optional rule means its adapter\n * recognizes any directory with components in default locations — including a\n * Copilot plugin, which has its own manifest and a lower precedence number. A\n * single precedence-ordered pass would therefore hand every Copilot plugin to\n * the Claude adapter. An explicit manifest always beats an inferred match.\n */\nfunction matchingFormats(root: string): PluginFormatAdapter[] {\n\tconst withManifest = PLUGIN_FORMATS.filter((f) => f.hasManifest(root));\n\tconst inferred = PLUGIN_FORMATS.filter((f) => !f.hasManifest(root) && f.detectPlugin(root));\n\treturn [...withManifest, ...inferred];\n}\n\n/**\n * Every platform a plugin directory *offers*, precedence winner first.\n *\n * Manifest-bearing formats only: an inferred match means \"Claude would also load\n * this\", not \"this directory ships a Claude layout\", and reporting it would make\n * a Copilot-only plugin claim `[\"github\", \"claude\"]`. When nothing has a manifest\n * the inferred winner is all there is, so it stands alone.\n */\nfunction detectPlatforms(root: string): MarketplacePlatform[] {\n\tconst declared = PLUGIN_FORMATS.filter((f) => f.hasManifest(root));\n\tconst platforms: MarketplacePlatform[] = [];\n\tfor (const format of declared.length > 0 ? declared : matchingFormats(root).slice(0, 1)) {\n\t\tif (!platforms.includes(format.platform)) platforms.push(format.platform);\n\t}\n\treturn platforms;\n}\n\n/**\n * Parse a plugin directory using the highest-precedence format present.\n * Returns null when no registered format recognizes the directory.\n *\n * `supportPlatform` records *every* format present (not just the winner), so a\n * directory carrying more than one vendor layout reports all of them.\n */\nexport function parsePluginWithFormats(root: string, options?: { requireManifest?: boolean }): NormalizedPlugin | null {\n\t// Inside a skills directory the manifest is what promotes a folder from plain\n\t// skill to plugin, so manifest-less detection must be switched off there or\n\t// every skill folder becomes a plugin. See §1.6.\n\tif (options?.requireManifest && !hasAnyManifest(root)) return null;\n\tfor (const format of matchingFormats(root)) {\n\t\tconst parsed = format.parsePlugin(root);\n\t\tif (parsed) {\n\t\t\t// Winner's platform is already first; fold in the rest.\n\t\t\tconst supportPlatform = detectPlatforms(root);\n\t\t\treturn { ...parsed, supportPlatform };\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Render a draft into the on-disk files for the given platforms (defaulting to\n * the draft's own `supportPlatform`, then to every format). Files from different\n * formats live under distinct marker directories, so the merge never collides.\n */\nexport function emitForPlatforms(draft: PluginDraft, platforms?: MarketplacePlatform[]): EmittedFile[] {\n\tconst targets = platforms ?? draft.supportPlatform ?? PLUGIN_FORMATS.map((f) => f.platform);\n\tconst files: EmittedFile[] = [];\n\tconst seen = new Set<MarketplacePlatform>();\n\tfor (const platform of targets) {\n\t\tif (seen.has(platform)) continue;\n\t\tseen.add(platform);\n\t\tconst adapter = getFormatByPlatform(platform);\n\t\tif (adapter) files.push(...adapter.emit(draft));\n\t}\n\treturn files;\n}\n\nexport { claudeFormat, copilotFormat };\nexport type {\n\tEmittedFile,\n\tMarketplacePlatform,\n\tPluginDraft,\n\tPluginFormatAdapter,\n\tPluginFormatId,\n} from \"./types.js\";\n\n/**\n * Everything the adapters claim to understand: manifest keys, plugin-relative\n * paths they read, marketplace index locations, and the surfaces they knowingly\n * skip.\n *\n * This is the input to the Tier 2 drift check (§2.2), and it is a *declaration*\n * on purpose. A checker that inferred coverage by reading the parsers would be\n * validating its own inference; a declared set is a claim the codebase makes,\n * which a vendor reference can falsify.\n *\n * `unsupportedSurfaces` is not a gap list. Those paths are known and\n * deliberately unhandled, so re-flagging them every run would bury the one\n * finding that matters: a vendor path in neither set.\n */\nexport function declaredVocabulary(): {\n\tmanifestKeys: string[];\n\tmarketplaceKeys: string[];\n\treadPaths: string[];\n\tmarketplaceFiles: string[];\n\tunsupportedSurfaces: string[];\n} {\n\tconst readPaths = new Set<string>([...JSON_MANIFEST_READ_PATHS, ...copilotReadPaths()]);\n\tfor (const format of PLUGIN_FORMATS) {\n\t\treadPaths.add(\n\t\t\t`${format.id === \"copilot\" ? \".github\" : format.id === \"claude\" ? \".claude-plugin\" : \".agents-plugin\"}/plugin.json`,\n\t\t);\n\t}\n\treturn {\n\t\tmanifestKeys: modelledManifestKeys(),\n\t\t// Marketplace *index* keys, modelled by `parseMarketplaceDir`. Declared\n\t\t// separately from plugin-manifest keys but checked together: a reference\n\t\t// page documents both, and a drift check that knew only one of the two\n\t\t// vocabularies would report the other's fields as unknown every run.\n\t\tmarketplaceKeys: [\"name\", \"owner\", \"metadata\", \"pluginRoot\", \"plugins\", \"source\", \"supportPlatform\", \"strict\"],\n\t\treadPaths: [...readPaths].sort(),\n\t\tmarketplaceFiles: [...new Set(PLUGIN_FORMATS.flatMap((f) => f.marketplaceFiles))].sort(),\n\t\tunsupportedSurfaces: knownUnsupportedSurfaces(),\n\t};\n}\n"]}