@kolisachint/hoocode-agent 0.5.2 → 0.5.4
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 +31 -0
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +10 -7
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +10 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/capabilities/deferral.d.ts +78 -0
- package/dist/core/capabilities/deferral.d.ts.map +1 -0
- package/dist/core/capabilities/deferral.js +99 -0
- package/dist/core/capabilities/deferral.js.map +1 -0
- package/dist/core/capabilities/dense.d.ts +51 -0
- package/dist/core/capabilities/dense.d.ts.map +1 -0
- package/dist/core/capabilities/dense.js +121 -0
- package/dist/core/capabilities/dense.js.map +1 -0
- package/dist/core/capabilities/lexical.d.ts +43 -0
- package/dist/core/capabilities/lexical.d.ts.map +1 -0
- package/dist/core/capabilities/lexical.js +104 -0
- package/dist/core/capabilities/lexical.js.map +1 -0
- package/dist/core/capabilities/registry.d.ts +56 -0
- package/dist/core/capabilities/registry.d.ts.map +1 -0
- package/dist/core/capabilities/registry.js +0 -0
- package/dist/core/capabilities/registry.js.map +1 -0
- package/dist/core/capabilities/search.d.ts +47 -0
- package/dist/core/capabilities/search.d.ts.map +1 -0
- package/dist/core/capabilities/search.js +82 -0
- package/dist/core/capabilities/search.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +27 -3
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +61 -7
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/plugins/authoring.d.ts +56 -9
- package/dist/core/extensions/plugins/authoring.d.ts.map +1 -1
- package/dist/core/extensions/plugins/authoring.js +0 -0
- package/dist/core/extensions/plugins/authoring.js.map +1 -1
- package/dist/core/extensions/plugins/drift.d.ts +75 -0
- package/dist/core/extensions/plugins/drift.d.ts.map +1 -0
- package/dist/core/extensions/plugins/drift.js +194 -0
- package/dist/core/extensions/plugins/drift.js.map +1 -0
- package/dist/core/extensions/plugins/formats/claude.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/claude.js +5 -0
- package/dist/core/extensions/plugins/formats/claude.js.map +1 -1
- package/dist/core/extensions/plugins/formats/copilot.d.ts +6 -0
- package/dist/core/extensions/plugins/formats/copilot.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/copilot.js +57 -16
- package/dist/core/extensions/plugins/formats/copilot.js.map +1 -1
- package/dist/core/extensions/plugins/formats/index.d.ts +39 -1
- package/dist/core/extensions/plugins/formats/index.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/index.js +90 -13
- package/dist/core/extensions/plugins/formats/index.js.map +1 -1
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts +14 -0
- package/dist/core/extensions/plugins/formats/jsonManifest.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/jsonManifest.js +37 -4
- package/dist/core/extensions/plugins/formats/jsonManifest.js.map +1 -1
- package/dist/core/extensions/plugins/formats/platform-targets.d.ts +48 -24
- package/dist/core/extensions/plugins/formats/platform-targets.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/platform-targets.js +62 -30
- package/dist/core/extensions/plugins/formats/platform-targets.js.map +1 -1
- package/dist/core/extensions/plugins/formats/shared.d.ts +22 -0
- package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/shared.js +81 -4
- package/dist/core/extensions/plugins/formats/shared.js.map +1 -1
- package/dist/core/extensions/plugins/formats/types.d.ts +10 -1
- package/dist/core/extensions/plugins/formats/types.d.ts.map +1 -1
- package/dist/core/extensions/plugins/formats/types.js.map +1 -1
- package/dist/core/extensions/plugins/gates.d.ts +64 -0
- package/dist/core/extensions/plugins/gates.d.ts.map +1 -0
- package/dist/core/extensions/plugins/gates.js +297 -0
- package/dist/core/extensions/plugins/gates.js.map +1 -0
- package/dist/core/extensions/plugins/hooks-bridge.d.ts +1 -1
- package/dist/core/extensions/plugins/hooks-bridge.d.ts.map +1 -1
- package/dist/core/extensions/plugins/hooks-bridge.js +12 -8
- package/dist/core/extensions/plugins/hooks-bridge.js.map +1 -1
- package/dist/core/extensions/plugins/index.d.ts +36 -5
- package/dist/core/extensions/plugins/index.d.ts.map +1 -1
- package/dist/core/extensions/plugins/index.js +66 -15
- package/dist/core/extensions/plugins/index.js.map +1 -1
- package/dist/core/extensions/plugins/install.d.ts +43 -24
- package/dist/core/extensions/plugins/install.d.ts.map +1 -1
- package/dist/core/extensions/plugins/install.js +167 -54
- package/dist/core/extensions/plugins/install.js.map +1 -1
- package/dist/core/extensions/plugins/locations.d.ts +84 -0
- package/dist/core/extensions/plugins/locations.d.ts.map +1 -0
- package/dist/core/extensions/plugins/locations.js +137 -0
- package/dist/core/extensions/plugins/locations.js.map +1 -0
- package/dist/core/extensions/plugins/manifest.d.ts +21 -1
- package/dist/core/extensions/plugins/manifest.d.ts.map +1 -1
- package/dist/core/extensions/plugins/manifest.js +2 -2
- package/dist/core/extensions/plugins/manifest.js.map +1 -1
- package/dist/core/extensions/plugins/packaging.d.ts +115 -0
- package/dist/core/extensions/plugins/packaging.d.ts.map +1 -0
- package/dist/core/extensions/plugins/packaging.js +362 -0
- package/dist/core/extensions/plugins/packaging.js.map +1 -0
- package/dist/core/extensions/plugins/smoke.d.ts +33 -0
- package/dist/core/extensions/plugins/smoke.d.ts.map +1 -0
- package/dist/core/extensions/plugins/smoke.js +231 -0
- package/dist/core/extensions/plugins/smoke.js.map +1 -0
- package/dist/core/extensions/plugins/trigger-eval.d.ts +136 -0
- package/dist/core/extensions/plugins/trigger-eval.d.ts.map +1 -0
- package/dist/core/extensions/plugins/trigger-eval.js +0 -0
- package/dist/core/extensions/plugins/trigger-eval.js.map +1 -0
- package/dist/core/package-manager.d.ts +6 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +15 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/settings-manager.d.ts +2 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -3
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/skills.d.ts +9 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +23 -3
- package/dist/core/skills.js.map +1 -1
- package/dist/core/tools/package-plugin.d.ts +19 -0
- package/dist/core/tools/package-plugin.d.ts.map +1 -0
- package/dist/core/tools/package-plugin.js +81 -0
- package/dist/core/tools/package-plugin.js.map +1 -0
- package/dist/core/tools/plugin-tool-names.d.ts +1 -0
- package/dist/core/tools/plugin-tool-names.d.ts.map +1 -1
- package/dist/core/tools/plugin-tool-names.js +6 -0
- package/dist/core/tools/plugin-tool-names.js.map +1 -1
- package/dist/core/tools/plugins.d.ts.map +1 -1
- package/dist/core/tools/plugins.js +48 -7
- package/dist/core/tools/plugins.js.map +1 -1
- package/dist/core/tools/propose-plugin.d.ts.map +1 -1
- package/dist/core/tools/propose-plugin.js +49 -21
- package/dist/core/tools/propose-plugin.js.map +1 -1
- package/dist/core/tools/webfetch.d.ts +3 -1
- package/dist/core/tools/webfetch.d.ts.map +1 -1
- package/dist/core/tools/webfetch.js +8 -2
- package/dist/core/tools/webfetch.js.map +1 -1
- package/dist/core/tools/websearch.d.ts +2 -0
- package/dist/core/tools/websearch.d.ts.map +1 -1
- package/dist/core/tools/websearch.js +2 -1
- package/dist/core/tools/websearch.js.map +1 -1
- package/dist/core/tools/webtools-shared.d.ts +23 -2
- package/dist/core/tools/webtools-shared.d.ts.map +1 -1
- package/dist/core/tools/webtools-shared.js +59 -9
- package/dist/core/tools/webtools-shared.js.map +1 -1
- package/dist/extensions/core/marketplace.d.ts +14 -3
- package/dist/extensions/core/marketplace.d.ts.map +1 -1
- package/dist/extensions/core/marketplace.js +77 -18
- package/dist/extensions/core/marketplace.js.map +1 -1
- package/dist/extensions/core/mcp-deferred.d.ts +39 -2
- package/dist/extensions/core/mcp-deferred.d.ts.map +1 -1
- package/dist/extensions/core/mcp-deferred.js +74 -10
- package/dist/extensions/core/mcp-deferred.js.map +1 -1
- package/dist/extensions/core/mcp-loader.d.ts.map +1 -1
- package/dist/extensions/core/mcp-loader.js +52 -9
- package/dist/extensions/core/mcp-loader.js.map +1 -1
- package/dist/extensions/core/scaffold.d.ts +3 -3
- package/dist/extensions/core/scaffold.d.ts.map +1 -1
- package/dist/extensions/core/scaffold.js +9 -9
- package/dist/extensions/core/scaffold.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +25 -7
- package/dist/main.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +8 -0
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +12 -3
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/providers.md +11 -0
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +5 -4
|
@@ -1,49 +1,73 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session-wide artifact platform targeting (`--
|
|
2
|
+
* Session-wide artifact platform targeting (`--platform`).
|
|
3
3
|
*
|
|
4
4
|
* hoocode reads resources from every vendor convention, but when it *writes*
|
|
5
|
-
* artifacts
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* artifacts it needs a target layout. The two kinds of write have **different
|
|
6
|
+
* rules**, which is why this module exposes two resolvers rather than one:
|
|
7
|
+
*
|
|
8
|
+
* Plugins distribution units. A plugin only means anything as a
|
|
9
|
+
* member of a platform ecosystem (there is no `.agents`
|
|
10
|
+
* marketplace), so production is platform-specific and
|
|
11
|
+
* `agents` is never a valid target.
|
|
12
|
+
* → {@link resolvePluginPlatforms}
|
|
13
|
+
* Workspace scaffolds `/new-skill`, `/new-agent`, `/new-command`. These are
|
|
14
|
+
* consumed in place, never shipped, and `.agents/` is a
|
|
15
|
+
* real cross-vendor convention for them.
|
|
16
|
+
* → {@link getWorkspacePlatforms}
|
|
8
17
|
*
|
|
9
18
|
* token vocabulary `agents` (alias `native`), `claude`,
|
|
10
19
|
* `github` (aliases `copilot`, `gh`)
|
|
11
|
-
* session state set once at startup from the `--
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
20
|
+
* session state set once at startup from the `--platform` flag or the
|
|
21
|
+
* `platform` setting (main.ts)
|
|
22
|
+
*
|
|
23
|
+
* See docs/plugin-system-architecture.md §1 for the reasoning.
|
|
15
24
|
*
|
|
16
25
|
* Kept beside the format registry (and importing only `types.ts`) so the
|
|
17
26
|
* platform vocabulary and the adapters stay a one-directory concern: adding a
|
|
18
27
|
* vendor means a new adapter file plus a token here, nothing else.
|
|
19
28
|
*/
|
|
20
29
|
import type { MarketplacePlatform } from "./types.js";
|
|
30
|
+
/** The platforms a *plugin* can be produced for. `agents` is excluded by construction. */
|
|
31
|
+
export type PluginPlatform = Exclude<MarketplacePlatform, "agents">;
|
|
32
|
+
/** True for the platform tokens that own a plugin ecosystem. */
|
|
33
|
+
export declare function isPluginPlatform(platform: MarketplacePlatform): platform is PluginPlatform;
|
|
21
34
|
/**
|
|
22
|
-
* Default
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* concern, requested only via the `--support-platform` session flag.
|
|
35
|
+
* Default plugin production target when neither the call nor the session picked
|
|
36
|
+
* one. Claude is the default because it is the only platform whose local loop
|
|
37
|
+
* closes: an authored plugin lands in `~/.claude/skills/<id>/` and is live on the
|
|
38
|
+
* next session, whereas Copilot has no in-place drop-in at all.
|
|
27
39
|
*/
|
|
28
|
-
export declare const
|
|
40
|
+
export declare const DEFAULT_PLUGIN_PLATFORMS: readonly PluginPlatform[];
|
|
29
41
|
/** Canonicalize one platform token, folding the user-facing aliases. */
|
|
30
42
|
export declare function normalizePlatformToken(token: string): MarketplacePlatform | undefined;
|
|
31
|
-
export interface
|
|
43
|
+
export interface PlatformParse {
|
|
32
44
|
/** Canonical platforms, deduped, in the order first mentioned. */
|
|
33
45
|
platforms: MarketplacePlatform[];
|
|
34
46
|
/** Tokens that matched no known platform (surfaced as diagnostics, never fatal). */
|
|
35
47
|
invalid: string[];
|
|
36
48
|
}
|
|
37
|
-
/** Parse raw `--
|
|
38
|
-
export declare function
|
|
49
|
+
/** Parse raw `--platform` / `platform` tokens into canonical platforms. */
|
|
50
|
+
export declare function parsePlatforms(tokens: readonly string[]): PlatformParse;
|
|
39
51
|
/** Set (or clear, with undefined/empty) the session's artifact platform targets. */
|
|
40
|
-
export declare function
|
|
41
|
-
/** The session's configured targets, or undefined when `--support-platform` was not given. */
|
|
42
|
-
export declare function getSupportPlatforms(): MarketplacePlatform[] | undefined;
|
|
52
|
+
export declare function setPlatforms(platforms: readonly MarketplacePlatform[] | undefined): void;
|
|
43
53
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
54
|
+
* The session's configured targets for **workspace scaffolds**, or undefined when
|
|
55
|
+
* `--platform` was not given — callers treat undefined as "use the hoocode-native
|
|
56
|
+
* location" rather than substituting a default. `agents` is a legitimate value
|
|
57
|
+
* here: a scaffolded skill or command is consumed in place, not distributed.
|
|
58
|
+
*/
|
|
59
|
+
export declare function getWorkspacePlatforms(): MarketplacePlatform[] | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Resolve the platforms a **plugin** should be produced for: an explicit per-call
|
|
62
|
+
* selection wins, then the session's `--platform` targets, then
|
|
63
|
+
* {@link DEFAULT_PLUGIN_PLATFORMS}.
|
|
64
|
+
*
|
|
65
|
+
* `agents` can never be the answer. An explicit `agents` throws — a caller asking
|
|
66
|
+
* for a plugin in a format no marketplace accepts has made a mistake and should
|
|
67
|
+
* hear about it. A session-level `agents` is *filtered* instead, because
|
|
68
|
+
* `--platform` also drives workspace scaffolds, where `agents` is a perfectly
|
|
69
|
+
* good choice; a user who set it for that reason should not have plugin authoring
|
|
70
|
+
* blow up in their face. If filtering leaves nothing, the default applies.
|
|
47
71
|
*/
|
|
48
|
-
export declare function
|
|
72
|
+
export declare function resolvePluginPlatforms(explicit?: readonly MarketplacePlatform[]): PluginPlatform[];
|
|
49
73
|
//# sourceMappingURL=platform-targets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-targets.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/platform-targets.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"platform-targets.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/platform-targets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,0FAA0F;AAC1F,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;AAEpE,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,QAAQ,IAAI,cAAc,CAE1F;AAED;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,SAAS,cAAc,EAAe,CAAC;AAE9E,wEAAwE;AACxE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAcrF;AAED,MAAM,WAAW,aAAa;IAC7B,kEAAkE;IAClE,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,oFAAoF;IACpF,OAAO,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,aAAa,CAWvE;AAKD,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,mBAAmB,EAAE,GAAG,SAAS,GAAG,IAAI,CAExF;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,IAAI,mBAAmB,EAAE,GAAG,SAAS,CAEzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,CAAC,EAAE,SAAS,mBAAmB,EAAE,GAAG,cAAc,EAAE,CAclG","sourcesContent":["/**\n * Session-wide artifact platform targeting (`--platform`).\n *\n * hoocode reads resources from every vendor convention, but when it *writes*\n * artifacts it needs a target layout. The two kinds of write have **different\n * rules**, which is why this module exposes two resolvers rather than one:\n *\n * Plugins distribution units. A plugin only means anything as a\n * member of a platform ecosystem (there is no `.agents`\n * marketplace), so production is platform-specific and\n * `agents` is never a valid target.\n * → {@link resolvePluginPlatforms}\n * Workspace scaffolds `/new-skill`, `/new-agent`, `/new-command`. These are\n * consumed in place, never shipped, and `.agents/` is a\n * real cross-vendor convention for them.\n * → {@link getWorkspacePlatforms}\n *\n * token vocabulary `agents` (alias `native`), `claude`,\n * `github` (aliases `copilot`, `gh`)\n * session state set once at startup from the `--platform` flag or the\n * `platform` setting (main.ts)\n *\n * See docs/plugin-system-architecture.md §1 for the reasoning.\n *\n * Kept beside the format registry (and importing only `types.ts`) so the\n * platform vocabulary and the adapters stay a one-directory concern: adding a\n * vendor means a new adapter file plus a token here, nothing else.\n */\n\nimport type { MarketplacePlatform } from \"./types.js\";\n\n/** The platforms a *plugin* can be produced for. `agents` is excluded by construction. */\nexport type PluginPlatform = Exclude<MarketplacePlatform, \"agents\">;\n\n/** True for the platform tokens that own a plugin ecosystem. */\nexport function isPluginPlatform(platform: MarketplacePlatform): platform is PluginPlatform {\n\treturn platform !== \"agents\";\n}\n\n/**\n * Default plugin production target when neither the call nor the session picked\n * one. Claude is the default because it is the only platform whose local loop\n * closes: an authored plugin lands in `~/.claude/skills/<id>/` and is live on the\n * next session, whereas Copilot has no in-place drop-in at all.\n */\nexport const DEFAULT_PLUGIN_PLATFORMS: readonly PluginPlatform[] = [\"claude\"];\n\n/** Canonicalize one platform token, folding the user-facing aliases. */\nexport function normalizePlatformToken(token: string): MarketplacePlatform | undefined {\n\tswitch (token.trim().toLowerCase()) {\n\t\tcase \"agents\":\n\t\tcase \"native\":\n\t\t\treturn \"agents\";\n\t\tcase \"claude\":\n\t\t\treturn \"claude\";\n\t\tcase \"github\":\n\t\tcase \"copilot\":\n\t\tcase \"gh\":\n\t\t\treturn \"github\";\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport interface PlatformParse {\n\t/** Canonical platforms, deduped, in the order first mentioned. */\n\tplatforms: MarketplacePlatform[];\n\t/** Tokens that matched no known platform (surfaced as diagnostics, never fatal). */\n\tinvalid: string[];\n}\n\n/** Parse raw `--platform` / `platform` tokens into canonical platforms. */\nexport function parsePlatforms(tokens: readonly string[]): PlatformParse {\n\tconst platforms: MarketplacePlatform[] = [];\n\tconst invalid: string[] = [];\n\tfor (const token of tokens) {\n\t\tconst trimmed = token.trim();\n\t\tif (!trimmed) continue;\n\t\tconst canonical = normalizePlatformToken(trimmed);\n\t\tif (!canonical) invalid.push(trimmed);\n\t\telse if (!platforms.includes(canonical)) platforms.push(canonical);\n\t}\n\treturn { platforms, invalid };\n}\n\n/** Session-wide targets. Undefined = not configured (fall back to per-consumer defaults). */\nlet sessionPlatforms: MarketplacePlatform[] | undefined;\n\n/** Set (or clear, with undefined/empty) the session's artifact platform targets. */\nexport function setPlatforms(platforms: readonly MarketplacePlatform[] | undefined): void {\n\tsessionPlatforms = platforms && platforms.length > 0 ? [...platforms] : undefined;\n}\n\n/**\n * The session's configured targets for **workspace scaffolds**, or undefined when\n * `--platform` was not given — callers treat undefined as \"use the hoocode-native\n * location\" rather than substituting a default. `agents` is a legitimate value\n * here: a scaffolded skill or command is consumed in place, not distributed.\n */\nexport function getWorkspacePlatforms(): MarketplacePlatform[] | undefined {\n\treturn sessionPlatforms ? [...sessionPlatforms] : undefined;\n}\n\n/**\n * Resolve the platforms a **plugin** should be produced for: an explicit per-call\n * selection wins, then the session's `--platform` targets, then\n * {@link DEFAULT_PLUGIN_PLATFORMS}.\n *\n * `agents` can never be the answer. An explicit `agents` throws — a caller asking\n * for a plugin in a format no marketplace accepts has made a mistake and should\n * hear about it. A session-level `agents` is *filtered* instead, because\n * `--platform` also drives workspace scaffolds, where `agents` is a perfectly\n * good choice; a user who set it for that reason should not have plugin authoring\n * blow up in their face. If filtering leaves nothing, the default applies.\n */\nexport function resolvePluginPlatforms(explicit?: readonly MarketplacePlatform[]): PluginPlatform[] {\n\tif (explicit && explicit.length > 0) {\n\t\tconst rejected = explicit.filter((p) => !isPluginPlatform(p));\n\t\tif (rejected.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot author a plugin for platform \"${rejected.join(\", \")}\": plugins are distribution units and ` +\n\t\t\t\t\t\"the native `agents` layout belongs to no marketplace, so it can never be published or installed. \" +\n\t\t\t\t\t\"Target `claude` or `github`, or author a workspace skill instead.\",\n\t\t\t);\n\t\t}\n\t\treturn explicit.filter(isPluginPlatform);\n\t}\n\tconst fromSession = (sessionPlatforms ?? []).filter(isPluginPlatform);\n\treturn fromSession.length > 0 ? fromSession : [...DEFAULT_PLUGIN_PLATFORMS];\n}\n"]}
|
|
@@ -1,30 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Session-wide artifact platform targeting (`--
|
|
2
|
+
* Session-wide artifact platform targeting (`--platform`).
|
|
3
3
|
*
|
|
4
4
|
* hoocode reads resources from every vendor convention, but when it *writes*
|
|
5
|
-
* artifacts
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* artifacts it needs a target layout. The two kinds of write have **different
|
|
6
|
+
* rules**, which is why this module exposes two resolvers rather than one:
|
|
7
|
+
*
|
|
8
|
+
* Plugins distribution units. A plugin only means anything as a
|
|
9
|
+
* member of a platform ecosystem (there is no `.agents`
|
|
10
|
+
* marketplace), so production is platform-specific and
|
|
11
|
+
* `agents` is never a valid target.
|
|
12
|
+
* → {@link resolvePluginPlatforms}
|
|
13
|
+
* Workspace scaffolds `/new-skill`, `/new-agent`, `/new-command`. These are
|
|
14
|
+
* consumed in place, never shipped, and `.agents/` is a
|
|
15
|
+
* real cross-vendor convention for them.
|
|
16
|
+
* → {@link getWorkspacePlatforms}
|
|
8
17
|
*
|
|
9
18
|
* token vocabulary `agents` (alias `native`), `claude`,
|
|
10
19
|
* `github` (aliases `copilot`, `gh`)
|
|
11
|
-
* session state set once at startup from the `--
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
20
|
+
* session state set once at startup from the `--platform` flag or the
|
|
21
|
+
* `platform` setting (main.ts)
|
|
22
|
+
*
|
|
23
|
+
* See docs/plugin-system-architecture.md §1 for the reasoning.
|
|
15
24
|
*
|
|
16
25
|
* Kept beside the format registry (and importing only `types.ts`) so the
|
|
17
26
|
* platform vocabulary and the adapters stay a one-directory concern: adding a
|
|
18
27
|
* vendor means a new adapter file plus a token here, nothing else.
|
|
19
28
|
*/
|
|
29
|
+
/** True for the platform tokens that own a plugin ecosystem. */
|
|
30
|
+
export function isPluginPlatform(platform) {
|
|
31
|
+
return platform !== "agents";
|
|
32
|
+
}
|
|
20
33
|
/**
|
|
21
|
-
* Default
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* concern, requested only via the `--support-platform` session flag.
|
|
34
|
+
* Default plugin production target when neither the call nor the session picked
|
|
35
|
+
* one. Claude is the default because it is the only platform whose local loop
|
|
36
|
+
* closes: an authored plugin lands in `~/.claude/skills/<id>/` and is live on the
|
|
37
|
+
* next session, whereas Copilot has no in-place drop-in at all.
|
|
26
38
|
*/
|
|
27
|
-
export const
|
|
39
|
+
export const DEFAULT_PLUGIN_PLATFORMS = ["claude"];
|
|
28
40
|
/** Canonicalize one platform token, folding the user-facing aliases. */
|
|
29
41
|
export function normalizePlatformToken(token) {
|
|
30
42
|
switch (token.trim().toLowerCase()) {
|
|
@@ -41,8 +53,8 @@ export function normalizePlatformToken(token) {
|
|
|
41
53
|
return undefined;
|
|
42
54
|
}
|
|
43
55
|
}
|
|
44
|
-
/** Parse raw `--
|
|
45
|
-
export function
|
|
56
|
+
/** Parse raw `--platform` / `platform` tokens into canonical platforms. */
|
|
57
|
+
export function parsePlatforms(tokens) {
|
|
46
58
|
const platforms = [];
|
|
47
59
|
const invalid = [];
|
|
48
60
|
for (const token of tokens) {
|
|
@@ -57,24 +69,44 @@ export function parseSupportPlatforms(tokens) {
|
|
|
57
69
|
}
|
|
58
70
|
return { platforms, invalid };
|
|
59
71
|
}
|
|
60
|
-
/** Session-wide targets. Undefined = not configured (fall back to defaults). */
|
|
61
|
-
let
|
|
72
|
+
/** Session-wide targets. Undefined = not configured (fall back to per-consumer defaults). */
|
|
73
|
+
let sessionPlatforms;
|
|
62
74
|
/** Set (or clear, with undefined/empty) the session's artifact platform targets. */
|
|
63
|
-
export function
|
|
64
|
-
|
|
75
|
+
export function setPlatforms(platforms) {
|
|
76
|
+
sessionPlatforms = platforms && platforms.length > 0 ? [...platforms] : undefined;
|
|
65
77
|
}
|
|
66
|
-
/**
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
/**
|
|
79
|
+
* The session's configured targets for **workspace scaffolds**, or undefined when
|
|
80
|
+
* `--platform` was not given — callers treat undefined as "use the hoocode-native
|
|
81
|
+
* location" rather than substituting a default. `agents` is a legitimate value
|
|
82
|
+
* here: a scaffolded skill or command is consumed in place, not distributed.
|
|
83
|
+
*/
|
|
84
|
+
export function getWorkspacePlatforms() {
|
|
85
|
+
return sessionPlatforms ? [...sessionPlatforms] : undefined;
|
|
69
86
|
}
|
|
70
87
|
/**
|
|
71
|
-
* Resolve the platforms
|
|
72
|
-
* selection wins, then the session's `--
|
|
73
|
-
* {@link
|
|
88
|
+
* Resolve the platforms a **plugin** should be produced for: an explicit per-call
|
|
89
|
+
* selection wins, then the session's `--platform` targets, then
|
|
90
|
+
* {@link DEFAULT_PLUGIN_PLATFORMS}.
|
|
91
|
+
*
|
|
92
|
+
* `agents` can never be the answer. An explicit `agents` throws — a caller asking
|
|
93
|
+
* for a plugin in a format no marketplace accepts has made a mistake and should
|
|
94
|
+
* hear about it. A session-level `agents` is *filtered* instead, because
|
|
95
|
+
* `--platform` also drives workspace scaffolds, where `agents` is a perfectly
|
|
96
|
+
* good choice; a user who set it for that reason should not have plugin authoring
|
|
97
|
+
* blow up in their face. If filtering leaves nothing, the default applies.
|
|
74
98
|
*/
|
|
75
|
-
export function
|
|
76
|
-
if (explicit && explicit.length > 0)
|
|
77
|
-
|
|
78
|
-
|
|
99
|
+
export function resolvePluginPlatforms(explicit) {
|
|
100
|
+
if (explicit && explicit.length > 0) {
|
|
101
|
+
const rejected = explicit.filter((p) => !isPluginPlatform(p));
|
|
102
|
+
if (rejected.length > 0) {
|
|
103
|
+
throw new Error(`Cannot author a plugin for platform "${rejected.join(", ")}": plugins are distribution units and ` +
|
|
104
|
+
"the native `agents` layout belongs to no marketplace, so it can never be published or installed. " +
|
|
105
|
+
"Target `claude` or `github`, or author a workspace skill instead.");
|
|
106
|
+
}
|
|
107
|
+
return explicit.filter(isPluginPlatform);
|
|
108
|
+
}
|
|
109
|
+
const fromSession = (sessionPlatforms ?? []).filter(isPluginPlatform);
|
|
110
|
+
return fromSession.length > 0 ? fromSession : [...DEFAULT_PLUGIN_PLATFORMS];
|
|
79
111
|
}
|
|
80
112
|
//# sourceMappingURL=platform-targets.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform-targets.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/platform-targets.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"platform-targets.js","sourceRoot":"","sources":["../../../../../src/core/extensions/plugins/formats/platform-targets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAOH,gEAAgE;AAChE,MAAM,UAAU,gBAAgB,CAAC,QAA6B,EAA8B;IAC3F,OAAO,QAAQ,KAAK,QAAQ,CAAC;AAAA,CAC7B;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA8B,CAAC,QAAQ,CAAC,CAAC;AAE9E,wEAAwE;AACxE,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAmC;IACtF,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QACpC,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ;YACZ,OAAO,QAAQ,CAAC;QACjB,KAAK,QAAQ;YACZ,OAAO,QAAQ,CAAC;QACjB,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS,CAAC;QACf,KAAK,IAAI;YACR,OAAO,QAAQ,CAAC;QACjB;YACC,OAAO,SAAS,CAAC;IACnB,CAAC;AAAA,CACD;AASD,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,MAAyB,EAAiB;IACxE,MAAM,SAAS,GAA0B,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,SAAS,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;aACjC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAAA,CAC9B;AAED,6FAA6F;AAC7F,IAAI,gBAAmD,CAAC;AAExD,oFAAoF;AACpF,MAAM,UAAU,YAAY,CAAC,SAAqD,EAAQ;IACzF,gBAAgB,GAAG,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAClF;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,GAAsC;IAC1E,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CAC5D;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAyC,EAAoB;IACnG,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9D,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACd,wCAAwC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,wCAAwC;gBAClG,mGAAmG;gBACnG,mEAAmE,CACpE,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC1C,CAAC;IACD,MAAM,WAAW,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACtE,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC,CAAC;AAAA,CAC5E","sourcesContent":["/**\n * Session-wide artifact platform targeting (`--platform`).\n *\n * hoocode reads resources from every vendor convention, but when it *writes*\n * artifacts it needs a target layout. The two kinds of write have **different\n * rules**, which is why this module exposes two resolvers rather than one:\n *\n * Plugins distribution units. A plugin only means anything as a\n * member of a platform ecosystem (there is no `.agents`\n * marketplace), so production is platform-specific and\n * `agents` is never a valid target.\n * → {@link resolvePluginPlatforms}\n * Workspace scaffolds `/new-skill`, `/new-agent`, `/new-command`. These are\n * consumed in place, never shipped, and `.agents/` is a\n * real cross-vendor convention for them.\n * → {@link getWorkspacePlatforms}\n *\n * token vocabulary `agents` (alias `native`), `claude`,\n * `github` (aliases `copilot`, `gh`)\n * session state set once at startup from the `--platform` flag or the\n * `platform` setting (main.ts)\n *\n * See docs/plugin-system-architecture.md §1 for the reasoning.\n *\n * Kept beside the format registry (and importing only `types.ts`) so the\n * platform vocabulary and the adapters stay a one-directory concern: adding a\n * vendor means a new adapter file plus a token here, nothing else.\n */\n\nimport type { MarketplacePlatform } from \"./types.js\";\n\n/** The platforms a *plugin* can be produced for. `agents` is excluded by construction. */\nexport type PluginPlatform = Exclude<MarketplacePlatform, \"agents\">;\n\n/** True for the platform tokens that own a plugin ecosystem. */\nexport function isPluginPlatform(platform: MarketplacePlatform): platform is PluginPlatform {\n\treturn platform !== \"agents\";\n}\n\n/**\n * Default plugin production target when neither the call nor the session picked\n * one. Claude is the default because it is the only platform whose local loop\n * closes: an authored plugin lands in `~/.claude/skills/<id>/` and is live on the\n * next session, whereas Copilot has no in-place drop-in at all.\n */\nexport const DEFAULT_PLUGIN_PLATFORMS: readonly PluginPlatform[] = [\"claude\"];\n\n/** Canonicalize one platform token, folding the user-facing aliases. */\nexport function normalizePlatformToken(token: string): MarketplacePlatform | undefined {\n\tswitch (token.trim().toLowerCase()) {\n\t\tcase \"agents\":\n\t\tcase \"native\":\n\t\t\treturn \"agents\";\n\t\tcase \"claude\":\n\t\t\treturn \"claude\";\n\t\tcase \"github\":\n\t\tcase \"copilot\":\n\t\tcase \"gh\":\n\t\t\treturn \"github\";\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nexport interface PlatformParse {\n\t/** Canonical platforms, deduped, in the order first mentioned. */\n\tplatforms: MarketplacePlatform[];\n\t/** Tokens that matched no known platform (surfaced as diagnostics, never fatal). */\n\tinvalid: string[];\n}\n\n/** Parse raw `--platform` / `platform` tokens into canonical platforms. */\nexport function parsePlatforms(tokens: readonly string[]): PlatformParse {\n\tconst platforms: MarketplacePlatform[] = [];\n\tconst invalid: string[] = [];\n\tfor (const token of tokens) {\n\t\tconst trimmed = token.trim();\n\t\tif (!trimmed) continue;\n\t\tconst canonical = normalizePlatformToken(trimmed);\n\t\tif (!canonical) invalid.push(trimmed);\n\t\telse if (!platforms.includes(canonical)) platforms.push(canonical);\n\t}\n\treturn { platforms, invalid };\n}\n\n/** Session-wide targets. Undefined = not configured (fall back to per-consumer defaults). */\nlet sessionPlatforms: MarketplacePlatform[] | undefined;\n\n/** Set (or clear, with undefined/empty) the session's artifact platform targets. */\nexport function setPlatforms(platforms: readonly MarketplacePlatform[] | undefined): void {\n\tsessionPlatforms = platforms && platforms.length > 0 ? [...platforms] : undefined;\n}\n\n/**\n * The session's configured targets for **workspace scaffolds**, or undefined when\n * `--platform` was not given — callers treat undefined as \"use the hoocode-native\n * location\" rather than substituting a default. `agents` is a legitimate value\n * here: a scaffolded skill or command is consumed in place, not distributed.\n */\nexport function getWorkspacePlatforms(): MarketplacePlatform[] | undefined {\n\treturn sessionPlatforms ? [...sessionPlatforms] : undefined;\n}\n\n/**\n * Resolve the platforms a **plugin** should be produced for: an explicit per-call\n * selection wins, then the session's `--platform` targets, then\n * {@link DEFAULT_PLUGIN_PLATFORMS}.\n *\n * `agents` can never be the answer. An explicit `agents` throws — a caller asking\n * for a plugin in a format no marketplace accepts has made a mistake and should\n * hear about it. A session-level `agents` is *filtered* instead, because\n * `--platform` also drives workspace scaffolds, where `agents` is a perfectly\n * good choice; a user who set it for that reason should not have plugin authoring\n * blow up in their face. If filtering leaves nothing, the default applies.\n */\nexport function resolvePluginPlatforms(explicit?: readonly MarketplacePlatform[]): PluginPlatform[] {\n\tif (explicit && explicit.length > 0) {\n\t\tconst rejected = explicit.filter((p) => !isPluginPlatform(p));\n\t\tif (rejected.length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot author a plugin for platform \"${rejected.join(\", \")}\": plugins are distribution units and ` +\n\t\t\t\t\t\"the native `agents` layout belongs to no marketplace, so it can never be published or installed. \" +\n\t\t\t\t\t\"Target `claude` or `github`, or author a workspace skill instead.\",\n\t\t\t);\n\t\t}\n\t\treturn explicit.filter(isPluginPlatform);\n\t}\n\tconst fromSession = (sessionPlatforms ?? []).filter(isPluginPlatform);\n\treturn fromSession.length > 0 ? fromSession : [...DEFAULT_PLUGIN_PLATFORMS];\n}\n"]}
|
|
@@ -28,7 +28,17 @@ export interface RawManifest {
|
|
|
28
28
|
mcpServers?: Record<string, unknown> | string;
|
|
29
29
|
/** Native-only. */
|
|
30
30
|
providers?: unknown;
|
|
31
|
+
/** Copilot: extension directories, `{ paths, exclusive }`. Parsed for preservation only. */
|
|
32
|
+
extensions?: unknown;
|
|
33
|
+
/** Copilot: LSP config path or inline definitions. Parsed for preservation only. */
|
|
34
|
+
lspServers?: unknown;
|
|
35
|
+
/** Any key the adapter does not model. */
|
|
36
|
+
[key: string]: unknown;
|
|
31
37
|
}
|
|
38
|
+
/** Manifest keys not modelled here, preserved verbatim so a re-emit cannot drop them. */
|
|
39
|
+
export declare function unknownManifestFields(raw: RawManifest): Record<string, unknown> | undefined;
|
|
40
|
+
/** Which unsupported surfaces are present under `root`. */
|
|
41
|
+
export declare function detectUnsupportedSurfaces(root: string): string[] | undefined;
|
|
32
42
|
export declare function readJson<T>(file: string): T | null;
|
|
33
43
|
export declare function dirIfExists(root: string, name: string): string | undefined;
|
|
34
44
|
/**
|
|
@@ -75,4 +85,16 @@ export declare function toolsYamlList(tools: string | undefined): string | undef
|
|
|
75
85
|
* `<root>/commands/<name>.md`. Copilot's differs and lives in its adapter.
|
|
76
86
|
*/
|
|
77
87
|
export declare function claudeStyleWorkspace(root: string): WorkspaceLayout;
|
|
88
|
+
/** The manifest keys the adapters model, as a sorted list. Input to the drift check (§2.2). */
|
|
89
|
+
export declare function modelledManifestKeys(): string[];
|
|
90
|
+
/**
|
|
91
|
+
* Surfaces we know about and deliberately do not load, as the **relative paths**
|
|
92
|
+
* they actually occupy rather than their display labels.
|
|
93
|
+
*
|
|
94
|
+
* The distinction bit once already: the labels are written for humans
|
|
95
|
+
* (`"monitors/"`), the vendor reference writes the real path
|
|
96
|
+
* (`monitors/monitors.json`), and a drift check comparing the two reported a
|
|
97
|
+
* known surface as a new finding.
|
|
98
|
+
*/
|
|
99
|
+
export declare function knownUnsupportedSurfaces(): string[];
|
|
78
100
|
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -1 +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,eAAe,EAAE,MAAM,YAAY,CAAC;AAEhE,8FAA8F;AAC9F,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,gIAAgI;IAChI,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,iBAAiB,GAAG;QAAE,KAAK,CAAC,EAAE,iBAAiB,CAAA;KAAE,CAAC;IAC1D,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAC9C,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;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ9G;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;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,EACjD,IAAI,EAAE,MAAM,EACZ,WAAW,SAAc,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAOrC;AA2BD,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;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAO3E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAgBlE","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, WorkspaceLayout } from \"./types.js\";\n\n/** Raw manifest shape accepted by the `plugin.json`-style formats (agents/claude/copilot). */\nexport interface RawManifest {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string | { name?: string };\n\t/** Optional capability-dir override(s): a path (or list of paths) relative to the plugin root, e.g. `\"skills\": \"./skills/\"`. */\n\tskills?: unknown;\n\tcommands?: unknown;\n\tagents?: unknown;\n\tthemes?: unknown;\n\thooks?: PluginHooksConfig | { hooks?: PluginHooksConfig };\n\t/** Inline server map, or a path (Claude Code form, e.g. `\"./.mcp.json\"`) relative to the plugin root. */\n\tmcpServers?: Record<string, unknown> | string;\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/**\n * Resolve a capability directory: an explicit manifest override — a path (or\n * list of paths, first existing wins) relative to the plugin root, e.g.\n * `\"skills\": \"./skills/\"` — beats the conventional directory.\n */\nexport function resolveCapabilityDir(root: string, override: unknown, conventional: string): string | undefined {\n\tconst candidates = Array.isArray(override) ? override : [override];\n\tfor (const c of candidates) {\n\t\tif (typeof c !== \"string\" || !c.trim()) continue;\n\t\tconst p = path.resolve(root, c.trim());\n\t\tif (fs.existsSync(p) && fs.statSync(p).isDirectory()) return p;\n\t}\n\treturn dirIfExists(root, conventional);\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/**\n * Resolve inline `mcpServers`, a manifest-supplied file path, or a `.mcp.json`\n * file at `root`.\n *\n * `raw` may be:\n * - a string path (Claude Code form: `\"mcpServers\": \"./.mcp.json\"`), resolved\n * relative to the plugin root and read as a server file;\n * - an inline server map (returned as-is);\n * - undefined, falling back to `<root>/<mcpFileName>`.\n *\n * Server files use `{ mcpServers }` (Claude/native) or `{ servers }` (Code/Copilot);\n * both keys are accepted.\n */\nexport function normalizeMcp(\n\traw: Record<string, unknown> | string | undefined,\n\troot: string,\n\tmcpFileName = \".mcp.json\",\n): Record<string, unknown> | undefined {\n\t// Claude Code form: a path to a server file, relative to the plugin root.\n\tif (typeof raw === \"string\") {\n\t\treturn raw.trim() ? readMcpFile(path.resolve(root, raw.trim())) : undefined;\n\t}\n\tif (raw && Object.keys(raw).length > 0) return raw;\n\treturn readMcpFile(path.join(root, mcpFileName));\n}\n\n/** Read a server file, accepting both `{ mcpServers }` and `{ servers }` keys. */\nfunction readMcpFile(mcpFile: string): Record<string, unknown> | undefined {\n\tif (!fs.existsSync(mcpFile)) return undefined;\n\tconst fileRaw = readJson<{ mcpServers?: Record<string, unknown>; servers?: Record<string, unknown> }>(mcpFile);\n\tconst servers = fileRaw?.mcpServers ?? fileRaw?.servers;\n\treturn servers && Object.keys(servers).length > 0 ? servers : 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. */\nfunction 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\n/**\n * Render a comma-separated tool allowlist (Claude Code convention, e.g.\n * \"read, grep\") as a YAML flow sequence (`['read', 'grep']`) for formats whose\n * frontmatter takes tools as a list (Copilot custom agents / prompt files).\n */\nexport function toolsYamlList(tools: string | undefined): string | undefined {\n\tconst items = (tools ?? \"\")\n\t\t.split(/[,\\s]+/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\tif (items.length === 0) return undefined;\n\treturn `[${items.map((t) => `'${t.replace(/'/g, \"''\")}'`).join(\", \")}]`;\n}\n\n/**\n * The workspace layout shared by the `.agents/` and `.claude/` conventions:\n * `<root>/skills/<name>/SKILL.md`, `<root>/agents/<name>.md`,\n * `<root>/commands/<name>.md`. Copilot's differs and lives in its adapter.\n */\nexport function claudeStyleWorkspace(root: string): WorkspaceLayout {\n\treturn {\n\t\troot,\n\t\temitSkill: (s) => ({\n\t\t\tpath: path.join(root, \"skills\", slug(s.name), \"SKILL.md\"),\n\t\t\tcontent: emitMarkdown({ name: s.name, description: s.description }, s.body),\n\t\t}),\n\t\temitAgent: (a) => ({\n\t\t\tpath: path.join(root, \"agents\", `${slug(a.name)}.md`),\n\t\t\tcontent: emitMarkdown({ name: a.name, description: a.description, tools: a.tools, model: a.model }, a.body),\n\t\t}),\n\t\temitCommand: (c) => ({\n\t\t\tpath: path.join(root, \"commands\", `${slug(c.name)}.md`),\n\t\t\tcontent: emitMarkdown({ name: c.name, description: c.description }, c.body),\n\t\t}),\n\t};\n}\n"]}
|
|
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,eAAe,EAAE,MAAM,YAAY,CAAC;AAEhE,8FAA8F;AAC9F,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,gIAAgI;IAChI,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,iBAAiB,GAAG;QAAE,KAAK,CAAC,EAAE,iBAAiB,CAAA;KAAE,CAAC;IAC1D,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;IAC9C,mBAAmB;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oFAAoF;IACpF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0CAA0C;IAC1C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AA0BD,yFAAyF;AACzF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAM3F;AAkBD,2DAA2D;AAC3D,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAG5E;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;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ9G;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;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,EACjD,IAAI,EAAE,MAAM,EACZ,WAAW,SAAc,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAOrC;AAmCD,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7F;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;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAO3E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAgBlE;AAED,gGAA+F;AAC/F,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD","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, WorkspaceLayout } from \"./types.js\";\n\n/** Raw manifest shape accepted by the `plugin.json`-style formats (agents/claude/copilot). */\nexport interface RawManifest {\n\tname?: string;\n\tversion?: string;\n\tdescription?: string;\n\tauthor?: string | { name?: string };\n\t/** Optional capability-dir override(s): a path (or list of paths) relative to the plugin root, e.g. `\"skills\": \"./skills/\"`. */\n\tskills?: unknown;\n\tcommands?: unknown;\n\tagents?: unknown;\n\tthemes?: unknown;\n\thooks?: PluginHooksConfig | { hooks?: PluginHooksConfig };\n\t/** Inline server map, or a path (Claude Code form, e.g. `\"./.mcp.json\"`) relative to the plugin root. */\n\tmcpServers?: Record<string, unknown> | string;\n\t/** Native-only. */\n\tproviders?: unknown;\n\t/** Copilot: extension directories, `{ paths, exclusive }`. Parsed for preservation only. */\n\textensions?: unknown;\n\t/** Copilot: LSP config path or inline definitions. Parsed for preservation only. */\n\tlspServers?: unknown;\n\t/** Any key the adapter does not model. */\n\t[key: string]: unknown;\n}\n\n/**\n * Manifest keys every adapter models; anything else is preserved via\n * `unknownFields`.\n *\n * Surfaced through {@link declaredVocabulary} so the Tier 2 drift check has\n * something to diff the vendor references against. A checker that inferred our\n * coverage by reading the parser would be checking its own inference; a declared\n * set is a claim the codebase makes and the check can falsify.\n */\nconst MODELLED_MANIFEST_KEYS = new Set([\n\t\"name\",\n\t\"version\",\n\t\"description\",\n\t\"author\",\n\t\"skills\",\n\t\"commands\",\n\t\"agents\",\n\t\"themes\",\n\t\"hooks\",\n\t\"mcpServers\",\n\t\"providers\",\n\t\"$schema\",\n]);\n\n/** Manifest keys not modelled here, preserved verbatim so a re-emit cannot drop them. */\nexport function unknownManifestFields(raw: RawManifest): Record<string, unknown> | undefined {\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(raw)) {\n\t\tif (!MODELLED_MANIFEST_KEYS.has(key)) out[key] = value;\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * On-disk component surfaces hoocode does not load, checked against both vendors'\n * file-location tables. The files are left alone; naming them is what makes the\n * gap visible (see NormalizedPlugin.unsupportedSurfaces).\n */\nconst UNSUPPORTED_SURFACES: ReadonlyArray<{ rel: string; label: string }> = [\n\t{ rel: \"workflows\", label: \"workflows/\" },\n\t{ rel: \"output-styles\", label: \"output-styles/\" },\n\t{ rel: \"monitors/monitors.json\", label: \"monitors/\" },\n\t{ rel: \"bin\", label: \"bin/\" },\n\t{ rel: \"settings.json\", label: \"settings.json\" },\n\t{ rel: \".lsp.json\", label: \".lsp.json\" },\n\t{ rel: \"lsp.json\", label: \"lsp.json\" },\n\t{ rel: path.join(\".github\", \"lsp.json\"), label: \".github/lsp.json\" },\n];\n\n/** Which unsupported surfaces are present under `root`. */\nexport function detectUnsupportedSurfaces(root: string): string[] | undefined {\n\tconst found = UNSUPPORTED_SURFACES.filter((s) => fs.existsSync(path.join(root, s.rel))).map((s) => s.label);\n\treturn found.length > 0 ? found : undefined;\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/**\n * Resolve a capability directory: an explicit manifest override — a path (or\n * list of paths, first existing wins) relative to the plugin root, e.g.\n * `\"skills\": \"./skills/\"` — beats the conventional directory.\n */\nexport function resolveCapabilityDir(root: string, override: unknown, conventional: string): string | undefined {\n\tconst candidates = Array.isArray(override) ? override : [override];\n\tfor (const c of candidates) {\n\t\tif (typeof c !== \"string\" || !c.trim()) continue;\n\t\tconst p = path.resolve(root, c.trim());\n\t\tif (fs.existsSync(p) && fs.statSync(p).isDirectory()) return p;\n\t}\n\treturn dirIfExists(root, conventional);\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/**\n * Resolve inline `mcpServers`, a manifest-supplied file path, or a `.mcp.json`\n * file at `root`.\n *\n * `raw` may be:\n * - a string path (Claude Code form: `\"mcpServers\": \"./.mcp.json\"`), resolved\n * relative to the plugin root and read as a server file;\n * - an inline server map (returned as-is);\n * - undefined, falling back to `<root>/<mcpFileName>`.\n *\n * Server files use `{ mcpServers }` (Claude/native) or `{ servers }` (Code/Copilot);\n * both keys are accepted.\n */\nexport function normalizeMcp(\n\traw: Record<string, unknown> | string | undefined,\n\troot: string,\n\tmcpFileName = \".mcp.json\",\n): Record<string, unknown> | undefined {\n\t// Claude Code form: a path to a server file, relative to the plugin root.\n\tif (typeof raw === \"string\") {\n\t\treturn raw.trim() ? readMcpFile(path.resolve(root, raw.trim())) : undefined;\n\t}\n\tif (raw && Object.keys(raw).length > 0) return raw;\n\treturn readMcpFile(path.join(root, mcpFileName));\n}\n\n/** Read a server file, accepting both `{ mcpServers }` and `{ servers }` keys. */\nfunction readMcpFile(mcpFile: string): Record<string, unknown> | undefined {\n\tif (!fs.existsSync(mcpFile)) return undefined;\n\tconst fileRaw = readJson<{ mcpServers?: Record<string, unknown>; servers?: Record<string, unknown> }>(mcpFile);\n\tconst servers = fileRaw?.mcpServers ?? fileRaw?.servers;\n\treturn servers && Object.keys(servers).length > 0 ? servers : 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. */\n/**\n * Render frontmatter, or nothing at all when every field is empty.\n *\n * Emitting bare `---\\n---` looks harmless and is not: YAML parses an empty\n * document as `null`, and Claude Code rejects a component whose frontmatter is\n * not a mapping. A command authored without a description hit exactly that, and\n * the artifact was invalid in the ecosystem it was written for while\n * round-tripping happily through our own reader.\n */\nfunction 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\treturn lines.length > 0 ? [\"---\", ...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\tconst frontmatter = emitFrontmatter(fields);\n\treturn frontmatter ? `${frontmatter}\\n\\n${body.trimEnd()}\\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\n/**\n * Render a comma-separated tool allowlist (Claude Code convention, e.g.\n * \"read, grep\") as a YAML flow sequence (`['read', 'grep']`) for formats whose\n * frontmatter takes tools as a list (Copilot custom agents / prompt files).\n */\nexport function toolsYamlList(tools: string | undefined): string | undefined {\n\tconst items = (tools ?? \"\")\n\t\t.split(/[,\\s]+/)\n\t\t.map((t) => t.trim())\n\t\t.filter(Boolean);\n\tif (items.length === 0) return undefined;\n\treturn `[${items.map((t) => `'${t.replace(/'/g, \"''\")}'`).join(\", \")}]`;\n}\n\n/**\n * The workspace layout shared by the `.agents/` and `.claude/` conventions:\n * `<root>/skills/<name>/SKILL.md`, `<root>/agents/<name>.md`,\n * `<root>/commands/<name>.md`. Copilot's differs and lives in its adapter.\n */\nexport function claudeStyleWorkspace(root: string): WorkspaceLayout {\n\treturn {\n\t\troot,\n\t\temitSkill: (s) => ({\n\t\t\tpath: path.join(root, \"skills\", slug(s.name), \"SKILL.md\"),\n\t\t\tcontent: emitMarkdown({ name: s.name, description: s.description }, s.body),\n\t\t}),\n\t\temitAgent: (a) => ({\n\t\t\tpath: path.join(root, \"agents\", `${slug(a.name)}.md`),\n\t\t\tcontent: emitMarkdown({ name: a.name, description: a.description, tools: a.tools, model: a.model }, a.body),\n\t\t}),\n\t\temitCommand: (c) => ({\n\t\t\tpath: path.join(root, \"commands\", `${slug(c.name)}.md`),\n\t\t\tcontent: emitMarkdown({ name: c.name, description: c.description }, c.body),\n\t\t}),\n\t};\n}\n\n/** The manifest keys the adapters model, as a sorted list. Input to the drift check (§2.2). */\nexport function modelledManifestKeys(): string[] {\n\treturn [...MODELLED_MANIFEST_KEYS].sort();\n}\n\n/**\n * Surfaces we know about and deliberately do not load, as the **relative paths**\n * they actually occupy rather than their display labels.\n *\n * The distinction bit once already: the labels are written for humans\n * (`\"monitors/\"`), the vendor reference writes the real path\n * (`monitors/monitors.json`), and a drift check comparing the two reported a\n * known surface as a new finding.\n */\nexport function knownUnsupportedSurfaces(): string[] {\n\treturn UNSUPPORTED_SURFACES.map((s) => s.rel.replace(/\\\\/g, \"/\")).sort();\n}\n"]}
|
|
@@ -8,6 +8,58 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
10
|
import * as path from "node:path";
|
|
11
|
+
/**
|
|
12
|
+
* Manifest keys every adapter models; anything else is preserved via
|
|
13
|
+
* `unknownFields`.
|
|
14
|
+
*
|
|
15
|
+
* Surfaced through {@link declaredVocabulary} so the Tier 2 drift check has
|
|
16
|
+
* something to diff the vendor references against. A checker that inferred our
|
|
17
|
+
* coverage by reading the parser would be checking its own inference; a declared
|
|
18
|
+
* set is a claim the codebase makes and the check can falsify.
|
|
19
|
+
*/
|
|
20
|
+
const MODELLED_MANIFEST_KEYS = new Set([
|
|
21
|
+
"name",
|
|
22
|
+
"version",
|
|
23
|
+
"description",
|
|
24
|
+
"author",
|
|
25
|
+
"skills",
|
|
26
|
+
"commands",
|
|
27
|
+
"agents",
|
|
28
|
+
"themes",
|
|
29
|
+
"hooks",
|
|
30
|
+
"mcpServers",
|
|
31
|
+
"providers",
|
|
32
|
+
"$schema",
|
|
33
|
+
]);
|
|
34
|
+
/** Manifest keys not modelled here, preserved verbatim so a re-emit cannot drop them. */
|
|
35
|
+
export function unknownManifestFields(raw) {
|
|
36
|
+
const out = {};
|
|
37
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
38
|
+
if (!MODELLED_MANIFEST_KEYS.has(key))
|
|
39
|
+
out[key] = value;
|
|
40
|
+
}
|
|
41
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* On-disk component surfaces hoocode does not load, checked against both vendors'
|
|
45
|
+
* file-location tables. The files are left alone; naming them is what makes the
|
|
46
|
+
* gap visible (see NormalizedPlugin.unsupportedSurfaces).
|
|
47
|
+
*/
|
|
48
|
+
const UNSUPPORTED_SURFACES = [
|
|
49
|
+
{ rel: "workflows", label: "workflows/" },
|
|
50
|
+
{ rel: "output-styles", label: "output-styles/" },
|
|
51
|
+
{ rel: "monitors/monitors.json", label: "monitors/" },
|
|
52
|
+
{ rel: "bin", label: "bin/" },
|
|
53
|
+
{ rel: "settings.json", label: "settings.json" },
|
|
54
|
+
{ rel: ".lsp.json", label: ".lsp.json" },
|
|
55
|
+
{ rel: "lsp.json", label: "lsp.json" },
|
|
56
|
+
{ rel: path.join(".github", "lsp.json"), label: ".github/lsp.json" },
|
|
57
|
+
];
|
|
58
|
+
/** Which unsupported surfaces are present under `root`. */
|
|
59
|
+
export function detectUnsupportedSurfaces(root) {
|
|
60
|
+
const found = UNSUPPORTED_SURFACES.filter((s) => fs.existsSync(path.join(root, s.rel))).map((s) => s.label);
|
|
61
|
+
return found.length > 0 ? found : undefined;
|
|
62
|
+
}
|
|
11
63
|
export function readJson(file) {
|
|
12
64
|
try {
|
|
13
65
|
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
@@ -95,8 +147,17 @@ function readMcpFile(mcpFile) {
|
|
|
95
147
|
// Emit helpers (used by the writer / scaffolding half of each adapter)
|
|
96
148
|
// ============================================================================
|
|
97
149
|
/** Serialize a small set of frontmatter fields to YAML. Values are strings only. */
|
|
150
|
+
/**
|
|
151
|
+
* Render frontmatter, or nothing at all when every field is empty.
|
|
152
|
+
*
|
|
153
|
+
* Emitting bare `---\n---` looks harmless and is not: YAML parses an empty
|
|
154
|
+
* document as `null`, and Claude Code rejects a component whose frontmatter is
|
|
155
|
+
* not a mapping. A command authored without a description hit exactly that, and
|
|
156
|
+
* the artifact was invalid in the ecosystem it was written for while
|
|
157
|
+
* round-tripping happily through our own reader.
|
|
158
|
+
*/
|
|
98
159
|
function emitFrontmatter(fields) {
|
|
99
|
-
const lines = [
|
|
160
|
+
const lines = [];
|
|
100
161
|
for (const [key, value] of Object.entries(fields)) {
|
|
101
162
|
if (value === undefined || value === "")
|
|
102
163
|
continue;
|
|
@@ -104,12 +165,12 @@ function emitFrontmatter(fields) {
|
|
|
104
165
|
const needsQuote = /[:#]|^[\s>|@`&*!%]/.test(value) || value.includes("\n");
|
|
105
166
|
lines.push(`${key}: ${needsQuote ? JSON.stringify(value) : value}`);
|
|
106
167
|
}
|
|
107
|
-
lines.
|
|
108
|
-
return lines.join("\n");
|
|
168
|
+
return lines.length > 0 ? ["---", ...lines, "---"].join("\n") : "";
|
|
109
169
|
}
|
|
110
170
|
/** A markdown capability file: frontmatter block + body. */
|
|
111
171
|
export function emitMarkdown(fields, body) {
|
|
112
|
-
|
|
172
|
+
const frontmatter = emitFrontmatter(fields);
|
|
173
|
+
return frontmatter ? `${frontmatter}\n\n${body.trimEnd()}\n` : `${body.trimEnd()}\n`;
|
|
113
174
|
}
|
|
114
175
|
/** Pretty-print a JSON manifest with a trailing newline (matches repo convention). */
|
|
115
176
|
export function emitJson(value) {
|
|
@@ -172,4 +233,20 @@ export function claudeStyleWorkspace(root) {
|
|
|
172
233
|
}),
|
|
173
234
|
};
|
|
174
235
|
}
|
|
236
|
+
/** The manifest keys the adapters model, as a sorted list. Input to the drift check (§2.2). */
|
|
237
|
+
export function modelledManifestKeys() {
|
|
238
|
+
return [...MODELLED_MANIFEST_KEYS].sort();
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Surfaces we know about and deliberately do not load, as the **relative paths**
|
|
242
|
+
* they actually occupy rather than their display labels.
|
|
243
|
+
*
|
|
244
|
+
* The distinction bit once already: the labels are written for humans
|
|
245
|
+
* (`"monitors/"`), the vendor reference writes the real path
|
|
246
|
+
* (`monitors/monitors.json`), and a drift check comparing the two reported a
|
|
247
|
+
* known surface as a new finding.
|
|
248
|
+
*/
|
|
249
|
+
export function knownUnsupportedSurfaces() {
|
|
250
|
+
return UNSUPPORTED_SURFACES.map((s) => s.rel.replace(/\\/g, "/")).sort();
|
|
251
|
+
}
|
|
175
252
|
//# sourceMappingURL=shared.js.map
|