@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.
- package/CHANGELOG.md +14 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/plugins/authoring.d.ts +51 -0
- package/dist/core/extensions/plugins/authoring.d.ts.map +1 -0
- package/dist/core/extensions/plugins/authoring.js +91 -0
- package/dist/core/extensions/plugins/authoring.js.map +1 -0
- package/dist/core/extensions/plugins/default-marketplace/.agents-plugin/marketplace.json +12 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/.agents-plugin/plugin.json +6 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/commands/hello.md +5 -0
- package/dist/core/extensions/plugins/default-marketplace/plugins/hello-world/skills/greet/SKILL.md +7 -0
- package/dist/core/extensions/plugins/formats/agents.d.ts +7 -0
- package/dist/core/extensions/plugins/formats/agents.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/agents.js +14 -0
- package/dist/core/extensions/plugins/formats/agents.js.map +1 -0
- package/dist/core/extensions/plugins/formats/claude.d.ts +10 -0
- package/dist/core/extensions/plugins/formats/claude.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/claude.js +17 -0
- package/dist/core/extensions/plugins/formats/claude.js.map +1 -0
- package/dist/core/extensions/plugins/formats/copilot.d.ts +23 -0
- package/dist/core/extensions/plugins/formats/copilot.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/copilot.js +130 -0
- package/dist/core/extensions/plugins/formats/copilot.js.map +1 -0
- package/dist/core/extensions/plugins/formats/index.d.ts +41 -0
- package/dist/core/extensions/plugins/formats/index.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/index.js +76 -0
- package/dist/core/extensions/plugins/formats/index.js.map +1 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts +21 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.js +97 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.js.map +1 -0
- package/dist/core/extensions/plugins/formats/shared.d.ts +44 -0
- package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/shared.js +107 -0
- package/dist/core/extensions/plugins/formats/shared.js.map +1 -0
- package/dist/core/extensions/plugins/formats/types.d.ts +118 -0
- package/dist/core/extensions/plugins/formats/types.d.ts.map +1 -0
- package/dist/core/extensions/plugins/formats/types.js +19 -0
- package/dist/core/extensions/plugins/formats/types.js.map +1 -0
- package/dist/core/extensions/plugins/install.d.ts +73 -0
- package/dist/core/extensions/plugins/install.d.ts.map +1 -0
- package/dist/core/extensions/plugins/install.js +173 -0
- package/dist/core/extensions/plugins/install.js.map +1 -0
- package/dist/core/extensions/plugins/manifest.d.ts +25 -7
- package/dist/core/extensions/plugins/manifest.d.ts.map +1 -1
- package/dist/core/extensions/plugins/manifest.js +16 -91
- package/dist/core/extensions/plugins/manifest.js.map +1 -1
- package/dist/core/extensions/plugins/marketplace.d.ts +5 -4
- package/dist/core/extensions/plugins/marketplace.d.ts.map +1 -1
- package/dist/core/extensions/plugins/marketplace.js +8 -13
- package/dist/core/extensions/plugins/marketplace.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +2 -0
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +2 -0
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +16 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +2 -0
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/subagent-depth.d.ts +13 -0
- package/dist/core/subagent-depth.d.ts.map +1 -1
- package/dist/core/subagent-depth.js +15 -0
- package/dist/core/subagent-depth.js.map +1 -1
- package/dist/core/subagent-pool.d.ts.map +1 -1
- package/dist/core/subagent-pool.js +4 -1
- package/dist/core/subagent-pool.js.map +1 -1
- package/dist/core/tools/find.d.ts +8 -3
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +189 -122
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/index.d.ts +0 -4
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +2 -5
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/plugin-tool-names.d.ts +21 -0
- package/dist/core/tools/plugin-tool-names.d.ts.map +1 -0
- package/dist/core/tools/plugin-tool-names.js +31 -0
- package/dist/core/tools/plugin-tool-names.js.map +1 -0
- package/dist/core/tools/plugins.d.ts +49 -0
- package/dist/core/tools/plugins.d.ts.map +1 -0
- package/dist/core/tools/plugins.js +186 -0
- package/dist/core/tools/plugins.js.map +1 -0
- package/dist/core/tools/propose-plugin.d.ts +36 -0
- package/dist/core/tools/propose-plugin.d.ts.map +1 -0
- package/dist/core/tools/propose-plugin.js +219 -0
- package/dist/core/tools/propose-plugin.js.map +1 -0
- package/dist/core/warm-subagent-pool.d.ts.map +1 -1
- package/dist/core/warm-subagent-pool.js +4 -1
- package/dist/core/warm-subagent-pool.js.map +1 -1
- package/dist/extensions/core/marketplace.d.ts +7 -3
- package/dist/extensions/core/marketplace.d.ts.map +1 -1
- package/dist/extensions/core/marketplace.js +23 -67
- package/dist/extensions/core/marketplace.js.map +1 -1
- package/dist/extensions/core/mcp-deferred.d.ts +28 -0
- package/dist/extensions/core/mcp-deferred.d.ts.map +1 -0
- package/dist/extensions/core/mcp-deferred.js +53 -0
- package/dist/extensions/core/mcp-deferred.js.map +1 -0
- package/dist/extensions/core/mcp-loader.d.ts.map +1 -1
- package/dist/extensions/core/mcp-loader.js +132 -78
- package/dist/extensions/core/mcp-loader.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -1
- package/dist/main.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +5 -5
- package/dist/core/tools/glob.d.ts +0 -42
- package/dist/core/tools/glob.d.ts.map +0 -1
- package/dist/core/tools/glob.js +0 -432
- package/dist/core/tools/glob.js.map +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin authoring engine (spec §3) — shared by the two ProposePlugin tools.
|
|
3
|
+
*
|
|
4
|
+
* Completes the discover → acquire → author spectrum: when no marketplace plugin
|
|
5
|
+
* fits a gap, the model can scaffold one. Authoring is gated on the *content /
|
|
6
|
+
* capability-grant* trust axis (what the plugin can do), not the *source* axis
|
|
7
|
+
* used for install. This module carries the risk classification, the
|
|
8
|
+
* privilege-amplification guardrail, and the file writer; the tools own the two
|
|
9
|
+
* escalating-risk *paths* (autonomous scaffold vs. confirm-then-activate).
|
|
10
|
+
*
|
|
11
|
+
* Everything is written through the format registry's {@link emitForPlatforms},
|
|
12
|
+
* so an authored plugin lands in the requested vendor layouts (Claude Code and
|
|
13
|
+
* GitHub Copilot by default) and round-trips back through {@link parsePluginDir}.
|
|
14
|
+
*/
|
|
15
|
+
import type { MarketplacePlatform, PluginDraft } from "./formats/types.js";
|
|
16
|
+
import { type NormalizedPlugin } from "./manifest.js";
|
|
17
|
+
/** Default authoring targets: the two external vendor formats the model can author into. */
|
|
18
|
+
export declare const DEFAULT_AUTHORING_PLATFORMS: MarketplacePlatform[];
|
|
19
|
+
export interface AllowlistClassification {
|
|
20
|
+
/** read-only grants are as safe as a skill; mutating/exec/network/`*` grants need confirmation. */
|
|
21
|
+
risk: "read-only" | "mutating";
|
|
22
|
+
/** Human-readable explanation of what drove the classification. */
|
|
23
|
+
reason: string;
|
|
24
|
+
/** Any plugin-system (capability-acquisition) tools found — always forbidden in an authored allowlist. */
|
|
25
|
+
pluginTools: string[];
|
|
26
|
+
/** The raw allowlist tokens. */
|
|
27
|
+
tokens: string[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Classify an authored subagent `tools:` allowlist as read-only vs. mutating,
|
|
31
|
+
* reusing the same Claude-alias vocabulary as the agent-frontmatter normalizer
|
|
32
|
+
* (spec §3 "compute the risk, don't guess it"). Anything unrecognized — an MCP
|
|
33
|
+
* tool, a bare `*`, an unknown name — is treated as mutating (fail-safe).
|
|
34
|
+
*/
|
|
35
|
+
export declare function classifyAllowlist(tools: string | undefined): AllowlistClassification;
|
|
36
|
+
export interface WriteResult {
|
|
37
|
+
dest: string;
|
|
38
|
+
/** Written file paths, relative to the plugin root. */
|
|
39
|
+
files: string[];
|
|
40
|
+
/** Re-parsed plugin (confirms the scaffold round-trips). */
|
|
41
|
+
plugin: NormalizedPlugin | null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render `draft` into the requested platform layouts and write it under
|
|
45
|
+
* `.agents/plugins/<id>/`. Returns the destination, the emitted files, and the
|
|
46
|
+
* re-parsed plugin so callers can confirm the round-trip.
|
|
47
|
+
*/
|
|
48
|
+
export declare function writePluginDraft(cwd: string, draft: PluginDraft, platforms?: MarketplacePlatform[]): WriteResult;
|
|
49
|
+
/** Whether a plugin id already exists on disk (so authoring never silently clobbers). */
|
|
50
|
+
export declare function pluginExists(cwd: string, id: string): boolean;
|
|
51
|
+
//# sourceMappingURL=authoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authoring.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/authoring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,EAAE,KAAK,gBAAgB,EAAkB,MAAM,eAAe,CAAC;AAEtE,4FAA4F;AAC5F,eAAO,MAAM,2BAA2B,EAAE,mBAAmB,EAAyB,CAAC;AAKvF,MAAM,WAAW,uBAAuB;IACvC,mGAAmG;IACnG,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IAC/B,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,4GAA0G;IAC1G,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gCAAgC;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,uBAAuB,CAuCpF;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4DAA4D;IAC5D,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,mBAAmB,EAAE,GAAG,WAAW,CAahH;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAE7D","sourcesContent":["/**\n * Plugin authoring engine (spec §3) — shared by the two ProposePlugin tools.\n *\n * Completes the discover → acquire → author spectrum: when no marketplace plugin\n * fits a gap, the model can scaffold one. Authoring is gated on the *content /\n * capability-grant* trust axis (what the plugin can do), not the *source* axis\n * used for install. This module carries the risk classification, the\n * privilege-amplification guardrail, and the file writer; the tools own the two\n * escalating-risk *paths* (autonomous scaffold vs. confirm-then-activate).\n *\n * Everything is written through the format registry's {@link emitForPlatforms},\n * so an authored plugin lands in the requested vendor layouts (Claude Code and\n * GitHub Copilot by default) and round-trips back through {@link parsePluginDir}.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { CLAUDE_TOOL_ALIASES } from \"../../agent-frontmatter.js\";\nimport { PLUGIN_SYSTEM_TOOL_NAMES } from \"../../tools/plugin-tool-names.js\";\nimport { emitForPlatforms } from \"./formats/index.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"./formats/types.js\";\nimport { installedPluginsDir, sanitizeForDir } from \"./install.js\";\nimport { type NormalizedPlugin, parsePluginDir } from \"./manifest.js\";\n\n/** Default authoring targets: the two external vendor formats the model can author into. */\nexport const DEFAULT_AUTHORING_PLATFORMS: MarketplacePlatform[] = [\"claude\", \"github\"];\n\n/** hoocode tool names that only read (no mutation, no exec). Grants limited to these are low-risk. */\nconst READONLY_TOOLS = new Set([\"read\", \"grep\", \"find\", \"ls\", \"webfetch\", \"websearch\"]);\n\nexport interface AllowlistClassification {\n\t/** read-only grants are as safe as a skill; mutating/exec/network/`*` grants need confirmation. */\n\trisk: \"read-only\" | \"mutating\";\n\t/** Human-readable explanation of what drove the classification. */\n\treason: string;\n\t/** Any plugin-system (capability-acquisition) tools found — always forbidden in an authored allowlist. */\n\tpluginTools: string[];\n\t/** The raw allowlist tokens. */\n\ttokens: string[];\n}\n\n/**\n * Classify an authored subagent `tools:` allowlist as read-only vs. mutating,\n * reusing the same Claude-alias vocabulary as the agent-frontmatter normalizer\n * (spec §3 \"compute the risk, don't guess it\"). Anything unrecognized — an MCP\n * tool, a bare `*`, an unknown name — is treated as mutating (fail-safe).\n */\nexport function classifyAllowlist(tools: string | undefined): AllowlistClassification {\n\tconst tokens = (tools ?? \"\")\n\t\t.split(/[,\\s]+/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\tconst pluginTools = tokens.filter((t) => PLUGIN_SYSTEM_TOOL_NAMES.some((n) => n.toLowerCase() === t.toLowerCase()));\n\n\tif (tokens.length === 0) {\n\t\treturn { risk: \"read-only\", reason: \"no tools granted\", pluginTools, tokens };\n\t}\n\n\tconst reasons: string[] = [];\n\tlet mutating = false;\n\tfor (const t of tokens) {\n\t\tconst low = t.toLowerCase();\n\t\tif (t === \"*\" || low === \"all\") {\n\t\t\tmutating = true;\n\t\t\treasons.push(\"grants all tools (*)\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (pluginTools.some((p) => p.toLowerCase() === low)) continue; // reported separately as a guardrail violation\n\t\tconst mapped = CLAUDE_TOOL_ALIASES[low];\n\t\tif (!mapped) {\n\t\t\tmutating = true;\n\t\t\treasons.push(`grants \"${t}\" (unrecognized or MCP tool — treated as mutating)`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!READONLY_TOOLS.has(mapped)) {\n\t\t\tmutating = true;\n\t\t\treasons.push(`grants \"${mapped}\" (mutating/exec)`);\n\t\t}\n\t}\n\n\treturn {\n\t\trisk: mutating ? \"mutating\" : \"read-only\",\n\t\treason: reasons.join(\"; \") || \"read-only tools only\",\n\t\tpluginTools,\n\t\ttokens,\n\t};\n}\n\nexport interface WriteResult {\n\tdest: string;\n\t/** Written file paths, relative to the plugin root. */\n\tfiles: string[];\n\t/** Re-parsed plugin (confirms the scaffold round-trips). */\n\tplugin: NormalizedPlugin | null;\n}\n\n/**\n * Render `draft` into the requested platform layouts and write it under\n * `.agents/plugins/<id>/`. Returns the destination, the emitted files, and the\n * re-parsed plugin so callers can confirm the round-trip.\n */\nexport function writePluginDraft(cwd: string, draft: PluginDraft, platforms?: MarketplacePlatform[]): WriteResult {\n\tconst targets = platforms ?? draft.supportPlatform ?? DEFAULT_AUTHORING_PLATFORMS;\n\tconst dest = path.join(installedPluginsDir(cwd), sanitizeForDir(draft.id));\n\tconst files = emitForPlatforms({ ...draft, supportPlatform: targets }, targets);\n\n\tmkdirSync(dest, { recursive: true });\n\tfor (const f of files) {\n\t\tconst abs = path.join(dest, f.path);\n\t\tmkdirSync(path.dirname(abs), { recursive: true });\n\t\twriteFileSync(abs, f.content);\n\t}\n\n\treturn { dest, files: files.map((f) => f.path), plugin: parsePluginDir(dest) };\n}\n\n/** Whether a plugin id already exists on disk (so authoring never silently clobbers). */\nexport function pluginExists(cwd: string, id: string): boolean {\n\treturn existsSync(path.join(installedPluginsDir(cwd), sanitizeForDir(id)));\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin authoring engine (spec §3) — shared by the two ProposePlugin tools.
|
|
3
|
+
*
|
|
4
|
+
* Completes the discover → acquire → author spectrum: when no marketplace plugin
|
|
5
|
+
* fits a gap, the model can scaffold one. Authoring is gated on the *content /
|
|
6
|
+
* capability-grant* trust axis (what the plugin can do), not the *source* axis
|
|
7
|
+
* used for install. This module carries the risk classification, the
|
|
8
|
+
* privilege-amplification guardrail, and the file writer; the tools own the two
|
|
9
|
+
* escalating-risk *paths* (autonomous scaffold vs. confirm-then-activate).
|
|
10
|
+
*
|
|
11
|
+
* Everything is written through the format registry's {@link emitForPlatforms},
|
|
12
|
+
* so an authored plugin lands in the requested vendor layouts (Claude Code and
|
|
13
|
+
* GitHub Copilot by default) and round-trips back through {@link parsePluginDir}.
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import { CLAUDE_TOOL_ALIASES } from "../../agent-frontmatter.js";
|
|
18
|
+
import { PLUGIN_SYSTEM_TOOL_NAMES } from "../../tools/plugin-tool-names.js";
|
|
19
|
+
import { emitForPlatforms } from "./formats/index.js";
|
|
20
|
+
import { installedPluginsDir, sanitizeForDir } from "./install.js";
|
|
21
|
+
import { parsePluginDir } from "./manifest.js";
|
|
22
|
+
/** Default authoring targets: the two external vendor formats the model can author into. */
|
|
23
|
+
export const DEFAULT_AUTHORING_PLATFORMS = ["claude", "github"];
|
|
24
|
+
/** hoocode tool names that only read (no mutation, no exec). Grants limited to these are low-risk. */
|
|
25
|
+
const READONLY_TOOLS = new Set(["read", "grep", "find", "ls", "webfetch", "websearch"]);
|
|
26
|
+
/**
|
|
27
|
+
* Classify an authored subagent `tools:` allowlist as read-only vs. mutating,
|
|
28
|
+
* reusing the same Claude-alias vocabulary as the agent-frontmatter normalizer
|
|
29
|
+
* (spec §3 "compute the risk, don't guess it"). Anything unrecognized — an MCP
|
|
30
|
+
* tool, a bare `*`, an unknown name — is treated as mutating (fail-safe).
|
|
31
|
+
*/
|
|
32
|
+
export function classifyAllowlist(tools) {
|
|
33
|
+
const tokens = (tools ?? "")
|
|
34
|
+
.split(/[,\s]+/)
|
|
35
|
+
.map((t) => t.trim())
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
const pluginTools = tokens.filter((t) => PLUGIN_SYSTEM_TOOL_NAMES.some((n) => n.toLowerCase() === t.toLowerCase()));
|
|
38
|
+
if (tokens.length === 0) {
|
|
39
|
+
return { risk: "read-only", reason: "no tools granted", pluginTools, tokens };
|
|
40
|
+
}
|
|
41
|
+
const reasons = [];
|
|
42
|
+
let mutating = false;
|
|
43
|
+
for (const t of tokens) {
|
|
44
|
+
const low = t.toLowerCase();
|
|
45
|
+
if (t === "*" || low === "all") {
|
|
46
|
+
mutating = true;
|
|
47
|
+
reasons.push("grants all tools (*)");
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (pluginTools.some((p) => p.toLowerCase() === low))
|
|
51
|
+
continue; // reported separately as a guardrail violation
|
|
52
|
+
const mapped = CLAUDE_TOOL_ALIASES[low];
|
|
53
|
+
if (!mapped) {
|
|
54
|
+
mutating = true;
|
|
55
|
+
reasons.push(`grants "${t}" (unrecognized or MCP tool — treated as mutating)`);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (!READONLY_TOOLS.has(mapped)) {
|
|
59
|
+
mutating = true;
|
|
60
|
+
reasons.push(`grants "${mapped}" (mutating/exec)`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
risk: mutating ? "mutating" : "read-only",
|
|
65
|
+
reason: reasons.join("; ") || "read-only tools only",
|
|
66
|
+
pluginTools,
|
|
67
|
+
tokens,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Render `draft` into the requested platform layouts and write it under
|
|
72
|
+
* `.agents/plugins/<id>/`. Returns the destination, the emitted files, and the
|
|
73
|
+
* re-parsed plugin so callers can confirm the round-trip.
|
|
74
|
+
*/
|
|
75
|
+
export function writePluginDraft(cwd, draft, platforms) {
|
|
76
|
+
const targets = platforms ?? draft.supportPlatform ?? DEFAULT_AUTHORING_PLATFORMS;
|
|
77
|
+
const dest = path.join(installedPluginsDir(cwd), sanitizeForDir(draft.id));
|
|
78
|
+
const files = emitForPlatforms({ ...draft, supportPlatform: targets }, targets);
|
|
79
|
+
mkdirSync(dest, { recursive: true });
|
|
80
|
+
for (const f of files) {
|
|
81
|
+
const abs = path.join(dest, f.path);
|
|
82
|
+
mkdirSync(path.dirname(abs), { recursive: true });
|
|
83
|
+
writeFileSync(abs, f.content);
|
|
84
|
+
}
|
|
85
|
+
return { dest, files: files.map((f) => f.path), plugin: parsePluginDir(dest) };
|
|
86
|
+
}
|
|
87
|
+
/** Whether a plugin id already exists on disk (so authoring never silently clobbers). */
|
|
88
|
+
export function pluginExists(cwd, id) {
|
|
89
|
+
return existsSync(path.join(installedPluginsDir(cwd), sanitizeForDir(id)));
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=authoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authoring.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/authoring.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Plugin authoring engine (spec §3) — shared by the two ProposePlugin tools.\n *\n * Completes the discover → acquire → author spectrum: when no marketplace plugin\n * fits a gap, the model can scaffold one. Authoring is gated on the *content /\n * capability-grant* trust axis (what the plugin can do), not the *source* axis\n * used for install. This module carries the risk classification, the\n * privilege-amplification guardrail, and the file writer; the tools own the two\n * escalating-risk *paths* (autonomous scaffold vs. confirm-then-activate).\n *\n * Everything is written through the format registry's {@link emitForPlatforms},\n * so an authored plugin lands in the requested vendor layouts (Claude Code and\n * GitHub Copilot by default) and round-trips back through {@link parsePluginDir}.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport { CLAUDE_TOOL_ALIASES } from \"../../agent-frontmatter.js\";\nimport { PLUGIN_SYSTEM_TOOL_NAMES } from \"../../tools/plugin-tool-names.js\";\nimport { emitForPlatforms } from \"./formats/index.js\";\nimport type { MarketplacePlatform, PluginDraft } from \"./formats/types.js\";\nimport { installedPluginsDir, sanitizeForDir } from \"./install.js\";\nimport { type NormalizedPlugin, parsePluginDir } from \"./manifest.js\";\n\n/** Default authoring targets: the two external vendor formats the model can author into. */\nexport const DEFAULT_AUTHORING_PLATFORMS: MarketplacePlatform[] = [\"claude\", \"github\"];\n\n/** hoocode tool names that only read (no mutation, no exec). Grants limited to these are low-risk. */\nconst READONLY_TOOLS = new Set([\"read\", \"grep\", \"find\", \"ls\", \"webfetch\", \"websearch\"]);\n\nexport interface AllowlistClassification {\n\t/** read-only grants are as safe as a skill; mutating/exec/network/`*` grants need confirmation. */\n\trisk: \"read-only\" | \"mutating\";\n\t/** Human-readable explanation of what drove the classification. */\n\treason: string;\n\t/** Any plugin-system (capability-acquisition) tools found — always forbidden in an authored allowlist. */\n\tpluginTools: string[];\n\t/** The raw allowlist tokens. */\n\ttokens: string[];\n}\n\n/**\n * Classify an authored subagent `tools:` allowlist as read-only vs. mutating,\n * reusing the same Claude-alias vocabulary as the agent-frontmatter normalizer\n * (spec §3 \"compute the risk, don't guess it\"). Anything unrecognized — an MCP\n * tool, a bare `*`, an unknown name — is treated as mutating (fail-safe).\n */\nexport function classifyAllowlist(tools: string | undefined): AllowlistClassification {\n\tconst tokens = (tools ?? \"\")\n\t\t.split(/[,\\s]+/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\tconst pluginTools = tokens.filter((t) => PLUGIN_SYSTEM_TOOL_NAMES.some((n) => n.toLowerCase() === t.toLowerCase()));\n\n\tif (tokens.length === 0) {\n\t\treturn { risk: \"read-only\", reason: \"no tools granted\", pluginTools, tokens };\n\t}\n\n\tconst reasons: string[] = [];\n\tlet mutating = false;\n\tfor (const t of tokens) {\n\t\tconst low = t.toLowerCase();\n\t\tif (t === \"*\" || low === \"all\") {\n\t\t\tmutating = true;\n\t\t\treasons.push(\"grants all tools (*)\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (pluginTools.some((p) => p.toLowerCase() === low)) continue; // reported separately as a guardrail violation\n\t\tconst mapped = CLAUDE_TOOL_ALIASES[low];\n\t\tif (!mapped) {\n\t\t\tmutating = true;\n\t\t\treasons.push(`grants \"${t}\" (unrecognized or MCP tool — treated as mutating)`);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!READONLY_TOOLS.has(mapped)) {\n\t\t\tmutating = true;\n\t\t\treasons.push(`grants \"${mapped}\" (mutating/exec)`);\n\t\t}\n\t}\n\n\treturn {\n\t\trisk: mutating ? \"mutating\" : \"read-only\",\n\t\treason: reasons.join(\"; \") || \"read-only tools only\",\n\t\tpluginTools,\n\t\ttokens,\n\t};\n}\n\nexport interface WriteResult {\n\tdest: string;\n\t/** Written file paths, relative to the plugin root. */\n\tfiles: string[];\n\t/** Re-parsed plugin (confirms the scaffold round-trips). */\n\tplugin: NormalizedPlugin | null;\n}\n\n/**\n * Render `draft` into the requested platform layouts and write it under\n * `.agents/plugins/<id>/`. Returns the destination, the emitted files, and the\n * re-parsed plugin so callers can confirm the round-trip.\n */\nexport function writePluginDraft(cwd: string, draft: PluginDraft, platforms?: MarketplacePlatform[]): WriteResult {\n\tconst targets = platforms ?? draft.supportPlatform ?? DEFAULT_AUTHORING_PLATFORMS;\n\tconst dest = path.join(installedPluginsDir(cwd), sanitizeForDir(draft.id));\n\tconst files = emitForPlatforms({ ...draft, supportPlatform: targets }, targets);\n\n\tmkdirSync(dest, { recursive: true });\n\tfor (const f of files) {\n\t\tconst abs = path.join(dest, f.path);\n\t\tmkdirSync(path.dirname(abs), { recursive: true });\n\t\twriteFileSync(abs, f.content);\n\t}\n\n\treturn { dest, files: files.map((f) => f.path), plugin: parsePluginDir(dest) };\n}\n\n/** Whether a plugin id already exists on disk (so authoring never silently clobbers). */\nexport function pluginExists(cwd: string, id: string): boolean {\n\treturn existsSync(path.join(installedPluginsDir(cwd), sanitizeForDir(id)));\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hoocode",
|
|
3
|
+
"owner": "hoocode",
|
|
4
|
+
"supportPlatform": ["agents", "claude", "github"],
|
|
5
|
+
"plugins": [
|
|
6
|
+
{
|
|
7
|
+
"name": "hello-world",
|
|
8
|
+
"source": "./plugins/hello-world",
|
|
9
|
+
"description": "Minimal example plugin: a greeting skill and a /hello command."
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native `.agents-plugin` format — hoocode's own layout and a strict superset of
|
|
3
|
+
* the Claude Code format. Wins precedence when several formats coexist, and is
|
|
4
|
+
* the only format that honors `providers`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const agentsFormat: import("./types.js").PluginFormatAdapter;
|
|
7
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/agents.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,YAAY,0CAMvB,CAAC","sourcesContent":["/**\n * Native `.agents-plugin` format — hoocode's own layout and a strict superset of\n * the Claude Code format. Wins precedence when several formats coexist, and is\n * the only format that honors `providers`.\n */\n\nimport { createJsonManifestAdapter } from \"./jsonManifest.js\";\n\nexport const agentsFormat = createJsonManifestAdapter({\n\tid: \"agents\",\n\tmanifestDir: \".agents-plugin\",\n\tprecedence: 0,\n\tlabel: \"Native (.agents-plugin)\",\n\tsupportsProviders: true,\n});\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native `.agents-plugin` format — hoocode's own layout and a strict superset of
|
|
3
|
+
* the Claude Code format. Wins precedence when several formats coexist, and is
|
|
4
|
+
* the only format that honors `providers`.
|
|
5
|
+
*/
|
|
6
|
+
import { createJsonManifestAdapter } from "./jsonManifest.js";
|
|
7
|
+
export const agentsFormat = createJsonManifestAdapter({
|
|
8
|
+
id: "agents",
|
|
9
|
+
manifestDir: ".agents-plugin",
|
|
10
|
+
precedence: 0,
|
|
11
|
+
label: "Native (.agents-plugin)",
|
|
12
|
+
supportsProviders: true,
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/agents.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,MAAM,CAAC,MAAM,YAAY,GAAG,yBAAyB,CAAC;IACrD,EAAE,EAAE,QAAQ;IACZ,WAAW,EAAE,gBAAgB;IAC7B,UAAU,EAAE,CAAC;IACb,KAAK,EAAE,yBAAyB;IAChC,iBAAiB,EAAE,IAAI;CACvB,CAAC,CAAC","sourcesContent":["/**\n * Native `.agents-plugin` format — hoocode's own layout and a strict superset of\n * the Claude Code format. Wins precedence when several formats coexist, and is\n * the only format that honors `providers`.\n */\n\nimport { createJsonManifestAdapter } from \"./jsonManifest.js\";\n\nexport const agentsFormat = createJsonManifestAdapter({\n\tid: \"agents\",\n\tmanifestDir: \".agents-plugin\",\n\tprecedence: 0,\n\tlabel: \"Native (.agents-plugin)\",\n\tsupportsProviders: true,\n});\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code `.claude-plugin` format. Same on-disk layout as the native format
|
|
3
|
+
* (skills/, commands/, agents/, hooks/hooks.json, inline mcpServers) but under
|
|
4
|
+
* the `.claude-plugin/` marker directory and without `providers` (native-only).
|
|
5
|
+
*
|
|
6
|
+
* This is one of the two vendor formats `ProposePlugin` can scaffold into; keep
|
|
7
|
+
* anything Claude-specific here so it can track upstream changes in isolation.
|
|
8
|
+
*/
|
|
9
|
+
export declare const claudeFormat: import("./types.js").PluginFormatAdapter;
|
|
10
|
+
//# sourceMappingURL=claude.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/claude.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,eAAO,MAAM,YAAY,0CAMvB,CAAC","sourcesContent":["/**\n * Claude Code `.claude-plugin` format. Same on-disk layout as the native format\n * (skills/, commands/, agents/, hooks/hooks.json, inline mcpServers) but under\n * the `.claude-plugin/` marker directory and without `providers` (native-only).\n *\n * This is one of the two vendor formats `ProposePlugin` can scaffold into; keep\n * anything Claude-specific here so it can track upstream changes in isolation.\n */\n\nimport { createJsonManifestAdapter } from \"./jsonManifest.js\";\n\nexport const claudeFormat = createJsonManifestAdapter({\n\tid: \"claude\",\n\tmanifestDir: \".claude-plugin\",\n\tprecedence: 1,\n\tlabel: \"Claude Code (.claude-plugin)\",\n\tsupportsProviders: false,\n});\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code `.claude-plugin` format. Same on-disk layout as the native format
|
|
3
|
+
* (skills/, commands/, agents/, hooks/hooks.json, inline mcpServers) but under
|
|
4
|
+
* the `.claude-plugin/` marker directory and without `providers` (native-only).
|
|
5
|
+
*
|
|
6
|
+
* This is one of the two vendor formats `ProposePlugin` can scaffold into; keep
|
|
7
|
+
* anything Claude-specific here so it can track upstream changes in isolation.
|
|
8
|
+
*/
|
|
9
|
+
import { createJsonManifestAdapter } from "./jsonManifest.js";
|
|
10
|
+
export const claudeFormat = createJsonManifestAdapter({
|
|
11
|
+
id: "claude",
|
|
12
|
+
manifestDir: ".claude-plugin",
|
|
13
|
+
precedence: 1,
|
|
14
|
+
label: "Claude Code (.claude-plugin)",
|
|
15
|
+
supportsProviders: false,
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/claude.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,MAAM,CAAC,MAAM,YAAY,GAAG,yBAAyB,CAAC;IACrD,EAAE,EAAE,QAAQ;IACZ,WAAW,EAAE,gBAAgB;IAC7B,UAAU,EAAE,CAAC;IACb,KAAK,EAAE,8BAA8B;IACrC,iBAAiB,EAAE,KAAK;CACxB,CAAC,CAAC","sourcesContent":["/**\n * Claude Code `.claude-plugin` format. Same on-disk layout as the native format\n * (skills/, commands/, agents/, hooks/hooks.json, inline mcpServers) but under\n * the `.claude-plugin/` marker directory and without `providers` (native-only).\n *\n * This is one of the two vendor formats `ProposePlugin` can scaffold into; keep\n * anything Claude-specific here so it can track upstream changes in isolation.\n */\n\nimport { createJsonManifestAdapter } from \"./jsonManifest.js\";\n\nexport const claudeFormat = createJsonManifestAdapter({\n\tid: \"claude\",\n\tmanifestDir: \".claude-plugin\",\n\tprecedence: 1,\n\tlabel: \"Claude Code (.claude-plugin)\",\n\tsupportsProviders: false,\n});\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot `.github` format.
|
|
3
|
+
*
|
|
4
|
+
* Copilot's customization surface differs from Claude Code's: it lives under
|
|
5
|
+
* `.github/` and uses prompt files and chat modes rather than a `skills/` /
|
|
6
|
+
* `agents/` tree. This adapter maps between that layout and hoocode's
|
|
7
|
+
* {@link NormalizedPlugin} model:
|
|
8
|
+
*
|
|
9
|
+
* marker/manifest .github/copilot-plugin.json { name, version, description }
|
|
10
|
+
* skills + commands .github/prompts/<name>.prompt.md → commandsDir
|
|
11
|
+
* subagents .github/chatmodes/<name>.chatmode.md → agentsDir
|
|
12
|
+
* MCP servers .github/mcp.json ({ servers } | { mcpServers })
|
|
13
|
+
* hooks .github/hooks/hooks.json (hoocode extension; Copilot has no hooks)
|
|
14
|
+
*
|
|
15
|
+
* Copilot conventions move quickly; this file is the single place that encodes
|
|
16
|
+
* them, so tracking an upstream change never reaches beyond this adapter. The
|
|
17
|
+
* capability files carry frontmatter that is a superset valid for both Copilot
|
|
18
|
+
* and hoocode's loaders (e.g. `name` + comma-separated `tools`), so an authored
|
|
19
|
+
* Copilot plugin both round-trips here and loads natively.
|
|
20
|
+
*/
|
|
21
|
+
import type { PluginFormatAdapter } from "./types.js";
|
|
22
|
+
export declare const copilotFormat: PluginFormatAdapter;
|
|
23
|
+
//# sourceMappingURL=copilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/copilot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,KAAK,EAA4B,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA4BhF,eAAO,MAAM,aAAa,EAAE,mBA+F3B,CAAC","sourcesContent":["/**\n * GitHub Copilot `.github` format.\n *\n * Copilot's customization surface differs from Claude Code's: it lives under\n * `.github/` and uses prompt files and chat modes rather than a `skills/` /\n * `agents/` tree. This adapter maps between that layout and hoocode's\n * {@link NormalizedPlugin} model:\n *\n * marker/manifest .github/copilot-plugin.json { name, version, description }\n * skills + commands .github/prompts/<name>.prompt.md → commandsDir\n * subagents .github/chatmodes/<name>.chatmode.md → agentsDir\n * MCP servers .github/mcp.json ({ servers } | { mcpServers })\n * hooks .github/hooks/hooks.json (hoocode extension; Copilot has no hooks)\n *\n * Copilot conventions move quickly; this file is the single place that encodes\n * them, so tracking an upstream change never reaches beyond this adapter. The\n * capability files carry frontmatter that is a superset valid for both Copilot\n * and hoocode's loaders (e.g. `name` + comma-separated `tools`), so an authored\n * Copilot plugin both round-trips here and loads natively.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { NormalizedPlugin } from \"../manifest.js\";\nimport { authoredHooksToConfig, dirIfExists, emitJson, emitMarkdown, normalizeMcp, readJson, slug } from \"./shared.js\";\nimport type { EmittedFile, PluginDraft, PluginFormatAdapter } from \"./types.js\";\n\nconst MARKER_DIR = \".github\";\nconst MANIFEST_FILE = \"copilot-plugin.json\";\nconst PROMPTS_DIR = path.join(\".github\", \"prompts\");\nconst CHATMODES_DIR = path.join(\".github\", \"chatmodes\");\nconst MCP_FILE = path.join(\".github\", \"mcp.json\");\nconst HOOKS_FILE = path.join(\".github\", \"hooks\", \"hooks.json\");\n\nconst manifestRelPath = path.join(MARKER_DIR, MANIFEST_FILE);\n\ninterface CopilotManifest {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string | { name?: string };\n}\n\n/** Read `.github/hooks/hooks.json` into the shared hooks event-map shape. */\nfunction readCopilotHooks(root: string): NormalizedPlugin[\"hooks\"] {\n\tconst file = path.join(root, HOOKS_FILE);\n\tif (!fs.existsSync(file)) return undefined;\n\tconst raw = readJson<{ hooks?: Record<string, unknown> } | Record<string, unknown>>(file);\n\tif (!raw || typeof raw !== \"object\") return undefined;\n\tconst config = (\"hooks\" in raw && raw.hooks ? raw.hooks : raw) as NormalizedPlugin[\"hooks\"];\n\treturn config && Object.keys(config).length > 0 ? config : undefined;\n}\n\nexport const copilotFormat: PluginFormatAdapter = {\n\tid: \"copilot\",\n\tplatform: \"github\",\n\tprecedence: 2,\n\tlabel: \"GitHub Copilot (.github)\",\n\tmarketplaceFile: path.join(MARKER_DIR, \"marketplace.json\"),\n\n\tdetectPlugin(root: string): boolean {\n\t\treturn fs.existsSync(path.join(root, manifestRelPath));\n\t},\n\n\tparsePlugin(root: string): NormalizedPlugin | null {\n\t\tconst manifestPath = path.join(root, manifestRelPath);\n\t\tconst raw = readJson<CopilotManifest>(manifestPath);\n\t\tif (!raw) return null;\n\n\t\tconst id = (raw.name ?? path.basename(root)).trim();\n\t\tif (!id) return null;\n\n\t\tconst author = typeof raw.author === \"string\" ? raw.author : raw.author?.name;\n\n\t\treturn {\n\t\t\tid,\n\t\t\tversion: raw.version,\n\t\t\tdescription: raw.description,\n\t\t\tauthor,\n\t\t\troot,\n\t\t\tmanifestPath,\n\t\t\tformat: \"copilot\",\n\t\t\t// Single-format view; the registry widens this to every format present.\n\t\t\tsupportPlatform: [\"github\"],\n\t\t\t// Copilot has no skills dir; prompts are command-shaped and map to commandsDir.\n\t\t\tcommandsDir: dirIfExists(root, PROMPTS_DIR),\n\t\t\tagentsDir: dirIfExists(root, CHATMODES_DIR),\n\t\t\thooks: readCopilotHooks(root),\n\t\t\t// `.github/mcp.json` only (no inline manifest MCP in the Copilot layout).\n\t\t\tmcpServers: normalizeMcp(undefined, root, MCP_FILE),\n\t\t\t// Providers are a native-only concept.\n\t\t\tproviders: undefined,\n\t\t};\n\t},\n\n\temit(draft: PluginDraft): EmittedFile[] {\n\t\tconst files: EmittedFile[] = [];\n\n\t\tfiles.push({\n\t\t\tpath: manifestRelPath,\n\t\t\tcontent: emitJson({\n\t\t\t\tname: draft.id,\n\t\t\t\t...(draft.version ? { version: draft.version } : {}),\n\t\t\t\t...(draft.description ? { description: draft.description } : {}),\n\t\t\t\t...(draft.author ? { author: draft.author } : {}),\n\t\t\t}),\n\t\t});\n\n\t\t// Skills and commands are both authored as Copilot prompt files.\n\t\tfor (const s of draft.skills ?? []) {\n\t\t\tfiles.push({\n\t\t\t\tpath: path.join(PROMPTS_DIR, `${slug(s.name)}.prompt.md`),\n\t\t\t\tcontent: emitMarkdown({ name: s.name, description: s.description }, s.body),\n\t\t\t});\n\t\t}\n\t\tfor (const c of draft.commands ?? []) {\n\t\t\tfiles.push({\n\t\t\t\tpath: path.join(PROMPTS_DIR, `${slug(c.name)}.prompt.md`),\n\t\t\t\tcontent: emitMarkdown({ name: c.name, description: c.description }, c.body),\n\t\t\t});\n\t\t}\n\t\t// Subagents become chat modes. Frontmatter is a superset valid for both\n\t\t// Copilot (description/tools/model) and hoocode's agent loader (name +\n\t\t// comma-separated tools).\n\t\tfor (const a of draft.agents ?? []) {\n\t\t\tfiles.push({\n\t\t\t\tpath: path.join(CHATMODES_DIR, `${slug(a.name)}.chatmode.md`),\n\t\t\t\tcontent: emitMarkdown({ name: a.name, description: a.description, tools: a.tools, model: a.model }, a.body),\n\t\t\t});\n\t\t}\n\t\tif (draft.mcpServers?.length) {\n\t\t\tfiles.push({\n\t\t\t\tpath: MCP_FILE,\n\t\t\t\tcontent: emitJson({\n\t\t\t\t\tservers: 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}),\n\t\t\t});\n\t\t}\n\t\tif (draft.hooks?.length) {\n\t\t\tfiles.push({ path: HOOKS_FILE, content: emitJson({ hooks: authoredHooksToConfig(draft.hooks) }) });\n\t\t}\n\t\treturn files;\n\t},\n};\n"]}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot `.github` format.
|
|
3
|
+
*
|
|
4
|
+
* Copilot's customization surface differs from Claude Code's: it lives under
|
|
5
|
+
* `.github/` and uses prompt files and chat modes rather than a `skills/` /
|
|
6
|
+
* `agents/` tree. This adapter maps between that layout and hoocode's
|
|
7
|
+
* {@link NormalizedPlugin} model:
|
|
8
|
+
*
|
|
9
|
+
* marker/manifest .github/copilot-plugin.json { name, version, description }
|
|
10
|
+
* skills + commands .github/prompts/<name>.prompt.md → commandsDir
|
|
11
|
+
* subagents .github/chatmodes/<name>.chatmode.md → agentsDir
|
|
12
|
+
* MCP servers .github/mcp.json ({ servers } | { mcpServers })
|
|
13
|
+
* hooks .github/hooks/hooks.json (hoocode extension; Copilot has no hooks)
|
|
14
|
+
*
|
|
15
|
+
* Copilot conventions move quickly; this file is the single place that encodes
|
|
16
|
+
* them, so tracking an upstream change never reaches beyond this adapter. The
|
|
17
|
+
* capability files carry frontmatter that is a superset valid for both Copilot
|
|
18
|
+
* and hoocode's loaders (e.g. `name` + comma-separated `tools`), so an authored
|
|
19
|
+
* Copilot plugin both round-trips here and loads natively.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from "node:fs";
|
|
22
|
+
import * as path from "node:path";
|
|
23
|
+
import { authoredHooksToConfig, dirIfExists, emitJson, emitMarkdown, normalizeMcp, readJson, slug } from "./shared.js";
|
|
24
|
+
const MARKER_DIR = ".github";
|
|
25
|
+
const MANIFEST_FILE = "copilot-plugin.json";
|
|
26
|
+
const PROMPTS_DIR = path.join(".github", "prompts");
|
|
27
|
+
const CHATMODES_DIR = path.join(".github", "chatmodes");
|
|
28
|
+
const MCP_FILE = path.join(".github", "mcp.json");
|
|
29
|
+
const HOOKS_FILE = path.join(".github", "hooks", "hooks.json");
|
|
30
|
+
const manifestRelPath = path.join(MARKER_DIR, MANIFEST_FILE);
|
|
31
|
+
/** Read `.github/hooks/hooks.json` into the shared hooks event-map shape. */
|
|
32
|
+
function readCopilotHooks(root) {
|
|
33
|
+
const file = path.join(root, HOOKS_FILE);
|
|
34
|
+
if (!fs.existsSync(file))
|
|
35
|
+
return undefined;
|
|
36
|
+
const raw = readJson(file);
|
|
37
|
+
if (!raw || typeof raw !== "object")
|
|
38
|
+
return undefined;
|
|
39
|
+
const config = ("hooks" in raw && raw.hooks ? raw.hooks : raw);
|
|
40
|
+
return config && Object.keys(config).length > 0 ? config : undefined;
|
|
41
|
+
}
|
|
42
|
+
export const copilotFormat = {
|
|
43
|
+
id: "copilot",
|
|
44
|
+
platform: "github",
|
|
45
|
+
precedence: 2,
|
|
46
|
+
label: "GitHub Copilot (.github)",
|
|
47
|
+
marketplaceFile: path.join(MARKER_DIR, "marketplace.json"),
|
|
48
|
+
detectPlugin(root) {
|
|
49
|
+
return fs.existsSync(path.join(root, manifestRelPath));
|
|
50
|
+
},
|
|
51
|
+
parsePlugin(root) {
|
|
52
|
+
const manifestPath = path.join(root, manifestRelPath);
|
|
53
|
+
const raw = readJson(manifestPath);
|
|
54
|
+
if (!raw)
|
|
55
|
+
return null;
|
|
56
|
+
const id = (raw.name ?? path.basename(root)).trim();
|
|
57
|
+
if (!id)
|
|
58
|
+
return null;
|
|
59
|
+
const author = typeof raw.author === "string" ? raw.author : raw.author?.name;
|
|
60
|
+
return {
|
|
61
|
+
id,
|
|
62
|
+
version: raw.version,
|
|
63
|
+
description: raw.description,
|
|
64
|
+
author,
|
|
65
|
+
root,
|
|
66
|
+
manifestPath,
|
|
67
|
+
format: "copilot",
|
|
68
|
+
// Single-format view; the registry widens this to every format present.
|
|
69
|
+
supportPlatform: ["github"],
|
|
70
|
+
// Copilot has no skills dir; prompts are command-shaped and map to commandsDir.
|
|
71
|
+
commandsDir: dirIfExists(root, PROMPTS_DIR),
|
|
72
|
+
agentsDir: dirIfExists(root, CHATMODES_DIR),
|
|
73
|
+
hooks: readCopilotHooks(root),
|
|
74
|
+
// `.github/mcp.json` only (no inline manifest MCP in the Copilot layout).
|
|
75
|
+
mcpServers: normalizeMcp(undefined, root, MCP_FILE),
|
|
76
|
+
// Providers are a native-only concept.
|
|
77
|
+
providers: undefined,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
emit(draft) {
|
|
81
|
+
const files = [];
|
|
82
|
+
files.push({
|
|
83
|
+
path: manifestRelPath,
|
|
84
|
+
content: emitJson({
|
|
85
|
+
name: draft.id,
|
|
86
|
+
...(draft.version ? { version: draft.version } : {}),
|
|
87
|
+
...(draft.description ? { description: draft.description } : {}),
|
|
88
|
+
...(draft.author ? { author: draft.author } : {}),
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
// Skills and commands are both authored as Copilot prompt files.
|
|
92
|
+
for (const s of draft.skills ?? []) {
|
|
93
|
+
files.push({
|
|
94
|
+
path: path.join(PROMPTS_DIR, `${slug(s.name)}.prompt.md`),
|
|
95
|
+
content: emitMarkdown({ name: s.name, description: s.description }, s.body),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
for (const c of draft.commands ?? []) {
|
|
99
|
+
files.push({
|
|
100
|
+
path: path.join(PROMPTS_DIR, `${slug(c.name)}.prompt.md`),
|
|
101
|
+
content: emitMarkdown({ name: c.name, description: c.description }, c.body),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Subagents become chat modes. Frontmatter is a superset valid for both
|
|
105
|
+
// Copilot (description/tools/model) and hoocode's agent loader (name +
|
|
106
|
+
// comma-separated tools).
|
|
107
|
+
for (const a of draft.agents ?? []) {
|
|
108
|
+
files.push({
|
|
109
|
+
path: path.join(CHATMODES_DIR, `${slug(a.name)}.chatmode.md`),
|
|
110
|
+
content: emitMarkdown({ name: a.name, description: a.description, tools: a.tools, model: a.model }, a.body),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (draft.mcpServers?.length) {
|
|
114
|
+
files.push({
|
|
115
|
+
path: MCP_FILE,
|
|
116
|
+
content: emitJson({
|
|
117
|
+
servers: Object.fromEntries(draft.mcpServers.map((s) => [
|
|
118
|
+
s.name,
|
|
119
|
+
{ command: s.command, ...(s.args ? { args: s.args } : {}), ...(s.env ? { env: s.env } : {}) },
|
|
120
|
+
])),
|
|
121
|
+
}),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (draft.hooks?.length) {
|
|
125
|
+
files.push({ path: HOOKS_FILE, content: emitJson({ hooks: authoredHooksToConfig(draft.hooks) }) });
|
|
126
|
+
}
|
|
127
|
+
return files;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=copilot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/copilot.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * GitHub Copilot `.github` format.\n *\n * Copilot's customization surface differs from Claude Code's: it lives under\n * `.github/` and uses prompt files and chat modes rather than a `skills/` /\n * `agents/` tree. This adapter maps between that layout and hoocode's\n * {@link NormalizedPlugin} model:\n *\n * marker/manifest .github/copilot-plugin.json { name, version, description }\n * skills + commands .github/prompts/<name>.prompt.md → commandsDir\n * subagents .github/chatmodes/<name>.chatmode.md → agentsDir\n * MCP servers .github/mcp.json ({ servers } | { mcpServers })\n * hooks .github/hooks/hooks.json (hoocode extension; Copilot has no hooks)\n *\n * Copilot conventions move quickly; this file is the single place that encodes\n * them, so tracking an upstream change never reaches beyond this adapter. The\n * capability files carry frontmatter that is a superset valid for both Copilot\n * and hoocode's loaders (e.g. `name` + comma-separated `tools`), so an authored\n * Copilot plugin both round-trips here and loads natively.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { NormalizedPlugin } from \"../manifest.js\";\nimport { authoredHooksToConfig, dirIfExists, emitJson, emitMarkdown, normalizeMcp, readJson, slug } from \"./shared.js\";\nimport type { EmittedFile, PluginDraft, PluginFormatAdapter } from \"./types.js\";\n\nconst MARKER_DIR = \".github\";\nconst MANIFEST_FILE = \"copilot-plugin.json\";\nconst PROMPTS_DIR = path.join(\".github\", \"prompts\");\nconst CHATMODES_DIR = path.join(\".github\", \"chatmodes\");\nconst MCP_FILE = path.join(\".github\", \"mcp.json\");\nconst HOOKS_FILE = path.join(\".github\", \"hooks\", \"hooks.json\");\n\nconst manifestRelPath = path.join(MARKER_DIR, MANIFEST_FILE);\n\ninterface CopilotManifest {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string | { name?: string };\n}\n\n/** Read `.github/hooks/hooks.json` into the shared hooks event-map shape. */\nfunction readCopilotHooks(root: string): NormalizedPlugin[\"hooks\"] {\n\tconst file = path.join(root, HOOKS_FILE);\n\tif (!fs.existsSync(file)) return undefined;\n\tconst raw = readJson<{ hooks?: Record<string, unknown> } | Record<string, unknown>>(file);\n\tif (!raw || typeof raw !== \"object\") return undefined;\n\tconst config = (\"hooks\" in raw && raw.hooks ? raw.hooks : raw) as NormalizedPlugin[\"hooks\"];\n\treturn config && Object.keys(config).length > 0 ? config : undefined;\n}\n\nexport const copilotFormat: PluginFormatAdapter = {\n\tid: \"copilot\",\n\tplatform: \"github\",\n\tprecedence: 2,\n\tlabel: \"GitHub Copilot (.github)\",\n\tmarketplaceFile: path.join(MARKER_DIR, \"marketplace.json\"),\n\n\tdetectPlugin(root: string): boolean {\n\t\treturn fs.existsSync(path.join(root, manifestRelPath));\n\t},\n\n\tparsePlugin(root: string): NormalizedPlugin | null {\n\t\tconst manifestPath = path.join(root, manifestRelPath);\n\t\tconst raw = readJson<CopilotManifest>(manifestPath);\n\t\tif (!raw) return null;\n\n\t\tconst id = (raw.name ?? path.basename(root)).trim();\n\t\tif (!id) return null;\n\n\t\tconst author = typeof raw.author === \"string\" ? raw.author : raw.author?.name;\n\n\t\treturn {\n\t\t\tid,\n\t\t\tversion: raw.version,\n\t\t\tdescription: raw.description,\n\t\t\tauthor,\n\t\t\troot,\n\t\t\tmanifestPath,\n\t\t\tformat: \"copilot\",\n\t\t\t// Single-format view; the registry widens this to every format present.\n\t\t\tsupportPlatform: [\"github\"],\n\t\t\t// Copilot has no skills dir; prompts are command-shaped and map to commandsDir.\n\t\t\tcommandsDir: dirIfExists(root, PROMPTS_DIR),\n\t\t\tagentsDir: dirIfExists(root, CHATMODES_DIR),\n\t\t\thooks: readCopilotHooks(root),\n\t\t\t// `.github/mcp.json` only (no inline manifest MCP in the Copilot layout).\n\t\t\tmcpServers: normalizeMcp(undefined, root, MCP_FILE),\n\t\t\t// Providers are a native-only concept.\n\t\t\tproviders: undefined,\n\t\t};\n\t},\n\n\temit(draft: PluginDraft): EmittedFile[] {\n\t\tconst files: EmittedFile[] = [];\n\n\t\tfiles.push({\n\t\t\tpath: manifestRelPath,\n\t\t\tcontent: emitJson({\n\t\t\t\tname: draft.id,\n\t\t\t\t...(draft.version ? { version: draft.version } : {}),\n\t\t\t\t...(draft.description ? { description: draft.description } : {}),\n\t\t\t\t...(draft.author ? { author: draft.author } : {}),\n\t\t\t}),\n\t\t});\n\n\t\t// Skills and commands are both authored as Copilot prompt files.\n\t\tfor (const s of draft.skills ?? []) {\n\t\t\tfiles.push({\n\t\t\t\tpath: path.join(PROMPTS_DIR, `${slug(s.name)}.prompt.md`),\n\t\t\t\tcontent: emitMarkdown({ name: s.name, description: s.description }, s.body),\n\t\t\t});\n\t\t}\n\t\tfor (const c of draft.commands ?? []) {\n\t\t\tfiles.push({\n\t\t\t\tpath: path.join(PROMPTS_DIR, `${slug(c.name)}.prompt.md`),\n\t\t\t\tcontent: emitMarkdown({ name: c.name, description: c.description }, c.body),\n\t\t\t});\n\t\t}\n\t\t// Subagents become chat modes. Frontmatter is a superset valid for both\n\t\t// Copilot (description/tools/model) and hoocode's agent loader (name +\n\t\t// comma-separated tools).\n\t\tfor (const a of draft.agents ?? []) {\n\t\t\tfiles.push({\n\t\t\t\tpath: path.join(CHATMODES_DIR, `${slug(a.name)}.chatmode.md`),\n\t\t\t\tcontent: emitMarkdown({ name: a.name, description: a.description, tools: a.tools, model: a.model }, a.body),\n\t\t\t});\n\t\t}\n\t\tif (draft.mcpServers?.length) {\n\t\t\tfiles.push({\n\t\t\t\tpath: MCP_FILE,\n\t\t\t\tcontent: emitJson({\n\t\t\t\t\tservers: 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}),\n\t\t\t});\n\t\t}\n\t\tif (draft.hooks?.length) {\n\t\t\tfiles.push({ path: HOOKS_FILE, content: emitJson({ hooks: authoredHooksToConfig(draft.hooks) }) });\n\t\t}\n\t\treturn files;\n\t},\n};\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin format registry.
|
|
3
|
+
*
|
|
4
|
+
* The single ordered list of every supported plugin format. Readers, the
|
|
5
|
+
* marketplace parser, and `ProposePlugin` all go through here instead of
|
|
6
|
+
* branching on a format, so adding or evolving a vendor layout is a one-file
|
|
7
|
+
* change (write/adjust an adapter, add it to {@link PLUGIN_FORMATS}).
|
|
8
|
+
*
|
|
9
|
+
* Order is precedence order — native `.agents-plugin` first, then Claude, then
|
|
10
|
+
* Copilot — so "first match wins" when a directory carries more than one format.
|
|
11
|
+
*/
|
|
12
|
+
import type { NormalizedPlugin } from "../manifest.js";
|
|
13
|
+
import { agentsFormat } from "./agents.js";
|
|
14
|
+
import { claudeFormat } from "./claude.js";
|
|
15
|
+
import { copilotFormat } from "./copilot.js";
|
|
16
|
+
import type { EmittedFile, MarketplacePlatform, PluginDraft, PluginFormatAdapter, PluginFormatId } from "./types.js";
|
|
17
|
+
/** All registered formats, in precedence order (lower `precedence` first). */
|
|
18
|
+
export declare const PLUGIN_FORMATS: readonly PluginFormatAdapter[];
|
|
19
|
+
/** Look up an adapter by its internal format id. */
|
|
20
|
+
export declare function getFormat(id: PluginFormatId): PluginFormatAdapter | undefined;
|
|
21
|
+
/** Look up an adapter by its public platform token (`"github"` → Copilot). */
|
|
22
|
+
export declare function getFormatByPlatform(platform: MarketplacePlatform): PluginFormatAdapter | undefined;
|
|
23
|
+
/** Every platform present in a plugin directory, precedence winner first. */
|
|
24
|
+
export declare function detectPlatforms(root: string): MarketplacePlatform[];
|
|
25
|
+
/**
|
|
26
|
+
* Parse a plugin directory using the highest-precedence format present.
|
|
27
|
+
* Returns null when no registered format recognizes the directory.
|
|
28
|
+
*
|
|
29
|
+
* `supportPlatform` records *every* format present (not just the winner), so a
|
|
30
|
+
* directory carrying more than one vendor layout reports all of them.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parsePluginWithFormats(root: string): NormalizedPlugin | null;
|
|
33
|
+
/**
|
|
34
|
+
* Render a draft into the on-disk files for the given platforms (defaulting to
|
|
35
|
+
* the draft's own `supportPlatform`, then to every format). Files from different
|
|
36
|
+
* formats live under distinct marker directories, so the merge never collides.
|
|
37
|
+
*/
|
|
38
|
+
export declare function emitForPlatforms(draft: PluginDraft, platforms?: MarketplacePlatform[]): EmittedFile[];
|
|
39
|
+
export { agentsFormat, claudeFormat, copilotFormat };
|
|
40
|
+
export type { EmittedFile, MarketplacePlatform, PluginDraft, PluginFormatAdapter, PluginFormatId } from "./types.js";
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"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;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,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,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAMnE;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAY5E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,EAAE,mBAAmB,EAAE,GAAG,WAAW,EAAE,CAWrG;AAED,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AACrD,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,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,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin format registry.
|
|
3
|
+
*
|
|
4
|
+
* The single ordered list of every supported plugin format. Readers, the
|
|
5
|
+
* marketplace parser, and `ProposePlugin` all go through here instead of
|
|
6
|
+
* branching on a format, so adding or evolving a vendor layout is a one-file
|
|
7
|
+
* change (write/adjust an adapter, add it to {@link PLUGIN_FORMATS}).
|
|
8
|
+
*
|
|
9
|
+
* Order is precedence order — native `.agents-plugin` first, then Claude, then
|
|
10
|
+
* Copilot — so "first match wins" when a directory carries more than one format.
|
|
11
|
+
*/
|
|
12
|
+
import { agentsFormat } from "./agents.js";
|
|
13
|
+
import { claudeFormat } from "./claude.js";
|
|
14
|
+
import { copilotFormat } from "./copilot.js";
|
|
15
|
+
/** All registered formats, in precedence order (lower `precedence` first). */
|
|
16
|
+
export const PLUGIN_FORMATS = [agentsFormat, claudeFormat, copilotFormat]
|
|
17
|
+
.slice()
|
|
18
|
+
.sort((a, b) => a.precedence - b.precedence);
|
|
19
|
+
/** Look up an adapter by its internal format id. */
|
|
20
|
+
export function getFormat(id) {
|
|
21
|
+
return PLUGIN_FORMATS.find((f) => f.id === id);
|
|
22
|
+
}
|
|
23
|
+
/** Look up an adapter by its public platform token (`"github"` → Copilot). */
|
|
24
|
+
export function getFormatByPlatform(platform) {
|
|
25
|
+
return PLUGIN_FORMATS.find((f) => f.platform === platform);
|
|
26
|
+
}
|
|
27
|
+
/** Every platform present in a plugin directory, precedence winner first. */
|
|
28
|
+
export function detectPlatforms(root) {
|
|
29
|
+
const platforms = [];
|
|
30
|
+
for (const format of PLUGIN_FORMATS) {
|
|
31
|
+
if (format.detectPlugin(root) && !platforms.includes(format.platform))
|
|
32
|
+
platforms.push(format.platform);
|
|
33
|
+
}
|
|
34
|
+
return platforms;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a plugin directory using the highest-precedence format present.
|
|
38
|
+
* Returns null when no registered format recognizes the directory.
|
|
39
|
+
*
|
|
40
|
+
* `supportPlatform` records *every* format present (not just the winner), so a
|
|
41
|
+
* directory carrying more than one vendor layout reports all of them.
|
|
42
|
+
*/
|
|
43
|
+
export function parsePluginWithFormats(root) {
|
|
44
|
+
for (const format of PLUGIN_FORMATS) {
|
|
45
|
+
if (format.detectPlugin(root)) {
|
|
46
|
+
const parsed = format.parsePlugin(root);
|
|
47
|
+
if (parsed) {
|
|
48
|
+
// Winner's platform is already first via precedence order; fold in the rest.
|
|
49
|
+
const supportPlatform = detectPlatforms(root);
|
|
50
|
+
return { ...parsed, supportPlatform };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Render a draft into the on-disk files for the given platforms (defaulting to
|
|
58
|
+
* the draft's own `supportPlatform`, then to every format). Files from different
|
|
59
|
+
* formats live under distinct marker directories, so the merge never collides.
|
|
60
|
+
*/
|
|
61
|
+
export function emitForPlatforms(draft, platforms) {
|
|
62
|
+
const targets = platforms ?? draft.supportPlatform ?? PLUGIN_FORMATS.map((f) => f.platform);
|
|
63
|
+
const files = [];
|
|
64
|
+
const seen = new Set();
|
|
65
|
+
for (const platform of targets) {
|
|
66
|
+
if (seen.has(platform))
|
|
67
|
+
continue;
|
|
68
|
+
seen.add(platform);
|
|
69
|
+
const adapter = getFormatByPlatform(platform);
|
|
70
|
+
if (adapter)
|
|
71
|
+
files.push(...adapter.emit(draft));
|
|
72
|
+
}
|
|
73
|
+
return files;
|
|
74
|
+
}
|
|
75
|
+
export { agentsFormat, claudeFormat, copilotFormat };
|
|
76
|
+
//# sourceMappingURL=index.js.map
|