@jianxx/dsh-cc-plugin-loader 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +201 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +45 -0
  4. package/README.zh.md +45 -0
  5. package/lib/agents.d.ts +109 -0
  6. package/lib/agents.d.ts.map +1 -0
  7. package/lib/agents.js +147 -0
  8. package/lib/agents.js.map +1 -0
  9. package/lib/commands.d.ts +56 -0
  10. package/lib/commands.d.ts.map +1 -0
  11. package/lib/commands.js +66 -0
  12. package/lib/commands.js.map +1 -0
  13. package/lib/hooks.d.ts +44 -0
  14. package/lib/hooks.d.ts.map +1 -0
  15. package/lib/hooks.js +89 -0
  16. package/lib/hooks.js.map +1 -0
  17. package/lib/index.d.ts +73 -0
  18. package/lib/index.d.ts.map +1 -0
  19. package/lib/index.js +92 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/manifest.d.ts +23 -0
  22. package/lib/manifest.d.ts.map +1 -0
  23. package/lib/manifest.js +117 -0
  24. package/lib/manifest.js.map +1 -0
  25. package/lib/mcp.d.ts +41 -0
  26. package/lib/mcp.d.ts.map +1 -0
  27. package/lib/mcp.js +64 -0
  28. package/lib/mcp.js.map +1 -0
  29. package/lib/seams.d.ts +59 -0
  30. package/lib/seams.d.ts.map +1 -0
  31. package/lib/seams.js +56 -0
  32. package/lib/seams.js.map +1 -0
  33. package/lib/settings.d.ts +41 -0
  34. package/lib/settings.d.ts.map +1 -0
  35. package/lib/settings.js +44 -0
  36. package/lib/settings.js.map +1 -0
  37. package/lib/skill-semantics.d.ts +92 -0
  38. package/lib/skill-semantics.d.ts.map +1 -0
  39. package/lib/skill-semantics.js +98 -0
  40. package/lib/skill-semantics.js.map +1 -0
  41. package/lib/skills.d.ts +49 -0
  42. package/lib/skills.d.ts.map +1 -0
  43. package/lib/skills.js +117 -0
  44. package/lib/skills.js.map +1 -0
  45. package/lib/types.d.ts +89 -0
  46. package/lib/types.d.ts.map +1 -0
  47. package/lib/types.js +12 -0
  48. package/lib/types.js.map +1 -0
  49. package/package.json +57 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Mount a Claude Code plugin's slash commands.
3
+ *
4
+ * Translates manifest `commands` entries (inline content or a source file) into
5
+ * typed command definitions registered on the commands seam. Each handler
6
+ * returns the command's rendered content, so invoking `/name` surfaces the
7
+ * plugin author's instructions. Registration is effect-scoped.
8
+ *
9
+ * @module
10
+ */
11
+ import { readFileSync } from 'node:fs';
12
+ import { resolve } from 'node:path';
13
+ import { ComponentTally } from "./seams.js";
14
+ /** Commands live under this directory in a plugin root, when present. */
15
+ export const STANDARD_COMMANDS_DIR = 'commands';
16
+ /**
17
+ * Register a plugin's manifest commands.
18
+ * @param options - plugin root, manifest, and the commands seam.
19
+ * @returns mounted disposers and per-component counts.
20
+ */
21
+ export function mountCommands(options) {
22
+ const tally = new ComponentTally('commands');
23
+ const disposers = [];
24
+ if (options.commands === undefined) {
25
+ tally.addSkipped('commands seam "commands" is not mounted');
26
+ return { disposers, tally };
27
+ }
28
+ const entries = options.manifest.commands;
29
+ if (entries.length === 0) {
30
+ tally.addSkipped('plugin ships no commands');
31
+ return { disposers, tally };
32
+ }
33
+ for (const entry of entries) {
34
+ const rendered = renderCommand(options.pluginRoot, entry);
35
+ if (rendered.error !== undefined) {
36
+ tally.addFailed(`command "${entry.name}": ${rendered.error}`);
37
+ continue;
38
+ }
39
+ disposers.push(options.commands.register({
40
+ name: entry.name,
41
+ description: entry.description ?? '',
42
+ handler: () => rendered.content === undefined
43
+ ? { kind: 'success' }
44
+ : { kind: 'success', text: rendered.content },
45
+ }));
46
+ tally.addLoaded();
47
+ }
48
+ return { disposers, tally };
49
+ }
50
+ /** Resolve a command entry to consumable content, or a failure reason. */
51
+ function renderCommand(pluginRoot, entry) {
52
+ if (entry.content !== undefined) {
53
+ return { content: entry.content };
54
+ }
55
+ if (entry.source !== undefined) {
56
+ const path = resolve(pluginRoot, entry.source);
57
+ try {
58
+ return { content: readFileSync(path, 'utf8') };
59
+ }
60
+ catch (error) {
61
+ return { error: `could not read command file "${entry.source}": ${String(error)}` };
62
+ }
63
+ }
64
+ return { error: 'has neither "content" nor a readable "source"' };
65
+ }
66
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAyB3C,yEAAyE;AACzE,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAA;AAY/C;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,OAA6B;IACzD,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,UAAU,CAAC,CAAA;IAC5C,MAAM,SAAS,GAAmB,EAAE,CAAA;IACpC,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,KAAK,CAAC,UAAU,CAAC,yCAAyC,CAAC,CAAA;QAC3D,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IAC7B,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAA;IACzC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAA;QAC5C,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IAC7B,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,CAAA;QACzD,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACjC,KAAK,CAAC,SAAS,CAAC,YAAY,KAAK,CAAC,IAAI,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;YAC7D,SAAQ;QACV,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACvC,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,EAAE;YACpC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,KAAK,SAAS;gBAC3C,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;gBACrB,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE;SAChD,CAAC,CAAC,CAAA;QACH,KAAK,CAAC,SAAS,EAAE,CAAA;IACnB,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;AAC7B,CAAC;AAED,0EAA0E;AAC1E,SAAS,aAAa,CAAC,UAAkB,EAAE,KAAgB;IACzD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;IACnC,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;QAC9C,IAAI,CAAC;YACH,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAA;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,KAAK,EAAE,gCAAgC,KAAK,CAAC,MAAM,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAA;QACrF,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAA;AACnE,CAAC"}
package/lib/hooks.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Mount a Claude Code plugin's hooks.
3
+ *
4
+ * Reads the plugin's `hooks/hooks.json` (or an inline manifest `hooks`
5
+ * declaration), validates the structure, and injects the hooks into the
6
+ * hooks-claude-code bridge through the optional `hooks` guest seam. When the
7
+ * seam is absent the component is reported skipped, never failed, so a
8
+ * deployment without the bridge keeps loading the rest of the plugin.
9
+ *
10
+ * @module
11
+ */
12
+ import type { CcPluginManifest } from './types.ts';
13
+ import { ComponentTally } from './seams.ts';
14
+ /** The hooks seam: accepts a plugin's translated per-event hooks. */
15
+ export interface HooksSeam {
16
+ /**
17
+ * Merge a plugin's hooks into the bridge.
18
+ * @param pluginName - the plugin that owns the hooks.
19
+ * @param config - the per-event hook map (`ClaudeCodeHookConfig` shape).
20
+ * @returns the exact disposer that removes the injected hooks.
21
+ */
22
+ mergePluginHooks(pluginName: string, config: unknown): () => void;
23
+ }
24
+ /** Hooks live under this file in a plugin root, when present. */
25
+ export declare const STANDARD_HOOKS_FILE = "hooks/hooks.json";
26
+ /** Options for mounting one plugin's hooks. */
27
+ export interface MountHooksOptions {
28
+ /** The plugin root directory; the standard hooks file resolves against it. */
29
+ readonly pluginRoot: string;
30
+ /** The parsed manifest; `hooks` supplies an inline or file reference. */
31
+ readonly manifest: CcPluginManifest;
32
+ /** The hooks seam (probed; `undefined` to skip hooks). */
33
+ readonly hooks: HooksSeam | undefined;
34
+ }
35
+ /**
36
+ * Read and inject a plugin's hooks through the optional seam.
37
+ * @param options - plugin root, manifest, and the hooks seam.
38
+ * @returns mounted disposers and per-component counts.
39
+ */
40
+ export declare function mountHooks(options: MountHooksOptions): {
41
+ disposers: (() => void)[];
42
+ tally: ComponentTally;
43
+ };
44
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB;;;;;OAKG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,CAAA;CAClE;AAED,iEAAiE;AACjE,eAAO,MAAM,mBAAmB,qBAAqB,CAAA;AAErD,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAChC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAA;CACtC;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG;IAAE,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAmB3G"}
package/lib/hooks.js ADDED
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Mount a Claude Code plugin's hooks.
3
+ *
4
+ * Reads the plugin's `hooks/hooks.json` (or an inline manifest `hooks`
5
+ * declaration), validates the structure, and injects the hooks into the
6
+ * hooks-claude-code bridge through the optional `hooks` guest seam. When the
7
+ * seam is absent the component is reported skipped, never failed, so a
8
+ * deployment without the bridge keeps loading the rest of the plugin.
9
+ *
10
+ * @module
11
+ */
12
+ import { readFileSync } from 'node:fs';
13
+ import { join, resolve } from 'node:path';
14
+ import { ComponentTally } from "./seams.js";
15
+ /** Hooks live under this file in a plugin root, when present. */
16
+ export const STANDARD_HOOKS_FILE = 'hooks/hooks.json';
17
+ /**
18
+ * Read and inject a plugin's hooks through the optional seam.
19
+ * @param options - plugin root, manifest, and the hooks seam.
20
+ * @returns mounted disposers and per-component counts.
21
+ */
22
+ export function mountHooks(options) {
23
+ const tally = new ComponentTally('hooks');
24
+ const disposers = [];
25
+ if (options.hooks === undefined) {
26
+ tally.addSkipped('hooks seam "hooks" is not mounted');
27
+ return { disposers, tally };
28
+ }
29
+ const hooks = resolveHooks(options.pluginRoot, options.manifest);
30
+ if (hooks.error !== undefined) {
31
+ tally.addFailed(hooks.error);
32
+ return { disposers, tally };
33
+ }
34
+ if (hooks.value === undefined) {
35
+ tally.addSkipped('plugin declares no hooks');
36
+ return { disposers, tally };
37
+ }
38
+ disposers.push(options.hooks.mergePluginHooks(options.manifest.name, hooks.value));
39
+ tally.addLoaded();
40
+ return { disposers, tally };
41
+ }
42
+ /** Resolve the plugin's hooks map, or a failure reason. */
43
+ function resolveHooks(pluginRoot, manifest) {
44
+ const inline = manifest.hooks;
45
+ if (typeof inline === 'string') {
46
+ return readHooksFile(resolve(pluginRoot, inline), `manifest hooks path "${inline}"`);
47
+ }
48
+ if (inline !== undefined) {
49
+ if (!isRecord(inline)) {
50
+ return { error: 'manifest "hooks" must be an object or a hooks file path' };
51
+ }
52
+ const fromInline = inline['hooks'] !== undefined ? inline['hooks'] : inline;
53
+ return validateHooks(fromInline);
54
+ }
55
+ return readHooksFile(join(pluginRoot, STANDARD_HOOKS_FILE), 'hooks/hooks.json');
56
+ }
57
+ /** Read and validate a hooks JSON file (absent file means no hooks). */
58
+ function readHooksFile(path, label) {
59
+ let raw;
60
+ try {
61
+ raw = readFileSync(path, 'utf8');
62
+ }
63
+ catch {
64
+ return { value: undefined }; // absent standard hooks file is valid empty state
65
+ }
66
+ let parsed;
67
+ try {
68
+ parsed = JSON.parse(raw);
69
+ }
70
+ catch {
71
+ return { error: `could not parse ${label}` };
72
+ }
73
+ return validateHooks(parsed);
74
+ }
75
+ /** Validate a hooks value into a per-event map. */
76
+ function validateHooks(value) {
77
+ if (!isRecord(value)) {
78
+ return { error: 'hooks must be an object keyed by event name' };
79
+ }
80
+ const hooks = (value['hooks'] !== undefined ? value['hooks'] : value);
81
+ if (!isRecord(hooks)) {
82
+ return { error: 'hooks "hooks" field must be an object keyed by event name' };
83
+ }
84
+ return { value: hooks };
85
+ }
86
+ function isRecord(value) {
87
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
88
+ }
89
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * A Claude Code `plugin.json` compatibility loader: reads a CC plugin manifest
3
+ * and mounts each component as an in-memory dsh plugin.
4
+ *
5
+ * The loader is peer-style: it parses the manifest subset, translates each
6
+ * component with the pure helpers from `dsh-skill-claude-code` and
7
+ * `dsh-claude-code-agents`, then consults the host seam for that component via
8
+ * `ctx.get(...)`. A component whose seam is absent is reported skipped (never a
9
+ * whole-load failure), matching "misconfiguration fails loud" for the manifest
10
+ * itself but graceful degradation for missing host seams. Every component
11
+ * mount is a Cordis effect, so disabling the plugin recalls all of it.
12
+ *
13
+ * @module @jianxx/dsh-cc-plugin-loader
14
+ */
15
+ import type { Context } from '@deepseek-ai/cordis';
16
+ import type { HooksSeam } from './hooks.ts';
17
+ import type { McpSeam } from './mcp.ts';
18
+ import type { CommandsSeam } from './commands.ts';
19
+ import type { SettingsSeam } from './settings.ts';
20
+ import type { PluginLoadReport } from './types.ts';
21
+ import { type SkillsSeam } from './skills.ts';
22
+ import { type ResolveModel, type SubagentsSeam } from './agents.ts';
23
+ export type { CcPluginManifest, CcCommand, CcSkillRef, CcAgentRef, CcMcpServer, ComponentKind, ComponentResult, PluginLoadReport } from './types.ts';
24
+ export { parsePluginManifest } from './manifest.ts';
25
+ export { AgentProvider, STANDARD_AGENTS_DIR } from './agents.ts';
26
+ export type { ResolveModel } from './agents.ts';
27
+ export type { McpSeam, HooksSeam } from './seams.ts';
28
+ export { skillToolRestriction, resolveSkillExecution, forbidsInlineShell, activationFor, registerSkillPathActivator, applySkillRestriction, PROVIDER, type AgentScope, type SkillExecution, type SkillActivation, } from './skill-semantics.ts';
29
+ /** The plugin.json file name at a plugin root. */
30
+ export declare const MANIFEST_FILE = "plugin.json";
31
+ /** The component host seams the loader probes. */
32
+ export interface MountedSeams {
33
+ /** Skill registry seam. */
34
+ skills?: SkillsSeam | undefined;
35
+ /** Subagent registry seam. */
36
+ subagents?: SubagentsSeam | undefined;
37
+ /** Command registry seam. */
38
+ commands?: CommandsSeam | undefined;
39
+ /** Settings seam. */
40
+ settings?: SettingsSeam | undefined;
41
+ /** Hooks bridge seam (guest; absent in the harness today). */
42
+ hooks?: HooksSeam | undefined;
43
+ /** MCP server seam (guest; absent in the harness today). */
44
+ mcp?: McpSeam | undefined;
45
+ }
46
+ /** Options for mounting one Claude Code plugin. */
47
+ export interface MountCcPluginOptions {
48
+ /** The plugin root directory holding `plugin.json` and its components. */
49
+ readonly root: string;
50
+ /** Optional seam overrides; when omitted the loader probes `ctx.get(...)`. */
51
+ readonly seams?: MountedSeams;
52
+ /** Optional spawn-time model resolver threaded into every mounted agent. */
53
+ readonly resolveModel?: ResolveModel;
54
+ }
55
+ /** The structural report plus a disposer that recalls every mounted component. */
56
+ export interface CcPluginMount {
57
+ /** The structural load report. */
58
+ report: PluginLoadReport;
59
+ /**
60
+ * Recall every mounted component. Effect-scoped: calling it also releases
61
+ * the Cordis effect, and a context teardown calls it automatically.
62
+ */
63
+ dispose(): void;
64
+ }
65
+ /**
66
+ * Load a Claude Code plugin manifest and mount its components.
67
+ * @param ctx - active context carrying the component host seams.
68
+ * @param options - plugin root and optional seam overrides.
69
+ * @returns the structural report and a disposer that recalls every mount.
70
+ * @throws when the manifest itself is invalid (with the plugin path/name).
71
+ */
72
+ export declare function mountCcPlugin(ctx: Context, options: MountCcPluginOptions): Promise<CcPluginMount>;
73
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAmB,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACnE,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EAAe,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAA;AAMhF,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACpJ,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAChE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,aAAa,EACb,0BAA0B,EAC1B,qBAAqB,EACrB,QAAQ,EACR,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,sBAAsB,CAAA;AAE7B,kDAAkD;AAClD,eAAO,MAAM,aAAa,gBAAgB,CAAA;AAE1C,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;IAC/B,8BAA8B;IAC9B,SAAS,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;IACrC,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IACnC,qBAAqB;IACrB,QAAQ,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IACnC,8DAA8D;IAC9D,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;IAC7B,4DAA4D;IAC5D,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC1B;AAED,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAA;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CACrC;AAED,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,kCAAkC;IAClC,MAAM,EAAE,gBAAgB,CAAA;IACxB;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAA;CAChB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,aAAa,CAAC,CAmCvG"}
package/lib/index.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * A Claude Code `plugin.json` compatibility loader: reads a CC plugin manifest
3
+ * and mounts each component as an in-memory dsh plugin.
4
+ *
5
+ * The loader is peer-style: it parses the manifest subset, translates each
6
+ * component with the pure helpers from `dsh-skill-claude-code` and
7
+ * `dsh-claude-code-agents`, then consults the host seam for that component via
8
+ * `ctx.get(...)`. A component whose seam is absent is reported skipped (never a
9
+ * whole-load failure), matching "misconfiguration fails loud" for the manifest
10
+ * itself but graceful degradation for missing host seams. Every component
11
+ * mount is a Cordis effect, so disabling the plugin recalls all of it.
12
+ *
13
+ * @module @jianxx/dsh-cc-plugin-loader
14
+ */
15
+ import { readFile } from 'node:fs/promises';
16
+ import { resolve } from 'node:path';
17
+ import { parsePluginManifest } from "./manifest.js";
18
+ import { mountSkills } from "./skills.js";
19
+ import { mountAgents } from "./agents.js";
20
+ import { mountCommands } from "./commands.js";
21
+ import { mountHooks } from "./hooks.js";
22
+ import { mountMcpServers } from "./mcp.js";
23
+ import { mountSettings } from "./settings.js";
24
+ export { parsePluginManifest } from "./manifest.js";
25
+ export { AgentProvider, STANDARD_AGENTS_DIR } from "./agents.js";
26
+ export { skillToolRestriction, resolveSkillExecution, forbidsInlineShell, activationFor, registerSkillPathActivator, applySkillRestriction, PROVIDER, } from "./skill-semantics.js";
27
+ /** The plugin.json file name at a plugin root. */
28
+ export const MANIFEST_FILE = 'plugin.json';
29
+ /**
30
+ * Load a Claude Code plugin manifest and mount its components.
31
+ * @param ctx - active context carrying the component host seams.
32
+ * @param options - plugin root and optional seam overrides.
33
+ * @returns the structural report and a disposer that recalls every mount.
34
+ * @throws when the manifest itself is invalid (with the plugin path/name).
35
+ */
36
+ export async function mountCcPlugin(ctx, options) {
37
+ const root = resolve(options.root);
38
+ const raw = await readManifest(root);
39
+ const manifest = parsePluginManifest(raw, root);
40
+ const probed = await probeSeams(ctx, options.seams);
41
+ const disposers = [];
42
+ const components = [];
43
+ fold(components, disposers, await mountSkills({
44
+ ctx,
45
+ pluginRoot: root,
46
+ manifest,
47
+ skills: probed.skills,
48
+ subagentsPresent: probed.subagents !== undefined,
49
+ }));
50
+ fold(components, disposers, await mountAgents({
51
+ pluginRoot: root,
52
+ manifest,
53
+ subagents: probed.subagents,
54
+ ...options.resolveModel !== undefined ? { resolveModel: options.resolveModel } : {},
55
+ }));
56
+ fold(components, disposers, mountCommands({ pluginRoot: root, manifest, commands: probed.commands }));
57
+ fold(components, disposers, mountHooks({ pluginRoot: root, manifest, hooks: probed.hooks }));
58
+ fold(components, disposers, mountMcpServers({ pluginRoot: root, manifest, mcp: probed.mcp }));
59
+ fold(components, disposers, mountSettings({ manifest, settings: probed.settings }));
60
+ const tearDown = () => {
61
+ for (const dispose of disposers)
62
+ dispose();
63
+ };
64
+ const effectDisposer = ctx.effect(() => tearDown, 'cc-plugin-loader.mount');
65
+ return {
66
+ report: { name: manifest.name, components },
67
+ dispose: () => effectDisposer(),
68
+ };
69
+ }
70
+ /** Read and JSON-parse the plugin manifest file. */
71
+ async function readManifest(root) {
72
+ const path = resolve(root, MANIFEST_FILE);
73
+ const raw = await readFile(path, 'utf8');
74
+ return JSON.parse(raw);
75
+ }
76
+ /** Probe each component host seam, preferring explicit overrides. */
77
+ async function probeSeams(ctx, overrides) {
78
+ return {
79
+ skills: overrides?.skills ?? ctx.get('skills'),
80
+ subagents: overrides?.subagents ?? ctx.get('subagents'),
81
+ commands: overrides?.commands ?? ctx.get('commands'),
82
+ settings: overrides?.settings ?? ctx.get('settings'),
83
+ hooks: overrides?.hooks ?? ctx.get('hooks'),
84
+ mcp: overrides?.mcp ?? ctx.get('mcp'),
85
+ };
86
+ }
87
+ /** Fold one component mount into the report and disposer list. */
88
+ function fold(components, disposers, mount) {
89
+ components.push(mount.tally.result());
90
+ disposers.push(...mount.disposers);
91
+ }
92
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Parse the Claude Code `plugin.json` manifest subset this loader consumes.
3
+ *
4
+ * Validation happens on the raw manifest object at load time: a malformed
5
+ * manifest (wrong types, a blank or space-containing plugin name, an unreadable
6
+ * file) throws with the plugin name so the failure is actionable. Component
7
+ * fields accept the union shapes Claude Code allows (single path, array, or
8
+ * object map for commands) and are normalized to the loader's typed view.
9
+ * Unknown top-level fields are ignored, matching Claude Code's tolerant
10
+ * top-level handling.
11
+ *
12
+ * @module
13
+ */
14
+ import type { CcPluginManifest } from './types.ts';
15
+ /**
16
+ * Validate a raw `plugin.json` object into the loader's typed manifest subset.
17
+ * @param raw - the parsed JSON contents of `plugin.json`.
18
+ * @param source - the plugin name or path used to prefix validation errors.
19
+ * @returns the normalized manifest subset.
20
+ * @throws when the manifest is structurally invalid.
21
+ */
22
+ export declare function parsePluginManifest(raw: unknown, source: string): CcPluginManifest;
23
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,YAAY,CAAA;AAE1E;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAuBlF"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Parse the Claude Code `plugin.json` manifest subset this loader consumes.
3
+ *
4
+ * Validation happens on the raw manifest object at load time: a malformed
5
+ * manifest (wrong types, a blank or space-containing plugin name, an unreadable
6
+ * file) throws with the plugin name so the failure is actionable. Component
7
+ * fields accept the union shapes Claude Code allows (single path, array, or
8
+ * object map for commands) and are normalized to the loader's typed view.
9
+ * Unknown top-level fields are ignored, matching Claude Code's tolerant
10
+ * top-level handling.
11
+ *
12
+ * @module
13
+ */
14
+ /**
15
+ * Validate a raw `plugin.json` object into the loader's typed manifest subset.
16
+ * @param raw - the parsed JSON contents of `plugin.json`.
17
+ * @param source - the plugin name or path used to prefix validation errors.
18
+ * @returns the normalized manifest subset.
19
+ * @throws when the manifest is structurally invalid.
20
+ */
21
+ export function parsePluginManifest(raw, source) {
22
+ if (!isRecord(raw)) {
23
+ throw new Error(`plugin ${source}: manifest must be a JSON object`);
24
+ }
25
+ const name = readName(raw['name'], source);
26
+ const commands = normalizeCommands(raw['commands'], name);
27
+ const agents = normalizeStringList(raw['agents'], name, 'agents');
28
+ const skills = normalizeStringList(raw['skills'], name, 'skills');
29
+ const { mcpServers, mcpServersPath } = normalizeMcpServers(raw['mcpServers'], name);
30
+ const settings = isRecord(raw['settings']) ? raw['settings'] : {};
31
+ return {
32
+ name,
33
+ ...typeof raw['version'] === 'string' ? { version: raw['version'] } : {},
34
+ ...typeof raw['description'] === 'string' ? { description: raw['description'] } : {},
35
+ ...raw['author'] !== undefined ? { author: raw['author'] } : {},
36
+ commands,
37
+ agents,
38
+ skills,
39
+ ...raw['hooks'] !== undefined ? { hooks: raw['hooks'] } : {},
40
+ mcpServers,
41
+ ...mcpServersPath !== undefined ? { mcpServersPath } : {},
42
+ settings,
43
+ };
44
+ }
45
+ /** Require and validate the mandatory plugin `name`. */
46
+ function readName(raw, source) {
47
+ if (typeof raw !== 'string' || raw.length === 0) {
48
+ throw new Error(`plugin ${source}: "name" must be a non-empty string`);
49
+ }
50
+ if (raw.includes(' ')) {
51
+ throw new Error(`plugin ${raw}: "name" cannot contain spaces; use kebab-case`);
52
+ }
53
+ return raw;
54
+ }
55
+ function normalizeCommands(raw, name) {
56
+ if (raw === undefined)
57
+ return [];
58
+ if (typeof raw === 'string')
59
+ return [{ name, source: raw }];
60
+ if (isStringArray(raw))
61
+ return raw.map(path => ({ name, source: path }));
62
+ if (isRecord(raw)) {
63
+ return Object.entries(raw).map(([commandName, meta]) => {
64
+ if (!isRecord(meta))
65
+ throw new Error(`plugin ${name}: command "${commandName}" must be an object`);
66
+ const entry = commandEntry(meta, name, commandName);
67
+ return { name: commandName, ...entry };
68
+ });
69
+ }
70
+ throw new Error(`plugin ${name}: "commands" must be a path, a list, or an object map`);
71
+ }
72
+ /** Validate one inline command metadata record; `source` and `content` are exclusive. */
73
+ function commandEntry(meta, name, commandName) {
74
+ const hasSource = typeof meta['source'] === 'string';
75
+ const hasContent = typeof meta['content'] === 'string';
76
+ if (hasSource === hasContent) {
77
+ throw new Error(`plugin ${name}: command "${commandName}" must provide exactly one of "source" (path) or "content" (inline)`);
78
+ }
79
+ const entry = {};
80
+ if (hasSource)
81
+ entry.source = meta['source'];
82
+ if (hasContent)
83
+ entry.content = meta['content'];
84
+ for (const key of ['description', 'argumentHint', 'model']) {
85
+ if (typeof meta[key] === 'string')
86
+ entry[key] = meta[key];
87
+ }
88
+ if (Array.isArray(meta['allowedTools'])) {
89
+ entry.allowedTools = meta['allowedTools'];
90
+ }
91
+ return entry;
92
+ }
93
+ function normalizeStringList(raw, name, field) {
94
+ if (raw === undefined)
95
+ return [];
96
+ if (typeof raw === 'string')
97
+ return [raw];
98
+ if (isStringArray(raw))
99
+ return [...raw];
100
+ throw new Error(`plugin ${name}: "${field}" must be a path or a list of paths`);
101
+ }
102
+ function normalizeMcpServers(raw, name) {
103
+ if (raw === undefined)
104
+ return { mcpServers: {} };
105
+ if (typeof raw === 'string')
106
+ return { mcpServers: {}, mcpServersPath: raw }; // an `.mcp.json` path
107
+ if (isRecord(raw))
108
+ return { mcpServers: raw };
109
+ throw new Error(`plugin ${name}: "mcpServers" must be a path or an object map of server configs`);
110
+ }
111
+ function isRecord(value) {
112
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
113
+ }
114
+ function isStringArray(value) {
115
+ return Array.isArray(value) && value.every(item => typeof item === 'string');
116
+ }
117
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAY,EAAE,MAAc;IAC9D,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,kCAAkC,CAAC,CAAA;IACrE,CAAC;IACD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAA;IACzD,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;IACjE,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;IACjE,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,mBAAmB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,IAAI,CAAC,CAAA;IACnF,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACjE,OAAO;QACL,IAAI;QACJ,GAAG,OAAO,GAAG,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QACxE,GAAG,OAAO,GAAG,CAAC,aAAa,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QACpF,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QAC/D,QAAQ;QACR,MAAM;QACN,MAAM;QACN,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QAC5D,UAAU;QACV,GAAG,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE;QACzD,QAAQ;KACT,CAAA;AACH,CAAC;AAYD,wDAAwD;AACxD,SAAS,QAAQ,CAAC,GAAY,EAAE,MAAc;IAC5C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,qCAAqC,CAAC,CAAA;IACxE,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,UAAU,GAAG,gDAAgD,CAAC,CAAA;IAChF,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAY,EAAE,IAAY;IACnD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IAChC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;IAC3D,IAAI,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;IACxE,IAAI,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAClB,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE;YACrD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,cAAc,WAAW,qBAAqB,CAAC,CAAA;YAClG,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAA;YACnD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,CAAA;QACxC,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,uDAAuD,CAAC,CAAA;AACxF,CAAC;AAED,yFAAyF;AACzF,SAAS,YAAY,CAAC,IAA6B,EAAE,IAAY,EAAE,WAAmB;IACpF,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAA;IACpD,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAA;IACtD,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,cAAc,WAAW,qEAAqE,CAAC,CAAA;IAC/H,CAAC;IACD,MAAM,KAAK,GAAwB,EAAE,CAAA;IACrC,IAAI,SAAS;QAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAW,CAAA;IACtD,IAAI,UAAU;QAAE,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAW,CAAA;IACzD,KAAK,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,cAAc,EAAE,OAAO,CAAU,EAAE,CAAC;QACpE,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,QAAQ;YAAE,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;IAC3D,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;QACxC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,cAAc,CAAa,CAAA;IACvD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAYD,SAAS,mBAAmB,CAAC,GAAY,EAAE,IAAY,EAAE,KAAa;IACpE,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAA;IAChC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,CAAC,GAAG,CAAC,CAAA;IACzC,IAAI,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,GAAG,GAAG,CAAC,CAAA;IACvC,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,MAAM,KAAK,qCAAqC,CAAC,CAAA;AACjF,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAY,EAAE,IAAY;IACrD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,CAAA;IAChD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,CAAA,CAAC,sBAAsB;IAClG,IAAI,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,GAA4C,EAAE,CAAA;IACtF,MAAM,IAAI,KAAK,CAAC,UAAU,IAAI,kEAAkE,CAAC,CAAA;AACnG,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAA;AAC9E,CAAC"}
package/lib/mcp.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Mount a Claude Code plugin's MCP servers.
3
+ *
4
+ * Collects server definitions from the manifest inline `mcpServers` record or
5
+ * an `.mcp.json` file, then registers each through the optional `mcp` guest
6
+ * seam. Tool naming (`mcp__<server>__<tool>`) is the seam's responsibility.
7
+ * When the seam is absent the component is reported skipped, never failed.
8
+ *
9
+ * @module
10
+ */
11
+ import type { CcPluginManifest } from './types.ts';
12
+ import { ComponentTally } from './seams.ts';
13
+ /** The MCP seam: registers a named server for tool discovery. */
14
+ export interface McpSeam {
15
+ /**
16
+ * Register one MCP server.
17
+ * @param name - unique server name.
18
+ * @param config - server transport configuration.
19
+ * @returns the exact disposer that unregisters the server.
20
+ */
21
+ registerServer(name: string, config: Record<string, unknown>): () => void;
22
+ }
23
+ /** Options for mounting one plugin's MCP servers. */
24
+ export interface MountMcpServersOptions {
25
+ /** The plugin root directory; an `.mcp.json` path resolves against it. */
26
+ readonly pluginRoot: string;
27
+ /** The parsed manifest; `mcpServers` and `mcpServersPath` drive the mount. */
28
+ readonly manifest: CcPluginManifest;
29
+ /** The mcp seam (probed; `undefined` to skip mcpServers). */
30
+ readonly mcp: McpSeam | undefined;
31
+ }
32
+ /**
33
+ * Collect and register a plugin's MCP servers through the optional seam.
34
+ * @param options - plugin root, manifest, and the mcp seam.
35
+ * @returns mounted disposers and per-component counts.
36
+ */
37
+ export declare function mountMcpServers(options: MountMcpServersOptions): {
38
+ disposers: (() => void)[];
39
+ tally: ComponentTally;
40
+ };
41
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C,iEAAiE;AACjE,MAAM,WAAW,OAAO;IACtB;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,CAAA;CAC1E;AAED,qDAAqD;AACrD,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAA;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAAA;CAClC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAAE,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAkBrH"}