@kolisachint/hoocode-agent 0.4.113 → 0.4.114

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.
Files changed (117) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +1 -1
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/extensions/plugins/authoring.d.ts +51 -0
  6. package/dist/core/extensions/plugins/authoring.d.ts.map +1 -0
  7. package/dist/core/extensions/plugins/authoring.js +91 -0
  8. package/dist/core/extensions/plugins/authoring.js.map +1 -0
  9. package/dist/core/extensions/plugins/default-marketplace/.agents-plugin/marketplace.json +12 -0
  10. package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/.agents-plugin/plugin.json +6 -0
  11. package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/commands/hello.md +5 -0
  12. package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/skills/greet/SKILL.md +7 -0
  13. package/dist/core/extensions/plugins/formats/agents.d.ts +7 -0
  14. package/dist/core/extensions/plugins/formats/agents.d.ts.map +1 -0
  15. package/dist/core/extensions/plugins/formats/agents.js +14 -0
  16. package/dist/core/extensions/plugins/formats/agents.js.map +1 -0
  17. package/dist/core/extensions/plugins/formats/claude.d.ts +10 -0
  18. package/dist/core/extensions/plugins/formats/claude.d.ts.map +1 -0
  19. package/dist/core/extensions/plugins/formats/claude.js +17 -0
  20. package/dist/core/extensions/plugins/formats/claude.js.map +1 -0
  21. package/dist/core/extensions/plugins/formats/copilot.d.ts +23 -0
  22. package/dist/core/extensions/plugins/formats/copilot.d.ts.map +1 -0
  23. package/dist/core/extensions/plugins/formats/copilot.js +130 -0
  24. package/dist/core/extensions/plugins/formats/copilot.js.map +1 -0
  25. package/dist/core/extensions/plugins/formats/index.d.ts +41 -0
  26. package/dist/core/extensions/plugins/formats/index.d.ts.map +1 -0
  27. package/dist/core/extensions/plugins/formats/index.js +76 -0
  28. package/dist/core/extensions/plugins/formats/index.js.map +1 -0
  29. package/dist/core/extensions/plugins/formats/jsonManifest.d.ts +21 -0
  30. package/dist/core/extensions/plugins/formats/jsonManifest.d.ts.map +1 -0
  31. package/dist/core/extensions/plugins/formats/jsonManifest.js +97 -0
  32. package/dist/core/extensions/plugins/formats/jsonManifest.js.map +1 -0
  33. package/dist/core/extensions/plugins/formats/shared.d.ts +44 -0
  34. package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -0
  35. package/dist/core/extensions/plugins/formats/shared.js +107 -0
  36. package/dist/core/extensions/plugins/formats/shared.js.map +1 -0
  37. package/dist/core/extensions/plugins/formats/types.d.ts +118 -0
  38. package/dist/core/extensions/plugins/formats/types.d.ts.map +1 -0
  39. package/dist/core/extensions/plugins/formats/types.js +19 -0
  40. package/dist/core/extensions/plugins/formats/types.js.map +1 -0
  41. package/dist/core/extensions/plugins/install.d.ts +73 -0
  42. package/dist/core/extensions/plugins/install.d.ts.map +1 -0
  43. package/dist/core/extensions/plugins/install.js +173 -0
  44. package/dist/core/extensions/plugins/install.js.map +1 -0
  45. package/dist/core/extensions/plugins/manifest.d.ts +25 -7
  46. package/dist/core/extensions/plugins/manifest.d.ts.map +1 -1
  47. package/dist/core/extensions/plugins/manifest.js +16 -91
  48. package/dist/core/extensions/plugins/manifest.js.map +1 -1
  49. package/dist/core/extensions/plugins/marketplace.d.ts +5 -4
  50. package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
  51. package/dist/core/extensions/plugins/marketplace.js +8 -13
  52. package/dist/core/extensions/plugins/marketplace.js.map +1 -1
  53. package/dist/core/settings-defaults.d.ts +2 -0
  54. package/dist/core/settings-defaults.d.ts.map +1 -1
  55. package/dist/core/settings-defaults.js +2 -0
  56. package/dist/core/settings-defaults.js.map +1 -1
  57. package/dist/core/settings-manager.d.ts +4 -0
  58. package/dist/core/settings-manager.d.ts.map +1 -1
  59. package/dist/core/settings-manager.js +16 -0
  60. package/dist/core/settings-manager.js.map +1 -1
  61. package/dist/core/settings-types.d.ts +2 -0
  62. package/dist/core/settings-types.d.ts.map +1 -1
  63. package/dist/core/settings-types.js.map +1 -1
  64. package/dist/core/subagent-depth.d.ts +13 -0
  65. package/dist/core/subagent-depth.d.ts.map +1 -1
  66. package/dist/core/subagent-depth.js +15 -0
  67. package/dist/core/subagent-depth.js.map +1 -1
  68. package/dist/core/subagent-pool.d.ts.map +1 -1
  69. package/dist/core/subagent-pool.js +4 -1
  70. package/dist/core/subagent-pool.js.map +1 -1
  71. package/dist/core/tools/find.d.ts +8 -3
  72. package/dist/core/tools/find.d.ts.map +1 -1
  73. package/dist/core/tools/find.js +189 -122
  74. package/dist/core/tools/find.js.map +1 -1
  75. package/dist/core/tools/index.d.ts +0 -4
  76. package/dist/core/tools/index.d.ts.map +1 -1
  77. package/dist/core/tools/index.js +2 -5
  78. package/dist/core/tools/index.js.map +1 -1
  79. package/dist/core/tools/plugin-tool-names.d.ts +21 -0
  80. package/dist/core/tools/plugin-tool-names.d.ts.map +1 -0
  81. package/dist/core/tools/plugin-tool-names.js +31 -0
  82. package/dist/core/tools/plugin-tool-names.js.map +1 -0
  83. package/dist/core/tools/plugins.d.ts +49 -0
  84. package/dist/core/tools/plugins.d.ts.map +1 -0
  85. package/dist/core/tools/plugins.js +186 -0
  86. package/dist/core/tools/plugins.js.map +1 -0
  87. package/dist/core/tools/propose-plugin.d.ts +36 -0
  88. package/dist/core/tools/propose-plugin.d.ts.map +1 -0
  89. package/dist/core/tools/propose-plugin.js +219 -0
  90. package/dist/core/tools/propose-plugin.js.map +1 -0
  91. package/dist/core/warm-subagent-pool.d.ts.map +1 -1
  92. package/dist/core/warm-subagent-pool.js +4 -1
  93. package/dist/core/warm-subagent-pool.js.map +1 -1
  94. package/dist/extensions/core/marketplace.d.ts +7 -3
  95. package/dist/extensions/core/marketplace.d.ts.map +1 -1
  96. package/dist/extensions/core/marketplace.js +23 -67
  97. package/dist/extensions/core/marketplace.js.map +1 -1
  98. package/dist/extensions/core/mcp-deferred.d.ts +28 -0
  99. package/dist/extensions/core/mcp-deferred.d.ts.map +1 -0
  100. package/dist/extensions/core/mcp-deferred.js +53 -0
  101. package/dist/extensions/core/mcp-deferred.js.map +1 -0
  102. package/dist/extensions/core/mcp-loader.d.ts.map +1 -1
  103. package/dist/extensions/core/mcp-loader.js +132 -78
  104. package/dist/extensions/core/mcp-loader.js.map +1 -1
  105. package/dist/main.d.ts.map +1 -1
  106. package/dist/main.js +19 -1
  107. package/dist/main.js.map +1 -1
  108. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  109. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  110. package/examples/extensions/minimal-mode.ts +1 -1
  111. package/examples/extensions/sandbox/package.json +1 -1
  112. package/examples/extensions/with-deps/package.json +1 -1
  113. package/package.json +5 -5
  114. package/dist/core/tools/glob.d.ts +0 -42
  115. package/dist/core/tools/glob.d.ts.map +0 -1
  116. package/dist/core/tools/glob.js +0 -432
  117. package/dist/core/tools/glob.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,8EAA8E;AAC9E,MAAM,CAAC,MAAM,cAAc,GAAmC,CAAC,YAAY,EAAE,YAAY,EAAE,aAAa,CAAC;KACvG,KAAK,EAAE;KACP,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;AAE9C,oDAAoD;AACpD,MAAM,UAAU,SAAS,CAAC,EAAkB,EAAmC;IAC9E,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAAA,CAC/C;AAED,gFAA8E;AAC9E,MAAM,UAAU,mBAAmB,CAAC,QAA6B,EAAmC;IACnG,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;AAAA,CAC3D;AAED,6EAA6E;AAC7E,MAAM,UAAU,eAAe,CAAC,IAAY,EAAyB;IACpE,MAAM,SAAS,GAA0B,EAAE,CAAC;IAC5C,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAA2B;IAC7E,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,MAAM,EAAE,CAAC;gBACZ,6EAA6E;gBAC7E,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;gBAC9C,OAAO,EAAE,GAAG,MAAM,EAAE,eAAe,EAAE,CAAC;YACvC,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAkB,EAAE,SAAiC,EAAiB;IACtG,MAAM,OAAO,GAAG,SAAS,IAAI,KAAK,CAAC,eAAe,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC5F,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC5C,KAAK,MAAM,QAAQ,IAAI,OAAO,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QACjC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnB,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QAC9C,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC","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 } from \"./copilot.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/** Every platform present in a plugin directory, precedence winner first. */\nexport function detectPlatforms(root: string): MarketplacePlatform[] {\n\tconst platforms: MarketplacePlatform[] = [];\n\tfor (const format of PLUGIN_FORMATS) {\n\t\tif (format.detectPlugin(root) && !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): NormalizedPlugin | null {\n\tfor (const format of PLUGIN_FORMATS) {\n\t\tif (format.detectPlugin(root)) {\n\t\t\tconst parsed = format.parsePlugin(root);\n\t\t\tif (parsed) {\n\t\t\t\t// Winner's platform is already first via precedence order; fold in the rest.\n\t\t\t\tconst supportPlatform = detectPlatforms(root);\n\t\t\t\treturn { ...parsed, supportPlatform };\n\t\t\t}\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 { agentsFormat, claudeFormat, copilotFormat };\nexport type { EmittedFile, MarketplacePlatform, PluginDraft, PluginFormatAdapter, PluginFormatId } from \"./types.js\";\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Factory for the `plugin.json`-style formats (native `.agents-plugin` and
3
+ * Claude Code `.claude-plugin`). They share one on-disk layout — a JSON manifest
4
+ * under a marker directory plus `skills/`, `commands/`, `agents/`, `themes/`,
5
+ * `hooks/hooks.json` — and differ only in the marker directory, the format id,
6
+ * and whether `providers` are honored (native-only). Both adapters are produced
7
+ * from this factory so the shared reader/writer lives in one place.
8
+ */
9
+ import type { PluginFormatAdapter, PluginFormatId } from "./types.js";
10
+ interface JsonManifestOptions {
11
+ id: Extract<PluginFormatId, "agents" | "claude">;
12
+ /** Marker subdirectory, e.g. ".agents-plugin". */
13
+ manifestDir: string;
14
+ precedence: number;
15
+ label: string;
16
+ /** Native honors `providers`; the Claude-compat path ignores them. */
17
+ supportsProviders: boolean;
18
+ }
19
+ export declare function createJsonManifestAdapter(opts: JsonManifestOptions): PluginFormatAdapter;
20
+ export {};
21
+ //# sourceMappingURL=jsonManifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonManifest.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/jsonManifest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,OAAO,KAAK,EAA4B,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIhG,UAAU,mBAAmB;IAC5B,EAAE,EAAE,OAAO,CAAC,cAAc,EAAE,QAAQ,GAAG,QAAQ,CAAC,CAAC;IACjD,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,iBAAiB,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,mBAAmB,CAiGxF","sourcesContent":["/**\n * Factory for the `plugin.json`-style formats (native `.agents-plugin` and\n * Claude Code `.claude-plugin`). They share one on-disk layout — a JSON manifest\n * under a marker directory plus `skills/`, `commands/`, `agents/`, `themes/`,\n * `hooks/hooks.json` — and differ only in the marker directory, the format id,\n * and whether `providers` are honored (native-only). Both adapters are produced\n * from this factory so the shared reader/writer lives in one place.\n */\n\nimport * as path from \"node:path\";\nimport type { NormalizedPlugin, PluginProvider } from \"../manifest.js\";\nimport {\n\tauthoredHooksToConfig,\n\tdirIfExists,\n\temitJson,\n\temitMarkdown,\n\tnormalizeHooks,\n\tnormalizeMcp,\n\tparseAuthor,\n\ttype RawManifest,\n\treadJson,\n\tslug,\n} from \"./shared.js\";\nimport type { EmittedFile, PluginDraft, PluginFormatAdapter, PluginFormatId } from \"./types.js\";\n\nconst MANIFEST_FILE = \"plugin.json\";\n\ninterface JsonManifestOptions {\n\tid: Extract<PluginFormatId, \"agents\" | \"claude\">;\n\t/** Marker subdirectory, e.g. \".agents-plugin\". */\n\tmanifestDir: string;\n\tprecedence: number;\n\tlabel: string;\n\t/** Native honors `providers`; the Claude-compat path ignores them. */\n\tsupportsProviders: boolean;\n}\n\nexport function createJsonManifestAdapter(opts: JsonManifestOptions): PluginFormatAdapter {\n\tconst manifestRelPath = path.join(opts.manifestDir, MANIFEST_FILE);\n\n\treturn {\n\t\tid: opts.id,\n\t\tplatform: opts.id, // \"agents\" | \"claude\" map 1:1 to their platform token\n\t\tprecedence: opts.precedence,\n\t\tlabel: opts.label,\n\t\tmarketplaceFile: path.join(opts.manifestDir, \"marketplace.json\"),\n\n\t\tdetectPlugin(root: string): boolean {\n\t\t\treturn readJson(path.join(root, manifestRelPath)) != null;\n\t\t},\n\n\t\tparsePlugin(root: string): NormalizedPlugin | null {\n\t\t\tconst manifestPath = path.join(root, manifestRelPath);\n\t\t\tconst raw = readJson<RawManifest>(manifestPath);\n\t\t\tif (!raw) return null;\n\n\t\t\tconst id = (raw.name ?? path.basename(root)).trim();\n\t\t\tif (!id) return null;\n\n\t\t\tconst providers: PluginProvider[] | undefined =\n\t\t\t\topts.supportsProviders && Array.isArray(raw.providers) ? (raw.providers as PluginProvider[]) : undefined;\n\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tversion: raw.version,\n\t\t\t\tdescription: raw.description,\n\t\t\t\tauthor: parseAuthor(raw.author),\n\t\t\t\troot,\n\t\t\t\tmanifestPath,\n\t\t\t\tformat: opts.id,\n\t\t\t\t// Single-format view; the registry widens this to every format present.\n\t\t\t\tsupportPlatform: [opts.id],\n\t\t\t\tskillsDir: dirIfExists(root, \"skills\"),\n\t\t\t\tcommandsDir: dirIfExists(root, \"commands\"),\n\t\t\t\tagentsDir: dirIfExists(root, \"agents\"),\n\t\t\t\tthemesDir: dirIfExists(root, \"themes\"),\n\t\t\t\thooks: normalizeHooks(raw.hooks, root),\n\t\t\t\tmcpServers: normalizeMcp(raw.mcpServers, root),\n\t\t\t\tproviders,\n\t\t\t};\n\t\t},\n\n\t\temit(draft: PluginDraft): EmittedFile[] {\n\t\t\tconst files: EmittedFile[] = [];\n\t\t\tconst mcpServers = draft.mcpServers?.length\n\t\t\t\t? Object.fromEntries(\n\t\t\t\t\t\tdraft.mcpServers.map((s) => [\n\t\t\t\t\t\t\ts.name,\n\t\t\t\t\t\t\t{ command: s.command, ...(s.args ? { args: s.args } : {}), ...(s.env ? { env: s.env } : {}) },\n\t\t\t\t\t\t]),\n\t\t\t\t\t)\n\t\t\t\t: undefined;\n\n\t\t\tfiles.push({\n\t\t\t\tpath: manifestRelPath,\n\t\t\t\tcontent: emitJson({\n\t\t\t\t\tname: draft.id,\n\t\t\t\t\t...(draft.version ? { version: draft.version } : {}),\n\t\t\t\t\t...(draft.description ? { description: draft.description } : {}),\n\t\t\t\t\t...(draft.author ? { author: draft.author } : {}),\n\t\t\t\t\t...(mcpServers ? { mcpServers } : {}),\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tfor (const s of draft.skills ?? []) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"skills\", slug(s.name), \"SKILL.md\"),\n\t\t\t\t\tcontent: emitMarkdown({ name: s.name, description: s.description }, s.body),\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const c of draft.commands ?? []) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"commands\", `${slug(c.name)}.md`),\n\t\t\t\t\tcontent: emitMarkdown({ description: c.description }, c.body),\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const a of draft.agents ?? []) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"agents\", `${slug(a.name)}.md`),\n\t\t\t\t\tcontent: emitMarkdown(\n\t\t\t\t\t\t{ name: a.name, description: a.description, tools: a.tools, model: a.model },\n\t\t\t\t\t\ta.body,\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (draft.hooks?.length) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"hooks\", \"hooks.json\"),\n\t\t\t\t\tcontent: emitJson({ hooks: authoredHooksToConfig(draft.hooks) }),\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn files;\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Factory for the `plugin.json`-style formats (native `.agents-plugin` and
3
+ * Claude Code `.claude-plugin`). They share one on-disk layout — a JSON manifest
4
+ * under a marker directory plus `skills/`, `commands/`, `agents/`, `themes/`,
5
+ * `hooks/hooks.json` — and differ only in the marker directory, the format id,
6
+ * and whether `providers` are honored (native-only). Both adapters are produced
7
+ * from this factory so the shared reader/writer lives in one place.
8
+ */
9
+ import * as path from "node:path";
10
+ import { authoredHooksToConfig, dirIfExists, emitJson, emitMarkdown, normalizeHooks, normalizeMcp, parseAuthor, readJson, slug, } from "./shared.js";
11
+ const MANIFEST_FILE = "plugin.json";
12
+ export function createJsonManifestAdapter(opts) {
13
+ const manifestRelPath = path.join(opts.manifestDir, MANIFEST_FILE);
14
+ return {
15
+ id: opts.id,
16
+ platform: opts.id, // "agents" | "claude" map 1:1 to their platform token
17
+ precedence: opts.precedence,
18
+ label: opts.label,
19
+ marketplaceFile: path.join(opts.manifestDir, "marketplace.json"),
20
+ detectPlugin(root) {
21
+ return readJson(path.join(root, manifestRelPath)) != null;
22
+ },
23
+ parsePlugin(root) {
24
+ const manifestPath = path.join(root, manifestRelPath);
25
+ const raw = readJson(manifestPath);
26
+ if (!raw)
27
+ return null;
28
+ const id = (raw.name ?? path.basename(root)).trim();
29
+ if (!id)
30
+ return null;
31
+ const providers = opts.supportsProviders && Array.isArray(raw.providers) ? raw.providers : undefined;
32
+ return {
33
+ id,
34
+ version: raw.version,
35
+ description: raw.description,
36
+ author: parseAuthor(raw.author),
37
+ root,
38
+ manifestPath,
39
+ format: opts.id,
40
+ // Single-format view; the registry widens this to every format present.
41
+ supportPlatform: [opts.id],
42
+ skillsDir: dirIfExists(root, "skills"),
43
+ commandsDir: dirIfExists(root, "commands"),
44
+ agentsDir: dirIfExists(root, "agents"),
45
+ themesDir: dirIfExists(root, "themes"),
46
+ hooks: normalizeHooks(raw.hooks, root),
47
+ mcpServers: normalizeMcp(raw.mcpServers, root),
48
+ providers,
49
+ };
50
+ },
51
+ emit(draft) {
52
+ const files = [];
53
+ const mcpServers = draft.mcpServers?.length
54
+ ? Object.fromEntries(draft.mcpServers.map((s) => [
55
+ s.name,
56
+ { command: s.command, ...(s.args ? { args: s.args } : {}), ...(s.env ? { env: s.env } : {}) },
57
+ ]))
58
+ : undefined;
59
+ files.push({
60
+ path: manifestRelPath,
61
+ content: emitJson({
62
+ name: draft.id,
63
+ ...(draft.version ? { version: draft.version } : {}),
64
+ ...(draft.description ? { description: draft.description } : {}),
65
+ ...(draft.author ? { author: draft.author } : {}),
66
+ ...(mcpServers ? { mcpServers } : {}),
67
+ }),
68
+ });
69
+ for (const s of draft.skills ?? []) {
70
+ files.push({
71
+ path: path.join("skills", slug(s.name), "SKILL.md"),
72
+ content: emitMarkdown({ name: s.name, description: s.description }, s.body),
73
+ });
74
+ }
75
+ for (const c of draft.commands ?? []) {
76
+ files.push({
77
+ path: path.join("commands", `${slug(c.name)}.md`),
78
+ content: emitMarkdown({ description: c.description }, c.body),
79
+ });
80
+ }
81
+ for (const a of draft.agents ?? []) {
82
+ files.push({
83
+ path: path.join("agents", `${slug(a.name)}.md`),
84
+ content: emitMarkdown({ name: a.name, description: a.description, tools: a.tools, model: a.model }, a.body),
85
+ });
86
+ }
87
+ if (draft.hooks?.length) {
88
+ files.push({
89
+ path: path.join("hooks", "hooks.json"),
90
+ content: emitJson({ hooks: authoredHooksToConfig(draft.hooks) }),
91
+ });
92
+ }
93
+ return files;
94
+ },
95
+ };
96
+ }
97
+ //# sourceMappingURL=jsonManifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonManifest.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/jsonManifest.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Factory for the `plugin.json`-style formats (native `.agents-plugin` and\n * Claude Code `.claude-plugin`). They share one on-disk layout — a JSON manifest\n * under a marker directory plus `skills/`, `commands/`, `agents/`, `themes/`,\n * `hooks/hooks.json` — and differ only in the marker directory, the format id,\n * and whether `providers` are honored (native-only). Both adapters are produced\n * from this factory so the shared reader/writer lives in one place.\n */\n\nimport * as path from \"node:path\";\nimport type { NormalizedPlugin, PluginProvider } from \"../manifest.js\";\nimport {\n\tauthoredHooksToConfig,\n\tdirIfExists,\n\temitJson,\n\temitMarkdown,\n\tnormalizeHooks,\n\tnormalizeMcp,\n\tparseAuthor,\n\ttype RawManifest,\n\treadJson,\n\tslug,\n} from \"./shared.js\";\nimport type { EmittedFile, PluginDraft, PluginFormatAdapter, PluginFormatId } from \"./types.js\";\n\nconst MANIFEST_FILE = \"plugin.json\";\n\ninterface JsonManifestOptions {\n\tid: Extract<PluginFormatId, \"agents\" | \"claude\">;\n\t/** Marker subdirectory, e.g. \".agents-plugin\". */\n\tmanifestDir: string;\n\tprecedence: number;\n\tlabel: string;\n\t/** Native honors `providers`; the Claude-compat path ignores them. */\n\tsupportsProviders: boolean;\n}\n\nexport function createJsonManifestAdapter(opts: JsonManifestOptions): PluginFormatAdapter {\n\tconst manifestRelPath = path.join(opts.manifestDir, MANIFEST_FILE);\n\n\treturn {\n\t\tid: opts.id,\n\t\tplatform: opts.id, // \"agents\" | \"claude\" map 1:1 to their platform token\n\t\tprecedence: opts.precedence,\n\t\tlabel: opts.label,\n\t\tmarketplaceFile: path.join(opts.manifestDir, \"marketplace.json\"),\n\n\t\tdetectPlugin(root: string): boolean {\n\t\t\treturn readJson(path.join(root, manifestRelPath)) != null;\n\t\t},\n\n\t\tparsePlugin(root: string): NormalizedPlugin | null {\n\t\t\tconst manifestPath = path.join(root, manifestRelPath);\n\t\t\tconst raw = readJson<RawManifest>(manifestPath);\n\t\t\tif (!raw) return null;\n\n\t\t\tconst id = (raw.name ?? path.basename(root)).trim();\n\t\t\tif (!id) return null;\n\n\t\t\tconst providers: PluginProvider[] | undefined =\n\t\t\t\topts.supportsProviders && Array.isArray(raw.providers) ? (raw.providers as PluginProvider[]) : undefined;\n\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\tversion: raw.version,\n\t\t\t\tdescription: raw.description,\n\t\t\t\tauthor: parseAuthor(raw.author),\n\t\t\t\troot,\n\t\t\t\tmanifestPath,\n\t\t\t\tformat: opts.id,\n\t\t\t\t// Single-format view; the registry widens this to every format present.\n\t\t\t\tsupportPlatform: [opts.id],\n\t\t\t\tskillsDir: dirIfExists(root, \"skills\"),\n\t\t\t\tcommandsDir: dirIfExists(root, \"commands\"),\n\t\t\t\tagentsDir: dirIfExists(root, \"agents\"),\n\t\t\t\tthemesDir: dirIfExists(root, \"themes\"),\n\t\t\t\thooks: normalizeHooks(raw.hooks, root),\n\t\t\t\tmcpServers: normalizeMcp(raw.mcpServers, root),\n\t\t\t\tproviders,\n\t\t\t};\n\t\t},\n\n\t\temit(draft: PluginDraft): EmittedFile[] {\n\t\t\tconst files: EmittedFile[] = [];\n\t\t\tconst mcpServers = draft.mcpServers?.length\n\t\t\t\t? Object.fromEntries(\n\t\t\t\t\t\tdraft.mcpServers.map((s) => [\n\t\t\t\t\t\t\ts.name,\n\t\t\t\t\t\t\t{ command: s.command, ...(s.args ? { args: s.args } : {}), ...(s.env ? { env: s.env } : {}) },\n\t\t\t\t\t\t]),\n\t\t\t\t\t)\n\t\t\t\t: undefined;\n\n\t\t\tfiles.push({\n\t\t\t\tpath: manifestRelPath,\n\t\t\t\tcontent: emitJson({\n\t\t\t\t\tname: draft.id,\n\t\t\t\t\t...(draft.version ? { version: draft.version } : {}),\n\t\t\t\t\t...(draft.description ? { description: draft.description } : {}),\n\t\t\t\t\t...(draft.author ? { author: draft.author } : {}),\n\t\t\t\t\t...(mcpServers ? { mcpServers } : {}),\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tfor (const s of draft.skills ?? []) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"skills\", slug(s.name), \"SKILL.md\"),\n\t\t\t\t\tcontent: emitMarkdown({ name: s.name, description: s.description }, s.body),\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const c of draft.commands ?? []) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"commands\", `${slug(c.name)}.md`),\n\t\t\t\t\tcontent: emitMarkdown({ description: c.description }, c.body),\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const a of draft.agents ?? []) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"agents\", `${slug(a.name)}.md`),\n\t\t\t\t\tcontent: emitMarkdown(\n\t\t\t\t\t\t{ name: a.name, description: a.description, tools: a.tools, model: a.model },\n\t\t\t\t\t\ta.body,\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (draft.hooks?.length) {\n\t\t\t\tfiles.push({\n\t\t\t\t\tpath: path.join(\"hooks\", \"hooks.json\"),\n\t\t\t\t\tcontent: emitJson({ hooks: authoredHooksToConfig(draft.hooks) }),\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn files;\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared, format-agnostic helpers used by the plugin format adapters.
3
+ *
4
+ * Kept deliberately free of any runtime dependency on `../manifest.js` (it only
5
+ * imports *types* from there) so the adapter registry can be wired without a
6
+ * module cycle: `manifest.ts → formats/index.ts → formats/<adapter>.ts →
7
+ * shared.ts`, with the back-reference to manifest types erased at compile time.
8
+ */
9
+ import type { PluginHooksConfig } from "../manifest.js";
10
+ import type { AuthoredHook } from "./types.js";
11
+ /** Raw manifest shape accepted by the `plugin.json`-style formats (agents/claude). */
12
+ export interface RawManifest {
13
+ name?: string;
14
+ version?: string;
15
+ description?: string;
16
+ author?: string | {
17
+ name?: string;
18
+ };
19
+ hooks?: PluginHooksConfig | {
20
+ hooks?: PluginHooksConfig;
21
+ };
22
+ mcpServers?: Record<string, unknown>;
23
+ /** Native-only. */
24
+ providers?: unknown;
25
+ }
26
+ export declare function readJson<T>(file: string): T | null;
27
+ export declare function dirIfExists(root: string, name: string): string | undefined;
28
+ /** Coerce an author field (string or `{ name }`) to a plain string. */
29
+ export declare function parseAuthor(author: RawManifest["author"]): string | undefined;
30
+ /** Accept either `{ hooks: {...} }` or a bare event map, falling back to `hooks/hooks.json`. */
31
+ export declare function normalizeHooks(raw: RawManifest["hooks"], root: string): PluginHooksConfig | undefined;
32
+ /** Read inline `mcpServers`, falling back to a `.mcp.json` file at `root`. */
33
+ export declare function normalizeMcp(raw: Record<string, unknown> | undefined, root: string, mcpFileName?: string): Record<string, unknown> | undefined;
34
+ /** Serialize a small set of frontmatter fields to YAML. Values are strings only. */
35
+ export declare function emitFrontmatter(fields: Record<string, string | undefined>): string;
36
+ /** A markdown capability file: frontmatter block + body. */
37
+ export declare function emitMarkdown(fields: Record<string, string | undefined>, body: string): string;
38
+ /** Pretty-print a JSON manifest with a trailing newline (matches repo convention). */
39
+ export declare function emitJson(value: unknown): string;
40
+ /** Convert authored hooks into the on-disk `hooks.json` event-map shape shared by all formats. */
41
+ export declare function authoredHooksToConfig(hooks: AuthoredHook[]): Record<string, unknown>;
42
+ /** Slug-safe filename component (no path separators, conservative charset). */
43
+ export declare function slug(name: string): string;
44
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/shared.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,KAAK,CAAC,EAAE,iBAAiB,GAAG;QAAE,KAAK,CAAC,EAAE,iBAAiB,CAAA;KAAE,CAAC;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,mBAAmB;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAMlD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED,uEAAuE;AACvE,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,MAAM,GAAG,SAAS,CAE7E;AAED,gGAAgG;AAChG,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAmBrG;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,CAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EACxC,IAAI,EAAE,MAAM,EACZ,WAAW,SAAc,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAUrC;AAMD,oFAAoF;AACpF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAUlF;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7F;AAED,sFAAsF;AACtF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE/C;AAED,kGAAkG;AAClG,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWpF;AAED,+EAA+E;AAC/E,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMzC","sourcesContent":["/**\n * Shared, format-agnostic helpers used by the plugin format adapters.\n *\n * Kept deliberately free of any runtime dependency on `../manifest.js` (it only\n * imports *types* from there) so the adapter registry can be wired without a\n * module cycle: `manifest.ts → formats/index.ts → formats/<adapter>.ts →\n * shared.ts`, with the back-reference to manifest types erased at compile time.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { PluginHooksConfig } from \"../manifest.js\";\nimport type { AuthoredHook } from \"./types.js\";\n\n/** Raw manifest shape accepted by the `plugin.json`-style formats (agents/claude). */\nexport interface RawManifest {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string | { name?: string };\n\thooks?: PluginHooksConfig | { hooks?: PluginHooksConfig };\n\tmcpServers?: Record<string, unknown>;\n\t/** Native-only. */\n\tproviders?: unknown;\n}\n\nexport function readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function dirIfExists(root: string, name: string): string | undefined {\n\tconst p = path.join(root, name);\n\treturn fs.existsSync(p) && fs.statSync(p).isDirectory() ? p : undefined;\n}\n\n/** Coerce an author field (string or `{ name }`) to a plain string. */\nexport function parseAuthor(author: RawManifest[\"author\"]): string | undefined {\n\treturn typeof author === \"string\" ? author : author?.name;\n}\n\n/** Accept either `{ hooks: {...} }` or a bare event map, falling back to `hooks/hooks.json`. */\nexport function normalizeHooks(raw: RawManifest[\"hooks\"], root: string): PluginHooksConfig | undefined {\n\tlet config: PluginHooksConfig | undefined;\n\tif (raw && typeof raw === \"object\") {\n\t\tconfig = \"hooks\" in raw && raw.hooks ? (raw.hooks as PluginHooksConfig) : (raw as PluginHooksConfig);\n\t}\n\t// Fall back to the conventional hooks/hooks.json file.\n\tif (!config) {\n\t\tconst hooksFile = path.join(root, \"hooks\", \"hooks.json\");\n\t\tif (fs.existsSync(hooksFile)) {\n\t\t\tconst fileRaw = readJson<RawManifest[\"hooks\"]>(hooksFile);\n\t\t\tif (fileRaw && typeof fileRaw === \"object\") {\n\t\t\t\tconfig =\n\t\t\t\t\t\"hooks\" in fileRaw && fileRaw.hooks\n\t\t\t\t\t\t? (fileRaw.hooks as PluginHooksConfig)\n\t\t\t\t\t\t: (fileRaw as PluginHooksConfig);\n\t\t\t}\n\t\t}\n\t}\n\treturn config && Object.keys(config).length > 0 ? config : undefined;\n}\n\n/** Read inline `mcpServers`, falling back to a `.mcp.json` file at `root`. */\nexport function normalizeMcp(\n\traw: Record<string, unknown> | undefined,\n\troot: string,\n\tmcpFileName = \".mcp.json\",\n): Record<string, unknown> | undefined {\n\tif (raw && Object.keys(raw).length > 0) return raw;\n\tconst mcpFile = path.join(root, mcpFileName);\n\tif (fs.existsSync(mcpFile)) {\n\t\t// VS Code / Copilot use `{ servers }`; Claude/native use `{ mcpServers }`. Accept both.\n\t\tconst fileRaw = readJson<{ mcpServers?: Record<string, unknown>; servers?: Record<string, unknown> }>(mcpFile);\n\t\tconst servers = fileRaw?.mcpServers ?? fileRaw?.servers;\n\t\tif (servers && Object.keys(servers).length > 0) return servers;\n\t}\n\treturn undefined;\n}\n\n// ============================================================================\n// Emit helpers (used by the writer / scaffolding half of each adapter)\n// ============================================================================\n\n/** Serialize a small set of frontmatter fields to YAML. Values are strings only. */\nexport function emitFrontmatter(fields: Record<string, string | undefined>): string {\n\tconst lines: string[] = [\"---\"];\n\tfor (const [key, value] of Object.entries(fields)) {\n\t\tif (value === undefined || value === \"\") continue;\n\t\t// Quote values that could be misparsed as YAML (contain a colon-space or start punctuation).\n\t\tconst needsQuote = /[:#]|^[\\s>|@`&*!%]/.test(value) || value.includes(\"\\n\");\n\t\tlines.push(`${key}: ${needsQuote ? JSON.stringify(value) : value}`);\n\t}\n\tlines.push(\"---\");\n\treturn lines.join(\"\\n\");\n}\n\n/** A markdown capability file: frontmatter block + body. */\nexport function emitMarkdown(fields: Record<string, string | undefined>, body: string): string {\n\treturn `${emitFrontmatter(fields)}\\n\\n${body.trimEnd()}\\n`;\n}\n\n/** Pretty-print a JSON manifest with a trailing newline (matches repo convention). */\nexport function emitJson(value: unknown): string {\n\treturn `${JSON.stringify(value, null, 2)}\\n`;\n}\n\n/** Convert authored hooks into the on-disk `hooks.json` event-map shape shared by all formats. */\nexport function authoredHooksToConfig(hooks: AuthoredHook[]): Record<string, unknown> {\n\tconst byEvent: Record<string, Array<{ matcher?: string; hooks: unknown[] }>> = {};\n\tfor (const h of hooks) {\n\t\tconst group = byEvent[h.event] ?? [];\n\t\tbyEvent[h.event] = group;\n\t\tgroup.push({\n\t\t\t...(h.matcher ? { matcher: h.matcher } : {}),\n\t\t\thooks: [{ type: \"command\", command: h.command, ...(h.timeout ? { timeout: h.timeout } : {}) }],\n\t\t});\n\t}\n\treturn byEvent;\n}\n\n/** Slug-safe filename component (no path separators, conservative charset). */\nexport function slug(name: string): string {\n\treturn name\n\t\t.trim()\n\t\t.replace(/[^a-zA-Z0-9._-]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\")\n\t\t.slice(0, 80);\n}\n"]}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Shared, format-agnostic helpers used by the plugin format adapters.
3
+ *
4
+ * Kept deliberately free of any runtime dependency on `../manifest.js` (it only
5
+ * imports *types* from there) so the adapter registry can be wired without a
6
+ * module cycle: `manifest.ts → formats/index.ts → formats/<adapter>.ts →
7
+ * shared.ts`, with the back-reference to manifest types erased at compile time.
8
+ */
9
+ import * as fs from "node:fs";
10
+ import * as path from "node:path";
11
+ export function readJson(file) {
12
+ try {
13
+ return JSON.parse(fs.readFileSync(file, "utf8"));
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export function dirIfExists(root, name) {
20
+ const p = path.join(root, name);
21
+ return fs.existsSync(p) && fs.statSync(p).isDirectory() ? p : undefined;
22
+ }
23
+ /** Coerce an author field (string or `{ name }`) to a plain string. */
24
+ export function parseAuthor(author) {
25
+ return typeof author === "string" ? author : author?.name;
26
+ }
27
+ /** Accept either `{ hooks: {...} }` or a bare event map, falling back to `hooks/hooks.json`. */
28
+ export function normalizeHooks(raw, root) {
29
+ let config;
30
+ if (raw && typeof raw === "object") {
31
+ config = "hooks" in raw && raw.hooks ? raw.hooks : raw;
32
+ }
33
+ // Fall back to the conventional hooks/hooks.json file.
34
+ if (!config) {
35
+ const hooksFile = path.join(root, "hooks", "hooks.json");
36
+ if (fs.existsSync(hooksFile)) {
37
+ const fileRaw = readJson(hooksFile);
38
+ if (fileRaw && typeof fileRaw === "object") {
39
+ config =
40
+ "hooks" in fileRaw && fileRaw.hooks
41
+ ? fileRaw.hooks
42
+ : fileRaw;
43
+ }
44
+ }
45
+ }
46
+ return config && Object.keys(config).length > 0 ? config : undefined;
47
+ }
48
+ /** Read inline `mcpServers`, falling back to a `.mcp.json` file at `root`. */
49
+ export function normalizeMcp(raw, root, mcpFileName = ".mcp.json") {
50
+ if (raw && Object.keys(raw).length > 0)
51
+ return raw;
52
+ const mcpFile = path.join(root, mcpFileName);
53
+ if (fs.existsSync(mcpFile)) {
54
+ // VS Code / Copilot use `{ servers }`; Claude/native use `{ mcpServers }`. Accept both.
55
+ const fileRaw = readJson(mcpFile);
56
+ const servers = fileRaw?.mcpServers ?? fileRaw?.servers;
57
+ if (servers && Object.keys(servers).length > 0)
58
+ return servers;
59
+ }
60
+ return undefined;
61
+ }
62
+ // ============================================================================
63
+ // Emit helpers (used by the writer / scaffolding half of each adapter)
64
+ // ============================================================================
65
+ /** Serialize a small set of frontmatter fields to YAML. Values are strings only. */
66
+ export function emitFrontmatter(fields) {
67
+ const lines = ["---"];
68
+ for (const [key, value] of Object.entries(fields)) {
69
+ if (value === undefined || value === "")
70
+ continue;
71
+ // Quote values that could be misparsed as YAML (contain a colon-space or start punctuation).
72
+ const needsQuote = /[:#]|^[\s>|@`&*!%]/.test(value) || value.includes("\n");
73
+ lines.push(`${key}: ${needsQuote ? JSON.stringify(value) : value}`);
74
+ }
75
+ lines.push("---");
76
+ return lines.join("\n");
77
+ }
78
+ /** A markdown capability file: frontmatter block + body. */
79
+ export function emitMarkdown(fields, body) {
80
+ return `${emitFrontmatter(fields)}\n\n${body.trimEnd()}\n`;
81
+ }
82
+ /** Pretty-print a JSON manifest with a trailing newline (matches repo convention). */
83
+ export function emitJson(value) {
84
+ return `${JSON.stringify(value, null, 2)}\n`;
85
+ }
86
+ /** Convert authored hooks into the on-disk `hooks.json` event-map shape shared by all formats. */
87
+ export function authoredHooksToConfig(hooks) {
88
+ const byEvent = {};
89
+ for (const h of hooks) {
90
+ const group = byEvent[h.event] ?? [];
91
+ byEvent[h.event] = group;
92
+ group.push({
93
+ ...(h.matcher ? { matcher: h.matcher } : {}),
94
+ hooks: [{ type: "command", command: h.command, ...(h.timeout ? { timeout: h.timeout } : {}) }],
95
+ });
96
+ }
97
+ return byEvent;
98
+ }
99
+ /** Slug-safe filename component (no path separators, conservative charset). */
100
+ export function slug(name) {
101
+ return name
102
+ .trim()
103
+ .replace(/[^a-zA-Z0-9._-]+/g, "-")
104
+ .replace(/^-+|-+$/g, "")
105
+ .slice(0, 80);
106
+ }
107
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/shared.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shared, format-agnostic helpers used by the plugin format adapters.\n *\n * Kept deliberately free of any runtime dependency on `../manifest.js` (it only\n * imports *types* from there) so the adapter registry can be wired without a\n * module cycle: `manifest.ts → formats/index.ts → formats/<adapter>.ts →\n * shared.ts`, with the back-reference to manifest types erased at compile time.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { PluginHooksConfig } from \"../manifest.js\";\nimport type { AuthoredHook } from \"./types.js\";\n\n/** Raw manifest shape accepted by the `plugin.json`-style formats (agents/claude). */\nexport interface RawManifest {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string | { name?: string };\n\thooks?: PluginHooksConfig | { hooks?: PluginHooksConfig };\n\tmcpServers?: Record<string, unknown>;\n\t/** Native-only. */\n\tproviders?: unknown;\n}\n\nexport function readJson<T>(file: string): T | null {\n\ttry {\n\t\treturn JSON.parse(fs.readFileSync(file, \"utf8\")) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function dirIfExists(root: string, name: string): string | undefined {\n\tconst p = path.join(root, name);\n\treturn fs.existsSync(p) && fs.statSync(p).isDirectory() ? p : undefined;\n}\n\n/** Coerce an author field (string or `{ name }`) to a plain string. */\nexport function parseAuthor(author: RawManifest[\"author\"]): string | undefined {\n\treturn typeof author === \"string\" ? author : author?.name;\n}\n\n/** Accept either `{ hooks: {...} }` or a bare event map, falling back to `hooks/hooks.json`. */\nexport function normalizeHooks(raw: RawManifest[\"hooks\"], root: string): PluginHooksConfig | undefined {\n\tlet config: PluginHooksConfig | undefined;\n\tif (raw && typeof raw === \"object\") {\n\t\tconfig = \"hooks\" in raw && raw.hooks ? (raw.hooks as PluginHooksConfig) : (raw as PluginHooksConfig);\n\t}\n\t// Fall back to the conventional hooks/hooks.json file.\n\tif (!config) {\n\t\tconst hooksFile = path.join(root, \"hooks\", \"hooks.json\");\n\t\tif (fs.existsSync(hooksFile)) {\n\t\t\tconst fileRaw = readJson<RawManifest[\"hooks\"]>(hooksFile);\n\t\t\tif (fileRaw && typeof fileRaw === \"object\") {\n\t\t\t\tconfig =\n\t\t\t\t\t\"hooks\" in fileRaw && fileRaw.hooks\n\t\t\t\t\t\t? (fileRaw.hooks as PluginHooksConfig)\n\t\t\t\t\t\t: (fileRaw as PluginHooksConfig);\n\t\t\t}\n\t\t}\n\t}\n\treturn config && Object.keys(config).length > 0 ? config : undefined;\n}\n\n/** Read inline `mcpServers`, falling back to a `.mcp.json` file at `root`. */\nexport function normalizeMcp(\n\traw: Record<string, unknown> | undefined,\n\troot: string,\n\tmcpFileName = \".mcp.json\",\n): Record<string, unknown> | undefined {\n\tif (raw && Object.keys(raw).length > 0) return raw;\n\tconst mcpFile = path.join(root, mcpFileName);\n\tif (fs.existsSync(mcpFile)) {\n\t\t// VS Code / Copilot use `{ servers }`; Claude/native use `{ mcpServers }`. Accept both.\n\t\tconst fileRaw = readJson<{ mcpServers?: Record<string, unknown>; servers?: Record<string, unknown> }>(mcpFile);\n\t\tconst servers = fileRaw?.mcpServers ?? fileRaw?.servers;\n\t\tif (servers && Object.keys(servers).length > 0) return servers;\n\t}\n\treturn undefined;\n}\n\n// ============================================================================\n// Emit helpers (used by the writer / scaffolding half of each adapter)\n// ============================================================================\n\n/** Serialize a small set of frontmatter fields to YAML. Values are strings only. */\nexport function emitFrontmatter(fields: Record<string, string | undefined>): string {\n\tconst lines: string[] = [\"---\"];\n\tfor (const [key, value] of Object.entries(fields)) {\n\t\tif (value === undefined || value === \"\") continue;\n\t\t// Quote values that could be misparsed as YAML (contain a colon-space or start punctuation).\n\t\tconst needsQuote = /[:#]|^[\\s>|@`&*!%]/.test(value) || value.includes(\"\\n\");\n\t\tlines.push(`${key}: ${needsQuote ? JSON.stringify(value) : value}`);\n\t}\n\tlines.push(\"---\");\n\treturn lines.join(\"\\n\");\n}\n\n/** A markdown capability file: frontmatter block + body. */\nexport function emitMarkdown(fields: Record<string, string | undefined>, body: string): string {\n\treturn `${emitFrontmatter(fields)}\\n\\n${body.trimEnd()}\\n`;\n}\n\n/** Pretty-print a JSON manifest with a trailing newline (matches repo convention). */\nexport function emitJson(value: unknown): string {\n\treturn `${JSON.stringify(value, null, 2)}\\n`;\n}\n\n/** Convert authored hooks into the on-disk `hooks.json` event-map shape shared by all formats. */\nexport function authoredHooksToConfig(hooks: AuthoredHook[]): Record<string, unknown> {\n\tconst byEvent: Record<string, Array<{ matcher?: string; hooks: unknown[] }>> = {};\n\tfor (const h of hooks) {\n\t\tconst group = byEvent[h.event] ?? [];\n\t\tbyEvent[h.event] = group;\n\t\tgroup.push({\n\t\t\t...(h.matcher ? { matcher: h.matcher } : {}),\n\t\t\thooks: [{ type: \"command\", command: h.command, ...(h.timeout ? { timeout: h.timeout } : {}) }],\n\t\t});\n\t}\n\treturn byEvent;\n}\n\n/** Slug-safe filename component (no path separators, conservative charset). */\nexport function slug(name: string): string {\n\treturn name\n\t\t.trim()\n\t\t.replace(/[^a-zA-Z0-9._-]+/g, \"-\")\n\t\t.replace(/^-+|-+$/g, \"\")\n\t\t.slice(0, 80);\n}\n"]}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Plugin format adapter contract.
3
+ *
4
+ * A `PluginFormatAdapter` owns *everything* format-specific for one vendor
5
+ * layout — how a plugin directory is detected and parsed (the reader), how a
6
+ * marketplace index file is located, and how a plugin is scaffolded from a
7
+ * normalized draft (the writer). The rest of the plugin subsystem is written
8
+ * against {@link NormalizedPlugin} / {@link PluginDraft} and never branches on a
9
+ * format again.
10
+ *
11
+ * This is the extension point the plugin system is designed around: Claude Code
12
+ * and GitHub Copilot conventions evolve independently, so each lives in its own
13
+ * adapter file ({@link ./claude.ts}, {@link ./copilot.ts}, {@link ./agents.ts}).
14
+ * Supporting a new vendor — or tracking a change in an existing one — means
15
+ * editing (or adding) exactly one adapter and registering it; no other module
16
+ * changes.
17
+ */
18
+ import type { NormalizedPlugin } from "../manifest.js";
19
+ /** Internal format id — matches `NormalizedPlugin.format` / `NormalizedMarketplace.format`. */
20
+ export type PluginFormatId = "agents" | "claude" | "copilot";
21
+ /**
22
+ * Public platform token surfaced to users. The Copilot format lives under
23
+ * `.github/`, so it is surfaced as `"github"` rather than the internal
24
+ * `"copilot"`.
25
+ */
26
+ export type MarketplacePlatform = "agents" | "claude" | "github";
27
+ /** A file produced by a format's scaffolder. `path` is relative to the plugin root (POSIX). */
28
+ export interface EmittedFile {
29
+ path: string;
30
+ content: string;
31
+ }
32
+ /** An authored skill: passive instructions loaded lazily (SKILL.md-style). */
33
+ export interface AuthoredSkill {
34
+ name: string;
35
+ description?: string;
36
+ /** Instruction body (markdown). */
37
+ body: string;
38
+ }
39
+ /** An authored slash command: a passive prompt template. */
40
+ export interface AuthoredCommand {
41
+ name: string;
42
+ description?: string;
43
+ /** Prompt template body (markdown). */
44
+ body: string;
45
+ }
46
+ /** An authored subagent definition. `tools` is a comma-separated allowlist (Claude Code convention). */
47
+ export interface AuthoredAgent {
48
+ name: string;
49
+ description?: string;
50
+ /** Comma-separated `allowed-tools` string, e.g. "read, grep, glob". */
51
+ tools?: string;
52
+ model?: string;
53
+ /** System-prompt / instruction body (markdown). */
54
+ body: string;
55
+ }
56
+ /** An authored shell hook wired to a tool event. Active code — gated on authoring. */
57
+ export interface AuthoredHook {
58
+ /** Claude Code event name: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ... */
59
+ event: string;
60
+ /** Regex matched against the tool name. Empty / "*" matches everything. */
61
+ matcher?: string;
62
+ command: string;
63
+ /** Timeout in seconds. */
64
+ timeout?: number;
65
+ }
66
+ /** An authored MCP server. Active code — gated on authoring. */
67
+ export interface AuthoredMcpServer {
68
+ name: string;
69
+ command: string;
70
+ args?: string[];
71
+ env?: Record<string, string>;
72
+ }
73
+ /**
74
+ * Format-agnostic authoring spec consumed by {@link PluginFormatAdapter.emit}.
75
+ * `ProposePlugin` builds one of these and asks each requested adapter to render
76
+ * it, so the same authored capability lands correctly in Claude Code and
77
+ * Copilot layouts without the authoring code knowing either.
78
+ */
79
+ export interface PluginDraft {
80
+ id: string;
81
+ version?: string;
82
+ description?: string;
83
+ author?: string;
84
+ /**
85
+ * Which platform layouts to scaffold this draft into (e.g. `["claude",
86
+ * "github"]`). Consumed as the default target set by `emitForPlatforms`;
87
+ * omit to render every registered format.
88
+ */
89
+ supportPlatform?: MarketplacePlatform[];
90
+ skills?: AuthoredSkill[];
91
+ commands?: AuthoredCommand[];
92
+ agents?: AuthoredAgent[];
93
+ hooks?: AuthoredHook[];
94
+ mcpServers?: AuthoredMcpServer[];
95
+ }
96
+ /** Reader + writer for a single plugin format. */
97
+ export interface PluginFormatAdapter {
98
+ /** Internal format id (stable; used in `NormalizedPlugin.format`). */
99
+ readonly id: PluginFormatId;
100
+ /** Public platform token (Copilot → `"github"`). */
101
+ readonly platform: MarketplacePlatform;
102
+ /**
103
+ * Precedence when several formats coexist in one directory (lower wins).
104
+ * Native `.agents-plugin` beats Claude beats Copilot.
105
+ */
106
+ readonly precedence: number;
107
+ /** Short human label for messages and scaffolding summaries. */
108
+ readonly label: string;
109
+ /** Location of this format's marketplace index file, relative to a marketplace root (POSIX). */
110
+ readonly marketplaceFile: string;
111
+ /** True if `root` carries this format's plugin manifest. */
112
+ detectPlugin(root: string): boolean;
113
+ /** Parse `root` as a plugin of this format. Returns null when unparseable. */
114
+ parsePlugin(root: string): NormalizedPlugin | null;
115
+ /** Render `draft` into this format's on-disk layout (paths relative to the plugin root). */
116
+ emit(draft: PluginDraft): EmittedFile[];
117
+ }
118
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/types.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin format adapter contract.\n *\n * A `PluginFormatAdapter` owns *everything* format-specific for one vendor\n * layout — how a plugin directory is detected and parsed (the reader), how a\n * marketplace index file is located, and how a plugin is scaffolded from a\n * normalized draft (the writer). The rest of the plugin subsystem is written\n * against {@link NormalizedPlugin} / {@link PluginDraft} and never branches on a\n * format again.\n *\n * This is the extension point the plugin system is designed around: Claude Code\n * and GitHub Copilot conventions evolve independently, so each lives in its own\n * adapter file ({@link ./claude.ts}, {@link ./copilot.ts}, {@link ./agents.ts}).\n * Supporting a new vendor — or tracking a change in an existing one — means\n * editing (or adding) exactly one adapter and registering it; no other module\n * changes.\n */\n\nimport type { NormalizedPlugin } from \"../manifest.js\";\n\n/** Internal format id — matches `NormalizedPlugin.format` / `NormalizedMarketplace.format`. */\nexport type PluginFormatId = \"agents\" | \"claude\" | \"copilot\";\n\n/**\n * Public platform token surfaced to users. The Copilot format lives under\n * `.github/`, so it is surfaced as `\"github\"` rather than the internal\n * `\"copilot\"`.\n */\nexport type MarketplacePlatform = \"agents\" | \"claude\" | \"github\";\n\n/** A file produced by a format's scaffolder. `path` is relative to the plugin root (POSIX). */\nexport interface EmittedFile {\n\tpath: string;\n\tcontent: string;\n}\n\n/** An authored skill: passive instructions loaded lazily (SKILL.md-style). */\nexport interface AuthoredSkill {\n\tname: string;\n\tdescription?: string;\n\t/** Instruction body (markdown). */\n\tbody: string;\n}\n\n/** An authored slash command: a passive prompt template. */\nexport interface AuthoredCommand {\n\tname: string;\n\tdescription?: string;\n\t/** Prompt template body (markdown). */\n\tbody: string;\n}\n\n/** An authored subagent definition. `tools` is a comma-separated allowlist (Claude Code convention). */\nexport interface AuthoredAgent {\n\tname: string;\n\tdescription?: string;\n\t/** Comma-separated `allowed-tools` string, e.g. \"read, grep, glob\". */\n\ttools?: string;\n\tmodel?: string;\n\t/** System-prompt / instruction body (markdown). */\n\tbody: string;\n}\n\n/** An authored shell hook wired to a tool event. Active code — gated on authoring. */\nexport interface AuthoredHook {\n\t/** Claude Code event name: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ... */\n\tevent: string;\n\t/** Regex matched against the tool name. Empty / \"*\" matches everything. */\n\tmatcher?: string;\n\tcommand: string;\n\t/** Timeout in seconds. */\n\ttimeout?: number;\n}\n\n/** An authored MCP server. Active code — gated on authoring. */\nexport interface AuthoredMcpServer {\n\tname: string;\n\tcommand: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n}\n\n/**\n * Format-agnostic authoring spec consumed by {@link PluginFormatAdapter.emit}.\n * `ProposePlugin` builds one of these and asks each requested adapter to render\n * it, so the same authored capability lands correctly in Claude Code and\n * Copilot layouts without the authoring code knowing either.\n */\nexport interface PluginDraft {\n\tid: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string;\n\t/**\n\t * Which platform layouts to scaffold this draft into (e.g. `[\"claude\",\n\t * \"github\"]`). Consumed as the default target set by `emitForPlatforms`;\n\t * omit to render every registered format.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n\tskills?: AuthoredSkill[];\n\tcommands?: AuthoredCommand[];\n\tagents?: AuthoredAgent[];\n\thooks?: AuthoredHook[];\n\tmcpServers?: AuthoredMcpServer[];\n}\n\n/** Reader + writer for a single plugin format. */\nexport interface PluginFormatAdapter {\n\t/** Internal format id (stable; used in `NormalizedPlugin.format`). */\n\treadonly id: PluginFormatId;\n\t/** Public platform token (Copilot → `\"github\"`). */\n\treadonly platform: MarketplacePlatform;\n\t/**\n\t * Precedence when several formats coexist in one directory (lower wins).\n\t * Native `.agents-plugin` beats Claude beats Copilot.\n\t */\n\treadonly precedence: number;\n\t/** Short human label for messages and scaffolding summaries. */\n\treadonly label: string;\n\t/** Location of this format's marketplace index file, relative to a marketplace root (POSIX). */\n\treadonly marketplaceFile: string;\n\n\t/** True if `root` carries this format's plugin manifest. */\n\tdetectPlugin(root: string): boolean;\n\t/** Parse `root` as a plugin of this format. Returns null when unparseable. */\n\tparsePlugin(root: string): NormalizedPlugin | null;\n\t/** Render `draft` into this format's on-disk layout (paths relative to the plugin root). */\n\temit(draft: PluginDraft): EmittedFile[];\n}\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Plugin format adapter contract.
3
+ *
4
+ * A `PluginFormatAdapter` owns *everything* format-specific for one vendor
5
+ * layout — how a plugin directory is detected and parsed (the reader), how a
6
+ * marketplace index file is located, and how a plugin is scaffolded from a
7
+ * normalized draft (the writer). The rest of the plugin subsystem is written
8
+ * against {@link NormalizedPlugin} / {@link PluginDraft} and never branches on a
9
+ * format again.
10
+ *
11
+ * This is the extension point the plugin system is designed around: Claude Code
12
+ * and GitHub Copilot conventions evolve independently, so each lives in its own
13
+ * adapter file ({@link ./claude.ts}, {@link ./copilot.ts}, {@link ./agents.ts}).
14
+ * Supporting a new vendor — or tracking a change in an existing one — means
15
+ * editing (or adding) exactly one adapter and registering it; no other module
16
+ * changes.
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG","sourcesContent":["/**\n * Plugin format adapter contract.\n *\n * A `PluginFormatAdapter` owns *everything* format-specific for one vendor\n * layout — how a plugin directory is detected and parsed (the reader), how a\n * marketplace index file is located, and how a plugin is scaffolded from a\n * normalized draft (the writer). The rest of the plugin subsystem is written\n * against {@link NormalizedPlugin} / {@link PluginDraft} and never branches on a\n * format again.\n *\n * This is the extension point the plugin system is designed around: Claude Code\n * and GitHub Copilot conventions evolve independently, so each lives in its own\n * adapter file ({@link ./claude.ts}, {@link ./copilot.ts}, {@link ./agents.ts}).\n * Supporting a new vendor — or tracking a change in an existing one — means\n * editing (or adding) exactly one adapter and registering it; no other module\n * changes.\n */\n\nimport type { NormalizedPlugin } from \"../manifest.js\";\n\n/** Internal format id — matches `NormalizedPlugin.format` / `NormalizedMarketplace.format`. */\nexport type PluginFormatId = \"agents\" | \"claude\" | \"copilot\";\n\n/**\n * Public platform token surfaced to users. The Copilot format lives under\n * `.github/`, so it is surfaced as `\"github\"` rather than the internal\n * `\"copilot\"`.\n */\nexport type MarketplacePlatform = \"agents\" | \"claude\" | \"github\";\n\n/** A file produced by a format's scaffolder. `path` is relative to the plugin root (POSIX). */\nexport interface EmittedFile {\n\tpath: string;\n\tcontent: string;\n}\n\n/** An authored skill: passive instructions loaded lazily (SKILL.md-style). */\nexport interface AuthoredSkill {\n\tname: string;\n\tdescription?: string;\n\t/** Instruction body (markdown). */\n\tbody: string;\n}\n\n/** An authored slash command: a passive prompt template. */\nexport interface AuthoredCommand {\n\tname: string;\n\tdescription?: string;\n\t/** Prompt template body (markdown). */\n\tbody: string;\n}\n\n/** An authored subagent definition. `tools` is a comma-separated allowlist (Claude Code convention). */\nexport interface AuthoredAgent {\n\tname: string;\n\tdescription?: string;\n\t/** Comma-separated `allowed-tools` string, e.g. \"read, grep, glob\". */\n\ttools?: string;\n\tmodel?: string;\n\t/** System-prompt / instruction body (markdown). */\n\tbody: string;\n}\n\n/** An authored shell hook wired to a tool event. Active code — gated on authoring. */\nexport interface AuthoredHook {\n\t/** Claude Code event name: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop, ... */\n\tevent: string;\n\t/** Regex matched against the tool name. Empty / \"*\" matches everything. */\n\tmatcher?: string;\n\tcommand: string;\n\t/** Timeout in seconds. */\n\ttimeout?: number;\n}\n\n/** An authored MCP server. Active code — gated on authoring. */\nexport interface AuthoredMcpServer {\n\tname: string;\n\tcommand: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n}\n\n/**\n * Format-agnostic authoring spec consumed by {@link PluginFormatAdapter.emit}.\n * `ProposePlugin` builds one of these and asks each requested adapter to render\n * it, so the same authored capability lands correctly in Claude Code and\n * Copilot layouts without the authoring code knowing either.\n */\nexport interface PluginDraft {\n\tid: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string;\n\t/**\n\t * Which platform layouts to scaffold this draft into (e.g. `[\"claude\",\n\t * \"github\"]`). Consumed as the default target set by `emitForPlatforms`;\n\t * omit to render every registered format.\n\t */\n\tsupportPlatform?: MarketplacePlatform[];\n\tskills?: AuthoredSkill[];\n\tcommands?: AuthoredCommand[];\n\tagents?: AuthoredAgent[];\n\thooks?: AuthoredHook[];\n\tmcpServers?: AuthoredMcpServer[];\n}\n\n/** Reader + writer for a single plugin format. */\nexport interface PluginFormatAdapter {\n\t/** Internal format id (stable; used in `NormalizedPlugin.format`). */\n\treadonly id: PluginFormatId;\n\t/** Public platform token (Copilot → `\"github\"`). */\n\treadonly platform: MarketplacePlatform;\n\t/**\n\t * Precedence when several formats coexist in one directory (lower wins).\n\t * Native `.agents-plugin` beats Claude beats Copilot.\n\t */\n\treadonly precedence: number;\n\t/** Short human label for messages and scaffolding summaries. */\n\treadonly label: string;\n\t/** Location of this format's marketplace index file, relative to a marketplace root (POSIX). */\n\treadonly marketplaceFile: string;\n\n\t/** True if `root` carries this format's plugin manifest. */\n\tdetectPlugin(root: string): boolean;\n\t/** Parse `root` as a plugin of this format. Returns null when unparseable. */\n\tparsePlugin(root: string): NormalizedPlugin | null;\n\t/** Render `draft` into this format's on-disk layout (paths relative to the plugin root). */\n\temit(draft: PluginDraft): EmittedFile[];\n}\n"]}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Plugin install/uninstall/discovery — the shared engine behind both the
3
+ * `/plugin` slash command (human-driven) and the model-facing lifecycle tools
4
+ * (`SearchPlugins`, `InstallPlugin`, ...). Keeping it in one place means the two
5
+ * surfaces can never drift on where plugins live or how sources resolve.
6
+ *
7
+ * Trust model (see docs/plugin-system-spec.md): *adding* a marketplace is the
8
+ * human trust boundary; *installing* from an already-trusted marketplace is the
9
+ * model's discretion (package-manager model), and must stay transparent and
10
+ * reversible. This module performs the mechanical install/remove; the gating
11
+ * (announce, injection carve-out) lives with the callers.
12
+ */
13
+ import type { MarketplacePlatform } from "./formats/types.js";
14
+ import type { NormalizedPlugin } from "./manifest.js";
15
+ import { type MarketplaceRecord } from "./marketplace.js";
16
+ /** `.agents/` is the primary, cross-vendor home for installed plugins and the added-marketplace registry. */
17
+ export declare function installedPluginsDir(cwd: string): string;
18
+ /** Filesystem-safe directory name derived from a plugin name (matches the `/plugin` command). */
19
+ export declare function sanitizeForDir(s: string): string;
20
+ /** Absolute path to the curated default marketplace bundled with hoocode. */
21
+ export declare function defaultMarketplaceDir(): string;
22
+ /** The default marketplace record — always present so source-level trust is meaningful out of the box. */
23
+ export declare function defaultMarketplaceRecord(): MarketplaceRecord;
24
+ /**
25
+ * All marketplace records in effect: the bundled default first (curated,
26
+ * trusted), then user-added ones from `.agents/` (falling back to legacy
27
+ * `.hoocode/`). Deduplicated by directory.
28
+ */
29
+ export declare function readMarketplaceRecords(cwd: string): MarketplaceRecord[];
30
+ /** A plugin offered by some registered marketplace (not necessarily installed). */
31
+ export interface AvailablePlugin {
32
+ name: string;
33
+ description?: string;
34
+ /** Relative path, git URL, or `npm:<spec>`. */
35
+ source: string;
36
+ /** Resolved source kind, for display and gating. */
37
+ sourceKind: "local" | "git" | "npm";
38
+ marketplaceName: string;
39
+ marketplaceRoot: string;
40
+ /** Platforms this entry targets (per-entry hint, else the marketplace's). */
41
+ supportPlatform: MarketplacePlatform[];
42
+ }
43
+ /** Every plugin offered across all registered marketplaces (first marketplace wins on name clash). */
44
+ export declare function listAvailablePlugins(cwd: string): AvailablePlugin[];
45
+ /** Find a single available plugin by exact name. */
46
+ export declare function findAvailablePlugin(cwd: string, name: string): AvailablePlugin | undefined;
47
+ /** All currently installed plugins (project + global plugin dirs). */
48
+ export declare function listInstalledPlugins(cwd: string, agentDir?: string): NormalizedPlugin[];
49
+ /** Whether a plugin with `name` (by id) is already installed. */
50
+ export declare function isPluginInstalled(cwd: string, name: string, agentDir?: string): boolean;
51
+ export interface InstallOutcome {
52
+ installed: boolean;
53
+ /** Install destination directory (when installed). */
54
+ dest?: string;
55
+ /** Platforms the installed plugin supports (read back from disk). */
56
+ supportPlatform?: MarketplacePlatform[];
57
+ /** Human-readable summary suitable for a tool result or notification. */
58
+ message: string;
59
+ }
60
+ /**
61
+ * Install an available plugin by name into `.agents/plugins`. Copies local
62
+ * sources; clones git sources. Transparent + reversible by construction — the
63
+ * plugin lands in a named directory and {@link uninstallPlugin} removes it.
64
+ * Active after the next reload.
65
+ */
66
+ export declare function installAvailablePlugin(cwd: string, name: string): Promise<InstallOutcome>;
67
+ export interface UninstallOutcome {
68
+ removed: boolean;
69
+ message: string;
70
+ }
71
+ /** Remove an installed plugin from `.agents/plugins` (and the legacy `.hoocode/plugins`). */
72
+ export declare function uninstallPlugin(cwd: string, name: string): UninstallOutcome;
73
+ //# sourceMappingURL=install.d.ts.map