@pixerate/editor 0.139.1 → 0.140.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 (120) hide show
  1. package/README.md +25 -4
  2. package/dist/agent/formatTools.d.cts +15 -0
  3. package/dist/agent/formatTools.d.ts +15 -0
  4. package/dist/agent/index.cjs +397 -46
  5. package/dist/agent/index.js +365 -14
  6. package/dist/agent/providers.d.cts +5 -3
  7. package/dist/agent/providers.d.ts +5 -3
  8. package/dist/animation-editor/effect-tools.d.cts +1 -1
  9. package/dist/animation-editor/effect-tools.d.ts +1 -1
  10. package/dist/animation-editor/index.cjs +250 -116
  11. package/dist/animation-editor/index.js +228 -94
  12. package/dist/animation-editor/instructions.d.cts +1 -1
  13. package/dist/animation-editor/instructions.d.ts +1 -1
  14. package/dist/animation-editor/parse.d.cts +3 -1
  15. package/dist/animation-editor/parse.d.ts +3 -1
  16. package/dist/animation-editor/tools.d.cts +8 -3
  17. package/dist/animation-editor/tools.d.ts +8 -3
  18. package/dist/bridge/cli.cjs +3 -3
  19. package/dist/bridge/cli.js +2 -2
  20. package/dist/bridge/index.cjs +7 -3
  21. package/dist/bridge/index.d.cts +7 -0
  22. package/dist/bridge/index.d.ts +7 -0
  23. package/dist/bridge/index.js +8 -4
  24. package/dist/bridge/skills.d.cts +9 -0
  25. package/dist/bridge/skills.d.ts +9 -0
  26. package/dist/canvas/effect-tools.d.cts +1 -1
  27. package/dist/canvas/effect-tools.d.ts +1 -1
  28. package/dist/canvas/index.cjs +22 -17
  29. package/dist/canvas/index.js +8 -3
  30. package/dist/chart/file-tools.d.cts +1 -1
  31. package/dist/chart/file-tools.d.ts +1 -1
  32. package/dist/chart/index.cjs +7 -1
  33. package/dist/chart/index.js +7 -1
  34. package/dist/chart/tools.d.cts +1 -1
  35. package/dist/chart/tools.d.ts +1 -1
  36. package/dist/{chunk-BKMU3V6K.js → chunk-5ED73EC6.js} +8 -1
  37. package/dist/{chunk-VRIR53SW.js → chunk-5G3KYRER.js} +2 -1
  38. package/dist/{chunk-KHMVB3SJ.js → chunk-7KWJEYIK.js} +4 -1
  39. package/dist/{chunk-R47NQICB.cjs → chunk-HDCH35UM.cjs} +47 -10
  40. package/dist/{chunk-6NCBFBXA.js → chunk-IQ6KKXOU.js} +1 -1
  41. package/dist/{chunk-PCNCT4ZY.js → chunk-KIBEEN5K.js} +40 -3
  42. package/dist/{chunk-CZRWP6IW.cjs → chunk-N2FGYZ4Q.cjs} +8 -1
  43. package/dist/{chunk-T6Y5FV7G.cjs → chunk-RPW7YL3D.cjs} +2 -1
  44. package/dist/{chunk-3V746PF4.cjs → chunk-RUZQMYXH.cjs} +4 -1
  45. package/dist/{chunk-GUPQ5BNA.cjs → chunk-RWML7TWG.cjs} +44 -44
  46. package/dist/{chunk-HGHXGCKG.cjs → chunk-SVFEVKLF.cjs} +1 -1
  47. package/dist/{chunk-PQMADRCI.js → chunk-UOJS6PUP.js} +1 -1
  48. package/dist/{chunk-BWT4IWLK.cjs → chunk-VKF3PWKR.cjs} +7 -7
  49. package/dist/chunk-WBKLYLZE.js +1156 -0
  50. package/dist/{chunk-TBSPPVTT.js → chunk-XCK7QK5D.js} +1 -1
  51. package/dist/chunk-ZAOOIJ6I.cjs +1156 -0
  52. package/dist/commands/agentTools.d.cts +1 -1
  53. package/dist/commands/agentTools.d.ts +1 -1
  54. package/dist/commands/index.cjs +3 -0
  55. package/dist/commands/index.js +3 -0
  56. package/dist/diagram/index.cjs +3 -0
  57. package/dist/diagram/index.js +3 -0
  58. package/dist/diagram/tools.d.cts +1 -1
  59. package/dist/diagram/tools.d.ts +1 -1
  60. package/dist/effects/index.cjs +32 -32
  61. package/dist/effects/index.js +1 -1
  62. package/dist/effects/tools.d.cts +5 -1
  63. package/dist/effects/tools.d.ts +5 -1
  64. package/dist/extensions/index.cjs +3 -3
  65. package/dist/extensions/index.js +2 -2
  66. package/dist/generation/index.cjs +7 -1
  67. package/dist/generation/index.js +7 -1
  68. package/dist/image-editor/index.cjs +21 -15
  69. package/dist/image-editor/index.js +8 -2
  70. package/dist/image-editor/provider-effect-tools.d.cts +1 -1
  71. package/dist/image-editor/provider-effect-tools.d.ts +1 -1
  72. package/dist/image-editor/tools.d.cts +1 -1
  73. package/dist/image-editor/tools.d.ts +1 -1
  74. package/dist/index.cjs +8 -8
  75. package/dist/index.js +2 -2
  76. package/dist/paint-editor/effect-tools.d.cts +1 -1
  77. package/dist/paint-editor/effect-tools.d.ts +1 -1
  78. package/dist/paint-editor/index.cjs +18 -8
  79. package/dist/paint-editor/index.js +13 -3
  80. package/dist/paint-editor/tools.d.cts +1 -1
  81. package/dist/paint-editor/tools.d.ts +1 -1
  82. package/dist/tools/bridge-client.d.cts +3 -3
  83. package/dist/tools/bridge-client.d.ts +3 -3
  84. package/dist/tools/capture.d.cts +5 -1
  85. package/dist/tools/capture.d.ts +5 -1
  86. package/dist/tools/definitions.d.cts +2 -2
  87. package/dist/tools/definitions.d.ts +2 -2
  88. package/dist/tools/index.cjs +31 -5
  89. package/dist/tools/index.d.cts +3 -0
  90. package/dist/tools/index.d.ts +3 -0
  91. package/dist/tools/index.js +31 -5
  92. package/dist/tools/mcp-server.d.cts +24 -6
  93. package/dist/tools/mcp-server.d.ts +24 -6
  94. package/dist/tools/registry.d.cts +81 -6
  95. package/dist/tools/registry.d.ts +81 -6
  96. package/dist/tools/search.d.cts +30 -0
  97. package/dist/tools/search.d.ts +30 -0
  98. package/dist/tools/skills.d.cts +26 -0
  99. package/dist/tools/skills.d.ts +26 -0
  100. package/dist/tools/view.d.cts +95 -0
  101. package/dist/tools/view.d.ts +95 -0
  102. package/dist/vector/effect-tools.d.cts +1 -1
  103. package/dist/vector/effect-tools.d.ts +1 -1
  104. package/dist/vector/index.cjs +13 -7
  105. package/dist/vector/index.js +8 -2
  106. package/dist/video-editor/effect-tools.d.cts +1 -1
  107. package/dist/video-editor/effect-tools.d.ts +1 -1
  108. package/dist/video-editor/index.cjs +275 -269
  109. package/dist/video-editor/index.js +8 -2
  110. package/dist/video-editor/tools.d.cts +1 -1
  111. package/dist/video-editor/tools.d.ts +1 -1
  112. package/package.json +4 -2
  113. package/skills/README.md +8 -3
  114. package/skills/pixerate-agent-tools/SKILL.md +33 -7
  115. package/skills/pixerate-animation-editor/SKILL.md +7 -0
  116. package/skills/pixerate-rich-text/SKILL.md +18 -4
  117. package/dist/chunk-5RRYDFU2.js +0 -466
  118. package/dist/chunk-SPVIXXIB.cjs +0 -466
  119. /package/dist/{chunk-SMJK5HBJ.js → chunk-MCTQXUXR.js} +0 -0
  120. /package/dist/{chunk-DDF6WQ62.cjs → chunk-R6DFK5OX.cjs} +0 -0
@@ -0,0 +1,1156 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
2
+
3
+ var _chunkWOFXZ3XUcjs = require('./chunk-WOFXZ3XU.cjs');
4
+
5
+ // src/tools/skills.ts
6
+ function parseSkillFrontmatter(markdown) {
7
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
8
+ if (!match) return { body: markdown };
9
+ const fields = {};
10
+ for (const line of match[1].split(/\r?\n/)) {
11
+ const kv = /^([A-Za-z_-]+):\s*(.*)$/.exec(line);
12
+ if (kv) fields[kv[1]] = kv[2].trim().replace(/^(['"])([\s\S]*)\1$/, "$2");
13
+ }
14
+ return { name: fields.name, description: fields.description, body: markdown.slice(match[0].length) };
15
+ }
16
+ var normalize = (path) => path.replace(/\\/g, "/").replace(/^\.?\//, "");
17
+ function listSkills(files) {
18
+ const skills = /* @__PURE__ */ new Map();
19
+ const paths = Object.keys(files).map(normalize);
20
+ for (const path of paths) {
21
+ const [dir, ...rest] = path.split("/");
22
+ if (rest.join("/") !== "SKILL.md") continue;
23
+ const { description } = parseSkillFrontmatter(_nullishCoalesce(_nullishCoalesce(files[path], () => ( files[`./${path}`])), () => ( "")));
24
+ skills.set(dir, { name: dir, description: _nullishCoalesce(description, () => ( "")), references: [] });
25
+ }
26
+ for (const path of paths) {
27
+ const [dir, ...rest] = path.split("/");
28
+ const file = rest.join("/");
29
+ if (file && file !== "SKILL.md" && skills.has(dir)) skills.get(dir).references.push(file);
30
+ }
31
+ for (const skill of skills.values()) skill.references.sort();
32
+ return Array.from(skills.values()).sort((a, b) => a.name.localeCompare(b.name));
33
+ }
34
+ function readSkillFile(files, skill, file = "SKILL.md") {
35
+ const path = `${skill}/${normalize(file)}`;
36
+ return _nullishCoalesce(files[path], () => ( files[`./${path}`]));
37
+ }
38
+
39
+ // src/tools/view.ts
40
+ var TOOL_DISCOVERY_TOOLS = ["list_toolsets", "search_tools", "enable_tools", "disable_tools", "get_skill"];
41
+ var READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
42
+ var LISTING = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
43
+ var SELECTION_SCHEMA = {
44
+ type: "object",
45
+ properties: {
46
+ tools: {
47
+ type: "array",
48
+ items: { type: "string", minLength: 1 },
49
+ uniqueItems: true,
50
+ description: 'Exposed tool names, as `search_tools` returns them (e.g. "chart__add_chart_trendline"). Default: none.'
51
+ },
52
+ toolsets: {
53
+ type: "array",
54
+ items: { type: "string", minLength: 1 },
55
+ uniqueItems: true,
56
+ description: 'Toolset names from `list_toolsets` (e.g. "chart"); covers every tool in each. Default: none.'
57
+ }
58
+ },
59
+ additionalProperties: false
60
+ };
61
+ var ToolRegistryView = (_class = class {
62
+ constructor(source, options = {}) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);
63
+ this.source = source;
64
+ this.allow = options.toolsets;
65
+ this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
66
+ this.discovery = options.discovery ? options.discovery === true ? {} : options.discovery : null;
67
+ for (const name of _nullishCoalesce(_optionalChain([this, 'access', _ => _.discovery, 'optionalAccess', _2 => _2.enabled]), () => ( []))) this.enabledSets.add(name);
68
+ this.meta = this.discovery ? this.createDiscoveryTools() : [];
69
+ }
70
+
71
+ __init() {this.capabilities = { tools: { listChanged: true } }}
72
+
73
+
74
+
75
+ __init2() {this.enabledSets = /* @__PURE__ */ new Set()}
76
+ __init3() {this.enabledTools = /* @__PURE__ */ new Set()}
77
+ __init4() {this.listeners = /* @__PURE__ */ new Set()}
78
+
79
+ __init5() {this.unsubscribe = null}
80
+ __init6() {this.lastSignature = ""}
81
+ /** Changes which toolsets the agent may use (`undefined`: all). Fires `listChanged` if the listing changes. */
82
+ setToolsets(toolsets) {
83
+ this.allow = toolsets;
84
+ this.changed();
85
+ }
86
+ /**
87
+ * Lists more tools in a discovery view, as the `enable_tools` tool does.
88
+ * Throws for names the view does not allow. Returns the tool names newly listed.
89
+ */
90
+ enableTools(selection) {
91
+ const cat = this.catalogue();
92
+ const { tools, toolsets } = this.checkSelection(cat, selection);
93
+ const before = new Set(this.listed(cat).map((t) => t.name));
94
+ for (const name of toolsets) this.enabledSets.add(name);
95
+ for (const name of tools) this.enabledTools.add(name);
96
+ this.changed();
97
+ return this.listed(cat).map((t) => t.name).filter((name) => !before.has(name));
98
+ }
99
+ /** Stops listing tools in a discovery view, as `disable_tools` does. Returns the tool names no longer listed. */
100
+ disableTools(selection) {
101
+ const cat = this.catalogue();
102
+ const { tools, toolsets } = this.checkSelection(cat, selection);
103
+ const before = this.listed(cat).map((t) => t.name);
104
+ for (const name of toolsets) {
105
+ this.enabledSets.delete(name);
106
+ for (const tool of _nullishCoalesce(_optionalChain([cat, 'access', _3 => _3.sets, 'access', _4 => _4.find, 'call', _5 => _5((s) => s.name === name), 'optionalAccess', _6 => _6.tools]), () => ( []))) this.enabledTools.delete(tool);
107
+ }
108
+ for (const name of tools) this.enabledTools.delete(name);
109
+ this.changed();
110
+ const after = new Set(this.listed(cat).map((t) => t.name));
111
+ return before.filter((name) => !after.has(name));
112
+ }
113
+ /** Allowed toolsets, each with only its allowed tools. */
114
+ toolsets() {
115
+ return this.catalogue().sets;
116
+ }
117
+ listTools() {
118
+ const cat = this.catalogue();
119
+ return { tools: [...this.meta.map(toDefinition), ...this.listed(cat)] };
120
+ }
121
+ hasTool(name) {
122
+ return this.meta.some((t) => t.name === name) || this.find(this.catalogue(), name).found !== void 0;
123
+ }
124
+ getTool(name) {
125
+ const meta = this.meta.find((t) => t.name === name);
126
+ if (meta) return toDefinition(meta);
127
+ return this.find(this.catalogue(), name).found;
128
+ }
129
+ async callTool(name, args = {}, options = {}) {
130
+ const meta = this.meta.find((t) => t.name === name);
131
+ if (meta) {
132
+ const details = _chunkWOFXZ3XUcjs.validateJsonSchema.call(void 0, _nullishCoalesce(args, () => ( {})), meta.inputSchema);
133
+ if (details.length > 0) {
134
+ return errorResult({ code: "invalid_arguments", message: `Invalid arguments for tool "${name}".`, tool: name, details });
135
+ }
136
+ try {
137
+ return toToolResult(await meta.handler(_nullishCoalesce(args, () => ( {})), { name, providerId: "discovery", signal: options.signal }));
138
+ } catch (error) {
139
+ return errorResult({ code: "tool_failed", message: error instanceof Error ? error.message : String(error), tool: name });
140
+ }
141
+ }
142
+ const cat = this.catalogue();
143
+ const match = this.find(cat, name);
144
+ if (match.found) return this.source.callTool(match.found.name, args, options);
145
+ return errorResult({ code: "unknown_tool", message: match.message, tool: name });
146
+ }
147
+ onListChanged(listener) {
148
+ if (this.listeners.size === 0) {
149
+ this.lastSignature = this.signature();
150
+ this.unsubscribe = this.source.onListChanged(() => this.changed());
151
+ }
152
+ this.listeners.add(listener);
153
+ return () => {
154
+ this.listeners.delete(listener);
155
+ if (this.listeners.size === 0) {
156
+ _optionalChain([this, 'access', _7 => _7.unsubscribe, 'optionalCall', _8 => _8()]);
157
+ this.unsubscribe = null;
158
+ }
159
+ };
160
+ }
161
+ /** Stops following the source and drops every listener. */
162
+ dispose() {
163
+ _optionalChain([this, 'access', _9 => _9.unsubscribe, 'optionalCall', _10 => _10()]);
164
+ this.unsubscribe = null;
165
+ this.listeners.clear();
166
+ }
167
+ changed() {
168
+ if (this.listeners.size === 0) return;
169
+ const signature = this.signature();
170
+ if (signature === this.lastSignature) return;
171
+ this.lastSignature = signature;
172
+ const { tools } = this.listTools();
173
+ for (const listener of Array.from(this.listeners)) listener(tools);
174
+ }
175
+ signature() {
176
+ return JSON.stringify(this.listTools().tools);
177
+ }
178
+ allowsSet(set) {
179
+ const allow = this.allow;
180
+ if (!allow) return true;
181
+ return typeof allow === "function" ? allow(set) : allow.includes(set.name);
182
+ }
183
+ catalogue() {
184
+ const defs = new Map(this.source.listTools().tools.map((t) => [t.name, t]));
185
+ const tools = [];
186
+ const sets = [];
187
+ const setOf = /* @__PURE__ */ new Map();
188
+ for (const set of this.source.toolsets()) {
189
+ if (!this.allowsSet(set)) continue;
190
+ const names = set.tools.filter((name) => {
191
+ const def = defs.get(name);
192
+ return def !== void 0 && (!this.readOnly || _optionalChain([def, 'access', _11 => _11.annotations, 'optionalAccess', _12 => _12.readOnlyHint]) === true);
193
+ });
194
+ if (names.length === 0) continue;
195
+ const allowed = { ...set, tools: names };
196
+ sets.push(allowed);
197
+ for (const name of names) setOf.set(name, allowed);
198
+ }
199
+ for (const def of defs.values()) if (setOf.has(def.name)) tools.push(def);
200
+ return { tools, sets, setOf };
201
+ }
202
+ listed(cat) {
203
+ if (!this.discovery) return cat.tools;
204
+ return cat.tools.filter((t) => this.enabledTools.has(t.name) || this.enabledSets.has(cat.setOf.get(t.name).name));
205
+ }
206
+ /** Resolves an exposed or unique bare name among the allowed tools, or explains why it isn't one. */
207
+ find(cat, name) {
208
+ const exact = cat.tools.find((t) => t.name === name);
209
+ if (exact) return { found: exact };
210
+ const bare = cat.tools.filter((t) => baseToolName(t.name) === name);
211
+ if (bare.length === 1) return { found: bare[0] };
212
+ if (bare.length > 1) return { message: unknownToolMessage(name, bare.map((t) => t.name)) };
213
+ const hidden = this.source.getTool(name);
214
+ if (hidden) {
215
+ const set = this.source.toolsets().find((s) => s.tools.includes(hidden.name));
216
+ const reason = set && !this.allowsSet(set) ? `its toolset "${set.name}" is not available to this agent` : "it changes content and this agent has read-only access";
217
+ return { message: `Tool "${name}" is not available: ${reason}.` };
218
+ }
219
+ const hint = this.discovery ? " Call search_tools to find the right tool." : "";
220
+ return { message: `${unknownToolMessage(name)}${hint}` };
221
+ }
222
+ /** Normalizes names to exposed tool and toolset names, or throws naming every unknown one. */
223
+ checkSelection(cat, selection) {
224
+ const tools = [];
225
+ const errors = [];
226
+ for (const name of _nullishCoalesce(selection.tools, () => ( []))) {
227
+ const match = this.find(cat, name);
228
+ if (match.found) tools.push(match.found.name);
229
+ else errors.push(match.message);
230
+ }
231
+ const toolsets = [];
232
+ for (const name of _nullishCoalesce(selection.toolsets, () => ( []))) {
233
+ if (cat.sets.some((s) => s.name === name)) toolsets.push(name);
234
+ else errors.push(`Unknown toolset "${name}".`);
235
+ }
236
+ if (errors.length > 0) {
237
+ throw new Error(`${errors.join(" ")} Nothing was changed. Call list_toolsets or search_tools for valid names.`);
238
+ }
239
+ if (tools.length === 0 && toolsets.length === 0) {
240
+ throw new Error("Pass at least one name in `tools` or `toolsets`.");
241
+ }
242
+ return { tools, toolsets };
243
+ }
244
+ createDiscoveryTools() {
245
+ const discovery = this.discovery;
246
+ const limit = Math.min(Math.max(1, _nullishCoalesce(discovery.searchLimit, () => ( 10))), 50);
247
+ const skills = discovery.skills ? listSkills(discovery.skills) : [];
248
+ const enabledState = (set) => {
249
+ if (this.enabledSets.has(set.name)) return "all";
250
+ return set.tools.some((t) => this.enabledTools.has(t)) ? "some" : "none";
251
+ };
252
+ const listedCount = () => this.listed(this.catalogue()).length;
253
+ const tools = [
254
+ {
255
+ name: "list_toolsets",
256
+ title: "List toolsets",
257
+ description: "Lists the groups of editor tools available here (one per open editor or feature): name, title, what it covers, its agent skill, how many tools it has and whether they are in your tool list (`enabled`: all, some or none). Start here, then call search_tools or enable_tools.",
258
+ annotations: READ_ONLY,
259
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
260
+ handler: () => {
261
+ const cat = this.catalogue();
262
+ return {
263
+ toolsets: cat.sets.map((set) => ({
264
+ name: set.name,
265
+ ...set.title ? { title: set.title } : {},
266
+ ...set.description ? { description: set.description } : {},
267
+ ...set.skill ? { skill: set.skill } : {},
268
+ tools: set.tools.length,
269
+ enabled: enabledState(set)
270
+ })),
271
+ ...skills.length > 0 ? { skills: skills.map((s) => s.name) } : {}
272
+ };
273
+ }
274
+ },
275
+ {
276
+ name: "search_tools",
277
+ title: "Search tools",
278
+ description: `Finds tools for a task across every toolset, best match first: name, toolset, title, a one-sentence summary, whether it only reads, and whether it is in your tool list. Describe the task in a few words ("add a trendline", "remove background noise from audio"). Call enable_tools with the names you need, then call them.`,
279
+ annotations: READ_ONLY,
280
+ inputSchema: {
281
+ type: "object",
282
+ properties: {
283
+ query: { type: "string", minLength: 1, description: "What you want to do, in plain words or likely tool-name words." },
284
+ toolset: { type: "string", minLength: 1, description: "Only search this toolset (a name from list_toolsets). Default: all toolsets." },
285
+ limit: { type: "integer", minimum: 1, maximum: 50, description: `Maximum number of results. Default: ${limit}.` }
286
+ },
287
+ required: ["query"],
288
+ additionalProperties: false
289
+ },
290
+ handler: (args) => {
291
+ const cat = this.catalogue();
292
+ if (args.toolset !== void 0 && !cat.sets.some((s) => s.name === args.toolset)) {
293
+ throw new Error(`Unknown toolset "${args.toolset}". Call list_toolsets for the names.`);
294
+ }
295
+ const listed = new Set(this.listed(cat).map((t) => t.name));
296
+ const results = searchTools(cat.tools, cat.sets, args.query, { toolset: args.toolset, limit: _nullishCoalesce(args.limit, () => ( limit)) }).map(
297
+ (r) => ({ ...r, enabled: listed.has(r.name) })
298
+ );
299
+ return {
300
+ results,
301
+ ...results.length === 0 ? { hint: "No tool matched. Try other words, or call list_toolsets." } : {},
302
+ ...results.some((r) => !r.enabled) ? { next: "Call enable_tools with the names you need; they are then added to your tool list." } : {}
303
+ };
304
+ }
305
+ },
306
+ {
307
+ name: "enable_tools",
308
+ title: "Enable tools",
309
+ description: "Adds tools to your tool list: single tools by name (from search_tools) and/or whole toolsets (from list_toolsets). Returns the names added and how many tools are listed now. Prefer single tools over large toolsets. Unknown names fail and change nothing.",
310
+ annotations: LISTING,
311
+ inputSchema: SELECTION_SCHEMA,
312
+ handler: (args) => {
313
+ const added = this.enableTools(args);
314
+ return { added, listed: listedCount() };
315
+ }
316
+ },
317
+ {
318
+ name: "disable_tools",
319
+ title: "Disable tools",
320
+ description: "Removes tools you no longer need from your tool list, by name and/or whole toolset, to keep it short. They can still be enabled again. Returns the names removed and how many tools are listed now.",
321
+ annotations: LISTING,
322
+ inputSchema: SELECTION_SCHEMA,
323
+ handler: (args) => {
324
+ const removed = this.disableTools(args);
325
+ return { removed, listed: listedCount() };
326
+ }
327
+ }
328
+ ];
329
+ if (skills.length > 0) {
330
+ tools.push({
331
+ name: "get_skill",
332
+ title: "Get skill",
333
+ description: "Reads an agent skill: the guide to a toolset's tools (when to use each, the working method, pitfalls). Read the skill a toolset names in list_toolsets before working with it. Returns markdown; pass `file` for one of its reference files.",
334
+ annotations: READ_ONLY,
335
+ inputSchema: {
336
+ type: "object",
337
+ properties: {
338
+ name: { type: "string", enum: skills.map((s) => s.name), description: "Skill name, as list_toolsets reports it." },
339
+ file: { type: "string", minLength: 1, description: 'A reference file of the skill, e.g. "references/commands.md". Default: the skill itself (SKILL.md).' }
340
+ },
341
+ required: ["name"],
342
+ additionalProperties: false
343
+ },
344
+ handler: (args) => {
345
+ const skill = skills.find((s) => s.name === args.name);
346
+ const file = _nullishCoalesce(args.file, () => ( "SKILL.md"));
347
+ const text = readSkillFile(discovery.skills, skill.name, file);
348
+ if (text === void 0) {
349
+ const files = skill.references.length ? ` Its files: ${skill.references.join(", ")}.` : " It has no reference files.";
350
+ throw new Error(`Skill "${skill.name}" has no file "${file}".${files}`);
351
+ }
352
+ const sets = this.catalogue().sets.filter((s) => s.skill === skill.name);
353
+ const prefixed = sets.filter((s) => s.tools.some((t) => t.startsWith(`${s.name}__`)));
354
+ const notes = [];
355
+ if (prefixed.length > 0) {
356
+ notes.push(
357
+ `> Tool names in this skill are bare. Here they are prefixed by toolset: ${prefixed.map((s) => `\`${s.name}__\``).join(", ")} (e.g. \`${prefixed[0].tools[0]}\`). A bare name also works when only one toolset has it.`
358
+ );
359
+ }
360
+ if (file === "SKILL.md" && skill.references.length > 0) {
361
+ notes.push(`> Reference files (call get_skill with \`file\`): ${skill.references.join(", ")}.`);
362
+ }
363
+ return {
364
+ content: [{ type: "text", text: notes.length ? `${notes.join("\n")}
365
+
366
+ ${text}` : text }],
367
+ structuredContent: { name: skill.name, file, toolsets: sets.map((s) => s.name), references: skill.references }
368
+ };
369
+ }
370
+ });
371
+ }
372
+ return tools;
373
+ }
374
+ }, _class);
375
+ function toDefinition(tool) {
376
+ const { handler: _handler, ...rest } = tool;
377
+ return rest;
378
+ }
379
+
380
+ // src/tools/registry.ts
381
+ var ToolRegistryError = class extends Error {
382
+
383
+ constructor(code, message) {
384
+ super(message);
385
+ this.name = "ToolRegistryError";
386
+ this.code = code;
387
+ }
388
+ };
389
+ var TOOL_NAMESPACE_SEPARATOR = "__";
390
+ var TOOL_NAME_REGEX = /^[A-Za-z0-9_.-]{1,128}$/;
391
+ var sanitizeNamespace = (id) => id.replace(/[^A-Za-z0-9_-]/g, "_");
392
+ function toolsetName(provider) {
393
+ return sanitizeNamespace(provider.namespace || provider.id);
394
+ }
395
+ function baseToolName(name) {
396
+ const index = name.lastIndexOf(TOOL_NAMESPACE_SEPARATOR);
397
+ return index === -1 ? name : name.slice(index + TOOL_NAMESPACE_SEPARATOR.length);
398
+ }
399
+ function lookup(tools, name) {
400
+ const exact = tools.find((t) => t.name === name);
401
+ if (exact) return { found: exact };
402
+ const candidates = tools.filter((t) => baseToolName(t.name) === name);
403
+ if (candidates.length === 1) return { found: candidates[0] };
404
+ return { candidates: candidates.map((t) => t.name) };
405
+ }
406
+ function unknownToolMessage(name, candidates = []) {
407
+ return candidates.length > 1 ? `Tool "${name}" is ambiguous: ${candidates.map((c) => `"${c}"`).join(", ")} all have that name. Call one of them by its full name.` : `Unknown tool "${name}". List the tools again; the editor that had it may have closed or become read-only.`;
408
+ }
409
+ function readTools(provider) {
410
+ return typeof provider.tools === "function" ? provider.tools() : provider.tools;
411
+ }
412
+ function toDefinition2(name, tool) {
413
+ const { handler: _handler, name: _name, ...rest } = tool;
414
+ return { name, ...rest };
415
+ }
416
+ function errorResult(error) {
417
+ const details = _optionalChain([error, 'access', _13 => _13.details, 'optionalAccess', _14 => _14.length]) ? "\n" + error.details.map((d) => `- ${d.path}: ${d.message}`).join("\n") : "";
418
+ return {
419
+ isError: true,
420
+ content: [{ type: "text", text: `${error.message}${details}` }],
421
+ structuredContent: { error }
422
+ };
423
+ }
424
+ function toToolResult(value) {
425
+ if (value && typeof value === "object" && Array.isArray(value.content)) {
426
+ return value;
427
+ }
428
+ if (value === void 0) return { content: [] };
429
+ if (typeof value === "string") return { content: [{ type: "text", text: value }] };
430
+ const text = JSON.stringify(value);
431
+ const isPlainObject = typeof value === "object" && value !== null && !Array.isArray(value);
432
+ return {
433
+ content: [{ type: "text", text: _nullishCoalesce(text, () => ( String(value))) }],
434
+ ...isPlainObject ? { structuredContent: value } : {}
435
+ };
436
+ }
437
+ var ToolRegistry = (_class2 = class {
438
+ /** MCP server capabilities this registry supports. */
439
+ __init7() {this.capabilities = { tools: { listChanged: true } }}
440
+ __init8() {this.entries = []}
441
+ __init9() {this.listeners = /* @__PURE__ */ new Set()}
442
+
443
+ __init10() {this.timer = null}
444
+
445
+ constructor(options = {}) {;_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);
446
+ this.options = {
447
+ namespaces: _nullishCoalesce(options.namespaces, () => ( "always")),
448
+ onCollision: _nullishCoalesce(options.onCollision, () => ( "namespace")),
449
+ listChangedDelayMs: _nullishCoalesce(options.listChangedDelayMs, () => ( 0)),
450
+ validateArguments: _nullishCoalesce(options.validateArguments, () => ( true))
451
+ };
452
+ this.lastSignature = this.signature();
453
+ }
454
+ /**
455
+ * Registers a provider and returns a function that unregisters it.
456
+ * Throws a {@link ToolRegistryError} for a duplicate provider id, an invalid
457
+ * tool name, or (with `onCollision: 'error'`) a tool name collision.
458
+ */
459
+ register(provider) {
460
+ if (this.entries.some((e) => e.provider.id === provider.id)) {
461
+ throw new ToolRegistryError("duplicate_provider", `Tool provider "${provider.id}" is already registered.`);
462
+ }
463
+ for (const tool of readTools(provider)) {
464
+ if (!TOOL_NAME_REGEX.test(tool.name)) {
465
+ throw new ToolRegistryError("invalid_tool", `Invalid tool name "${tool.name}" from provider "${provider.id}".`);
466
+ }
467
+ }
468
+ const entry = { provider };
469
+ this.entries.push(entry);
470
+ try {
471
+ const names = this.resolve(this.options.onCollision === "error").map((t) => t.name);
472
+ const duplicate = names.find((name, i) => names.indexOf(name) !== i);
473
+ if (duplicate) {
474
+ throw new ToolRegistryError("name_collision", `Tool name "${duplicate}" is exposed more than once.`);
475
+ }
476
+ } catch (error) {
477
+ this.entries.pop();
478
+ throw error;
479
+ }
480
+ if (provider.subscribe) {
481
+ entry.unsubscribe = provider.subscribe(() => this.notifyListChanged());
482
+ }
483
+ this.notifyListChanged();
484
+ let registered = true;
485
+ return () => {
486
+ if (!registered) return;
487
+ registered = false;
488
+ this.unregister(provider);
489
+ };
490
+ }
491
+ /** Removes a provider by id or instance. Returns whether it was registered. */
492
+ unregister(providerOrId) {
493
+ const index = this.entries.findIndex(
494
+ (e) => typeof providerOrId === "string" ? e.provider.id === providerOrId : e.provider === providerOrId
495
+ );
496
+ if (index === -1) return false;
497
+ const [entry] = this.entries.splice(index, 1);
498
+ _optionalChain([entry, 'access', _15 => _15.unsubscribe, 'optionalCall', _16 => _16()]);
499
+ this.notifyListChanged();
500
+ return true;
501
+ }
502
+ /** Registered provider ids in registration order. */
503
+ providerIds() {
504
+ return this.entries.map((e) => e.provider.id);
505
+ }
506
+ /**
507
+ * Resolves exposed names. Tools are ordered by provider registration, then
508
+ * by their order within the provider.
509
+ */
510
+ resolve(throwOnCollision = false) {
511
+ const all = [];
512
+ const counts = /* @__PURE__ */ new Map();
513
+ const fixed = (provider) => provider.namespace ? provider.namespace : provider.namespace === void 0 && this.options.namespaces === "always" ? provider.id : void 0;
514
+ for (const { provider } of this.entries) {
515
+ const prefixed = fixed(provider) !== void 0;
516
+ for (const tool of readTools(provider)) {
517
+ all.push({ name: tool.name, tool, provider });
518
+ if (!prefixed) counts.set(tool.name, (_nullishCoalesce(counts.get(tool.name), () => ( 0))) + 1);
519
+ }
520
+ }
521
+ for (const item of all) {
522
+ const explicit = fixed(item.provider);
523
+ const prefix = _nullishCoalesce(explicit, () => ( ((_nullishCoalesce(counts.get(item.name), () => ( 0))) > 1 ? item.provider.id : void 0)));
524
+ if (prefix !== void 0) {
525
+ if (throwOnCollision && explicit === void 0) {
526
+ throw new ToolRegistryError(
527
+ "name_collision",
528
+ `Tool "${item.name}" from provider "${item.provider.id}" collides with another provider's tool.`
529
+ );
530
+ }
531
+ item.name = `${sanitizeNamespace(prefix)}${TOOL_NAMESPACE_SEPARATOR}${item.name}`;
532
+ }
533
+ }
534
+ return all;
535
+ }
536
+ /** MCP `tools/list` result for the current providers. */
537
+ listTools() {
538
+ return { tools: this.resolve().map(({ name, tool }) => toDefinition2(name, tool)) };
539
+ }
540
+ /** Whether a tool with this exposed name (or unique bare name) exists. */
541
+ hasTool(name) {
542
+ return lookup(this.resolve(), name).found !== void 0;
543
+ }
544
+ /** Looks up a tool definition by exposed name (or unique bare name). */
545
+ getTool(name) {
546
+ const { found } = lookup(this.resolve(), name);
547
+ return found ? toDefinition2(found.name, found.tool) : void 0;
548
+ }
549
+ /** The registered providers grouped by namespace, in listing order. */
550
+ toolsets() {
551
+ const sets = /* @__PURE__ */ new Map();
552
+ for (const { name, provider } of this.resolve()) {
553
+ const key = toolsetName(provider);
554
+ let set = sets.get(key);
555
+ if (!set) {
556
+ set = { name: key, providers: [], tools: [] };
557
+ sets.set(key, set);
558
+ }
559
+ if (!set.providers.includes(provider.id)) set.providers.push(provider.id);
560
+ set.title ??= provider.title;
561
+ set.description ??= provider.description;
562
+ set.skill ??= provider.skill;
563
+ set.tools.push(name);
564
+ }
565
+ return Array.from(sets.values(), (set) => ({
566
+ name: set.name,
567
+ ...set.title ? { title: set.title } : {},
568
+ ...set.description ? { description: set.description } : {},
569
+ ...set.skill ? { skill: set.skill } : {},
570
+ providers: set.providers,
571
+ tools: set.tools
572
+ }));
573
+ }
574
+ /**
575
+ * A filtered, optionally self-discovering view of this registry: serve it
576
+ * with `createMcpServer` or `connectToolBridge` instead of the registry to
577
+ * show an agent only some toolsets. See {@link ToolRegistryView}.
578
+ */
579
+ view(options = {}) {
580
+ return new ToolRegistryView(this, options);
581
+ }
582
+ /**
583
+ * Calls a tool by exposed name and returns an MCP `tools/call` result.
584
+ * Never rejects: unknown tools, invalid arguments and handler errors come
585
+ * back as `isError: true` results with a {@link ToolCallError} in
586
+ * `structuredContent.error` (MCP transports may map `unknown_tool` to a
587
+ * JSON-RPC `-32602` error instead).
588
+ */
589
+ async callTool(name, args = {}, options = {}) {
590
+ const match = lookup(this.resolve(), name);
591
+ const found = match.found;
592
+ if (!found) {
593
+ return errorResult({ code: "unknown_tool", message: unknownToolMessage(name, match.candidates), tool: name });
594
+ }
595
+ name = found.name;
596
+ const input = _nullishCoalesce(args, () => ( {}));
597
+ if (this.options.validateArguments) {
598
+ const details = _chunkWOFXZ3XUcjs.validateJsonSchema.call(void 0, input, found.tool.inputSchema);
599
+ if (details.length > 0) {
600
+ return errorResult({
601
+ code: "invalid_arguments",
602
+ message: `Invalid arguments for tool "${name}".`,
603
+ tool: name,
604
+ details
605
+ });
606
+ }
607
+ }
608
+ try {
609
+ const value = await found.tool.handler(input, {
610
+ name,
611
+ providerId: found.provider.id,
612
+ signal: options.signal
613
+ });
614
+ return toToolResult(value);
615
+ } catch (error) {
616
+ return errorResult({
617
+ code: "tool_failed",
618
+ message: error instanceof Error ? error.message : String(error),
619
+ tool: name
620
+ });
621
+ }
622
+ }
623
+ /**
624
+ * Subscribes to tool-list changes. Notifications are coalesced: many
625
+ * registrations or provider signals within the delay window produce one
626
+ * call, and none if the exposed list ends up unchanged (e.g. a provider
627
+ * unmounted and remounted). Returns an unsubscribe function.
628
+ */
629
+ onListChanged(listener) {
630
+ this.listeners.add(listener);
631
+ return () => {
632
+ this.listeners.delete(listener);
633
+ };
634
+ }
635
+ /** Schedules a coalesced `listChanged` check; providers may call it directly. */
636
+ notifyListChanged() {
637
+ if (this.timer !== null) return;
638
+ this.timer = setTimeout(() => this.flush(), this.options.listChangedDelayMs);
639
+ }
640
+ /** Runs a pending `listChanged` check immediately. */
641
+ flush() {
642
+ if (this.timer !== null) {
643
+ clearTimeout(this.timer);
644
+ this.timer = null;
645
+ }
646
+ const signature = this.signature();
647
+ if (signature === this.lastSignature) return;
648
+ this.lastSignature = signature;
649
+ const { tools } = this.listTools();
650
+ for (const listener of Array.from(this.listeners)) listener(tools);
651
+ }
652
+ signature() {
653
+ try {
654
+ return JSON.stringify(this.listTools().tools);
655
+ } catch (e2) {
656
+ return String(Math.random());
657
+ }
658
+ }
659
+ /** Unregisters every provider and listener and cancels pending notifications. */
660
+ dispose() {
661
+ if (this.timer !== null) clearTimeout(this.timer);
662
+ this.timer = null;
663
+ for (const entry of this.entries.splice(0)) _optionalChain([entry, 'access', _17 => _17.unsubscribe, 'optionalCall', _18 => _18()]);
664
+ this.listeners.clear();
665
+ }
666
+ }, _class2);
667
+ function createToolRegistry(options) {
668
+ return new ToolRegistry(options);
669
+ }
670
+
671
+ // src/tools/search.ts
672
+ var STOP_WORDS = new Set(
673
+ "a an and are as at be by can do for from has have how i in into is it its me my of on or our so than that the their them then there these this to up us use using want we what when which will with you your please make let".split(
674
+ " "
675
+ )
676
+ );
677
+ function stem(word) {
678
+ if (word.length > 5 && word.endsWith("ing")) return word.slice(0, -3);
679
+ if (word.length > 4 && word.endsWith("ies")) return `${word.slice(0, -3)}y`;
680
+ if (word.length > 4 && word.endsWith("ed")) return word.slice(0, -2);
681
+ if (word.length > 4 && word.endsWith("es") && /(ch|sh|x|ss)es$/.test(word)) return word.slice(0, -2);
682
+ if (word.length > 2 && word.endsWith("s") && !/(ss|us|is|as)$/.test(word)) return word.slice(0, -1);
683
+ return word;
684
+ }
685
+ var SPELLING = { colour: "color", grey: "gray", centre: "center", organise: "organize", normalise: "normalize" };
686
+ var SYNONYMS = {
687
+ link: ["connect", "edge"],
688
+ join: ["connect", "merge"],
689
+ arrow: ["edge", "connect"],
690
+ step: ["node"],
691
+ box: ["node", "shape"],
692
+ flowchart: ["diagram"],
693
+ graph: ["chart", "diagram"],
694
+ plot: ["chart"],
695
+ photo: ["image"],
696
+ picture: ["image"],
697
+ footage: ["video", "clip"],
698
+ timeline: ["track", "clip", "video"],
699
+ recording: ["audio", "video"],
700
+ sound: ["audio"],
701
+ quiet: ["silence"],
702
+ silent: ["silence"],
703
+ delete: ["remove"],
704
+ erase: ["remove", "clear"],
705
+ screenshot: ["capture", "view"],
706
+ snapshot: ["capture"],
707
+ tidy: ["layout", "arrange"],
708
+ arrange: ["layout"],
709
+ organize: ["layout", "arrange"],
710
+ number: ["value", "data"],
711
+ figure: ["value", "data"],
712
+ cost: ["estimate", "credit"],
713
+ price: ["estimate", "credit"],
714
+ version: ["vary", "variation"],
715
+ variation: ["vary"],
716
+ illustration: ["vector", "art"],
717
+ svg: ["vector"],
718
+ space: ["gap"],
719
+ empty: ["gap"],
720
+ blemish: ["heal", "spot"],
721
+ tonal: ["histogram"],
722
+ tone: ["histogram", "level"],
723
+ um: ["filler"],
724
+ uh: ["filler"],
725
+ dissolve: ["transition"],
726
+ crossfade: ["transition"],
727
+ wipe: ["transition"],
728
+ mark: ["marker"],
729
+ timelapse: ["time", "lapse"],
730
+ brightness: ["adjustment", "exposure"],
731
+ brighter: ["adjustment", "exposure"],
732
+ contrast: ["adjustment"],
733
+ shadow: ["effect"],
734
+ glow: ["effect"],
735
+ grain: ["effect", "paper"],
736
+ look: ["grade", "preset"],
737
+ cinematic: ["grade", "lut"],
738
+ save: ["export"],
739
+ find: ["search"],
740
+ search: ["find"],
741
+ change: ["update", "set", "edit"],
742
+ menu: ["command"],
743
+ action: ["command"],
744
+ shortcut: ["command"]
745
+ };
746
+ function searchTokens(text) {
747
+ return text.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter((w) => w.length > 1 && !STOP_WORDS.has(w)).map((w) => {
748
+ const stemmed = stem(w);
749
+ return _nullishCoalesce(SPELLING[stemmed], () => ( stemmed));
750
+ });
751
+ }
752
+ function summarize(description, max = 200) {
753
+ if (!description) return "";
754
+ const match = /^[\s\S]*?[.!?](?=\s|$)/.exec(description.trim());
755
+ const sentence = (match ? match[0] : description.trim()).replace(/\s+/g, " ");
756
+ return sentence.length > max ? `${sentence.slice(0, max - 1)}\u2026` : sentence;
757
+ }
758
+ var COMMON_VERBS = /* @__PURE__ */ new Set(["add", "set", "get", "remove", "list", "update", "change", "make", "create", "delete", "read", "show", "put", "use", "edit"]);
759
+ var WEIGHTS = { name: 4, title: 2.5, toolset: 1.5, description: 1, params: 0.5 };
760
+ function paramText(schema, depth = 0) {
761
+ if (!schema || typeof schema !== "object" || depth > 2) return "";
762
+ const s = schema;
763
+ const parts = [];
764
+ if (typeof s.description === "string" && depth > 0) parts.push(s.description);
765
+ if (Array.isArray(s.enum)) parts.push(s.enum.filter((v) => typeof v === "string").join(" "));
766
+ for (const [key, value] of Object.entries(_nullishCoalesce(s.properties, () => ( {})))) parts.push(key, paramText(value, depth + 1));
767
+ if (s.items) parts.push(paramText(s.items, depth + 1));
768
+ if (Array.isArray(s.anyOf)) for (const branch of s.anyOf) parts.push(paramText(branch, depth + 1));
769
+ return parts.join(" ");
770
+ }
771
+ function document(tool, toolset) {
772
+ const fields = {
773
+ name: new Set(searchTokens(baseToolName(tool.name))),
774
+ title: new Set(searchTokens(_nullishCoalesce(_nullishCoalesce(tool.title, () => ( _optionalChain([tool, 'access', _19 => _19.annotations, 'optionalAccess', _20 => _20.title]))), () => ( "")))),
775
+ toolset: new Set(searchTokens(`${toolset.name} ${_nullishCoalesce(toolset.title, () => ( ""))} ${_nullishCoalesce(toolset.description, () => ( ""))}`)),
776
+ description: new Set(searchTokens(_nullishCoalesce(tool.description, () => ( "")))),
777
+ params: new Set(searchTokens(paramText(tool.inputSchema)))
778
+ };
779
+ const all = /* @__PURE__ */ new Set();
780
+ for (const set of Object.values(fields)) for (const t of set) all.add(t);
781
+ return { tool, toolset, fields, all };
782
+ }
783
+ function matchValue(tokens, word, alternatives, idf) {
784
+ const rarity = _nullishCoalesce(idf.get(word), () => ( 1));
785
+ if (tokens.has(word)) return rarity;
786
+ let value = 0;
787
+ if (word.length >= 4) {
788
+ for (const t of tokens) {
789
+ if (t.startsWith(word) || t.length >= 4 && word.startsWith(t)) {
790
+ value = rarity / 2;
791
+ break;
792
+ }
793
+ }
794
+ }
795
+ for (const alt of alternatives) {
796
+ if (tokens.has(alt)) value = Math.max(value, Math.min(rarity, _nullishCoalesce(idf.get(alt), () => ( 1))) / 2);
797
+ }
798
+ return value;
799
+ }
800
+ function searchTools(tools, toolsets, query, options = {}) {
801
+ const setOf = /* @__PURE__ */ new Map();
802
+ for (const set of toolsets) for (const name of set.tools) setOf.set(name, set);
803
+ const docs = tools.flatMap((tool) => {
804
+ const set = _nullishCoalesce(setOf.get(tool.name), () => ( { name: "", providers: [], tools: [tool.name] }));
805
+ return !options.toolset || set.name === options.toolset ? [document(tool, set)] : [];
806
+ });
807
+ const words = Array.from(new Set(searchTokens(query)));
808
+ if (words.length === 0 || docs.length === 0) return [];
809
+ const alternatives = new Map(words.map((w) => [w, (_nullishCoalesce(SYNONYMS[w], () => ( []))).map(stem)]));
810
+ const all = options.toolset ? tools.map((t) => document(t, _nullishCoalesce(setOf.get(t.name), () => ( { name: "", providers: [], tools: [] })))) : docs;
811
+ const idf = /* @__PURE__ */ new Map();
812
+ for (const word of [...words, ...[...alternatives.values()].flat()]) {
813
+ if (idf.has(word)) continue;
814
+ const df = all.filter((d) => d.all.has(word)).length;
815
+ idf.set(word, Math.log(1 + all.length / (1 + df)));
816
+ }
817
+ const phrase = query.trim().toLowerCase().replace(/\s+/g, "_");
818
+ const results = [];
819
+ for (const doc of docs) {
820
+ let score = 0;
821
+ let matched = 0;
822
+ for (const word of words) {
823
+ let best = 0;
824
+ for (const field of Object.keys(WEIGHTS)) {
825
+ const weight = COMMON_VERBS.has(word) ? Math.min(WEIGHTS[field], WEIGHTS.description) : WEIGHTS[field];
826
+ const value = matchValue(doc.fields[field], word, _nullishCoalesce(alternatives.get(word), () => ( [])), idf) * weight;
827
+ best = Math.max(best, value);
828
+ score += value * 0.1;
829
+ }
830
+ if (best > 0) matched++;
831
+ score += best;
832
+ }
833
+ if (matched === 0) continue;
834
+ score *= 0.5 + 0.5 * matched / words.length;
835
+ const base = baseToolName(doc.tool.name);
836
+ if (base === phrase || doc.tool.name === phrase) score *= 3;
837
+ results.push({
838
+ name: doc.tool.name,
839
+ toolset: doc.toolset.name,
840
+ ...doc.tool.title ? { title: doc.tool.title } : {},
841
+ summary: summarize(doc.tool.description),
842
+ readOnly: _optionalChain([doc, 'access', _21 => _21.tool, 'access', _22 => _22.annotations, 'optionalAccess', _23 => _23.readOnlyHint]) === true,
843
+ score: Math.round(score * 100) / 100
844
+ });
845
+ }
846
+ results.sort((a, b) => b.score - a.score);
847
+ return results.slice(0, _nullishCoalesce(options.limit, () => ( 10)));
848
+ }
849
+
850
+ // src/tools/mcp-server.ts
851
+ var MCP_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
852
+ var JSON_RPC_ERRORS = {
853
+ parseError: -32700,
854
+ invalidRequest: -32600,
855
+ methodNotFound: -32601,
856
+ invalidParams: -32602,
857
+ internalError: -32603
858
+ };
859
+ var errorResponse = (id, code, message, data) => ({
860
+ jsonrpc: "2.0",
861
+ id,
862
+ error: { code, message, ...data !== void 0 ? { data } : {} }
863
+ });
864
+ function toolsetInstructions(source) {
865
+ const sets = source.toolsets();
866
+ const discovery = source.hasTool("list_toolsets") && source.hasTool("search_tools");
867
+ const lines = [
868
+ "Tools are grouped into toolsets, one per open editor or feature, and named `<toolset>__<tool>`." + (discovery ? " Only some are listed at first: call list_toolsets to see them all, search_tools to find a tool for a task, and enable_tools to add what you need." : "")
869
+ ];
870
+ if (sets.length > 0) {
871
+ lines.push("", "Toolsets:");
872
+ for (const set of sets) {
873
+ const about = [set.title, summarize(set.description)].filter(Boolean).join(": ");
874
+ const skill = set.skill ? ` (skill: ${set.skill})` : "";
875
+ lines.push(`- ${set.name}${about ? ` - ${about}` : ""}${skill}, ${set.tools.length} tools`);
876
+ }
877
+ }
878
+ if (sets.some((s) => s.skill)) {
879
+ lines.push(
880
+ "",
881
+ "Read the skill a toolset names before using it" + (source.hasTool("get_skill") ? " (get_skill)." : " (an MCP resource or prompt when the server offers skills).")
882
+ );
883
+ }
884
+ return lines.join("\n");
885
+ }
886
+ var skillUri = (path) => `skill://${path}`;
887
+ function createMcpServer(registry, options) {
888
+ const skillFiles = _nullishCoalesce(options.skills, () => ( null));
889
+ const skills = skillFiles ? listSkills(skillFiles) : [];
890
+ const capabilities = {
891
+ ...registry.capabilities,
892
+ ...skills.length > 0 ? { resources: {}, prompts: {} } : {}
893
+ };
894
+ const readSkillPath = (path) => _nullishCoalesce(_optionalChain([skillFiles, 'optionalAccess', _24 => _24[path]]), () => ( _optionalChain([skillFiles, 'optionalAccess', _25 => _25[`./${path}`]])));
895
+ let initialized = false;
896
+ let unsubscribe = null;
897
+ const running = /* @__PURE__ */ new Map();
898
+ const startNotifications = () => {
899
+ if (unsubscribe || !options.send) return;
900
+ const send = options.send;
901
+ unsubscribe = registry.onListChanged(() => send({ jsonrpc: "2.0", method: "notifications/tools/list_changed" }));
902
+ };
903
+ async function dispatch(request) {
904
+ const id = _nullishCoalesce(request.id, () => ( null));
905
+ const isNotification = request.id === void 0 || request.id === null;
906
+ const params = _nullishCoalesce(request.params, () => ( {}));
907
+ switch (request.method) {
908
+ case "initialize": {
909
+ const instructions = typeof options.instructions === "function" ? options.instructions(registry) : options.instructions;
910
+ const requested = String(_nullishCoalesce(params.protocolVersion, () => ( "")));
911
+ const protocolVersion = MCP_PROTOCOL_VERSIONS.includes(requested) ? requested : MCP_PROTOCOL_VERSIONS[0];
912
+ return {
913
+ jsonrpc: "2.0",
914
+ id,
915
+ result: {
916
+ protocolVersion,
917
+ capabilities,
918
+ serverInfo: { name: options.name, version: options.version, ...options.title ? { title: options.title } : {} },
919
+ ...instructions ? { instructions } : {}
920
+ }
921
+ };
922
+ }
923
+ case "notifications/initialized":
924
+ initialized = true;
925
+ startNotifications();
926
+ return null;
927
+ case "notifications/cancelled": {
928
+ const requestId = params.requestId;
929
+ if (requestId !== void 0) _optionalChain([running, 'access', _26 => _26.get, 'call', _27 => _27(requestId), 'optionalAccess', _28 => _28.abort, 'call', _29 => _29(params.reason)]);
930
+ return null;
931
+ }
932
+ case "ping":
933
+ return { jsonrpc: "2.0", id, result: {} };
934
+ case "tools/list":
935
+ return { jsonrpc: "2.0", id, result: registry.listTools() };
936
+ case "tools/call": {
937
+ const name = params.name;
938
+ if (typeof name !== "string") {
939
+ return errorResponse(id, JSON_RPC_ERRORS.invalidParams, 'tools/call needs a string "name".');
940
+ }
941
+ if (!registry.hasTool(name)) {
942
+ return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown tool: ${name}`);
943
+ }
944
+ const args = params.arguments;
945
+ if (args !== void 0 && (typeof args !== "object" || args === null || Array.isArray(args))) {
946
+ return errorResponse(id, JSON_RPC_ERRORS.invalidParams, '"arguments" must be an object.');
947
+ }
948
+ const controller = new AbortController();
949
+ if (!isNotification) running.set(id, controller);
950
+ try {
951
+ const result = await registry.callTool(name, _nullishCoalesce(args, () => ( {})), { signal: controller.signal });
952
+ if (controller.signal.aborted) return null;
953
+ return { jsonrpc: "2.0", id, result };
954
+ } finally {
955
+ if (!isNotification) running.delete(id);
956
+ }
957
+ }
958
+ case "resources/list":
959
+ case "prompts/list":
960
+ case "resources/read":
961
+ case "prompts/get": {
962
+ if (skills.length === 0) break;
963
+ if (request.method === "resources/list") {
964
+ const resources = skills.flatMap(
965
+ (skill2) => ["SKILL.md", ...skill2.references].map((file) => ({
966
+ uri: skillUri(`${skill2.name}/${file}`),
967
+ name: file === "SKILL.md" ? skill2.name : `${skill2.name}/${file}`,
968
+ ...file === "SKILL.md" ? { description: skill2.description } : {},
969
+ mimeType: "text/markdown"
970
+ }))
971
+ );
972
+ return { jsonrpc: "2.0", id, result: { resources } };
973
+ }
974
+ if (request.method === "resources/read") {
975
+ const uri = String(_nullishCoalesce(params.uri, () => ( "")));
976
+ const text2 = uri.startsWith("skill://") ? readSkillPath(uri.slice("skill://".length)) : void 0;
977
+ if (text2 === void 0) return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown resource: ${uri}`, { uri });
978
+ return { jsonrpc: "2.0", id, result: { contents: [{ uri, mimeType: "text/markdown", text: text2 }] } };
979
+ }
980
+ if (request.method === "prompts/list") {
981
+ return { jsonrpc: "2.0", id, result: { prompts: skills.map((s) => ({ name: s.name, description: s.description })) } };
982
+ }
983
+ const skill = skills.find((s) => s.name === params.name);
984
+ const text = skill ? readSkillPath(`${skill.name}/SKILL.md`) : void 0;
985
+ if (!skill || text === void 0) return errorResponse(id, JSON_RPC_ERRORS.invalidParams, `Unknown prompt: ${String(params.name)}`);
986
+ return {
987
+ jsonrpc: "2.0",
988
+ id,
989
+ result: {
990
+ description: skill.description,
991
+ messages: [{ role: "user", content: { type: "text", text: parseSkillFrontmatter(text).body.trim() } }]
992
+ }
993
+ };
994
+ }
995
+ }
996
+ if (isNotification) return null;
997
+ return errorResponse(id, JSON_RPC_ERRORS.methodNotFound, `Method not found: ${request.method}`);
998
+ }
999
+ return {
1000
+ get initialized() {
1001
+ return initialized;
1002
+ },
1003
+ async handle(message) {
1004
+ let request;
1005
+ if (typeof message === "string") {
1006
+ try {
1007
+ request = JSON.parse(message);
1008
+ } catch (e3) {
1009
+ return errorResponse(null, JSON_RPC_ERRORS.parseError, "Parse error.");
1010
+ }
1011
+ } else {
1012
+ request = message;
1013
+ }
1014
+ if (!request || typeof request !== "object" || Array.isArray(request) || request.jsonrpc !== "2.0" || typeof request.method !== "string") {
1015
+ const id = request && typeof request === "object" && !Array.isArray(request) ? _nullishCoalesce(request.id, () => ( null)) : null;
1016
+ return errorResponse(id, JSON_RPC_ERRORS.invalidRequest, "Invalid JSON-RPC 2.0 request.");
1017
+ }
1018
+ try {
1019
+ return await dispatch(request);
1020
+ } catch (error) {
1021
+ if (request.id === void 0 || request.id === null) return null;
1022
+ return errorResponse(
1023
+ request.id,
1024
+ JSON_RPC_ERRORS.internalError,
1025
+ error instanceof Error ? error.message : String(error)
1026
+ );
1027
+ }
1028
+ },
1029
+ close() {
1030
+ _optionalChain([unsubscribe, 'optionalCall', _30 => _30()]);
1031
+ unsubscribe = null;
1032
+ for (const controller of running.values()) controller.abort("Server closed.");
1033
+ running.clear();
1034
+ }
1035
+ };
1036
+ }
1037
+
1038
+ // src/tools/bridge-client.ts
1039
+ var TOOL_BRIDGE_PROTOCOL = 1;
1040
+ var TOOL_BRIDGE_DEFAULT_PORT = 4477;
1041
+ function connectToolBridge(registry, options) {
1042
+ const Ctor = _nullishCoalesce(options.WebSocket, () => ( globalThis.WebSocket));
1043
+ if (!Ctor) throw new Error("No WebSocket implementation: pass options.WebSocket.");
1044
+ const base = _nullishCoalesce(options.url, () => ( `ws://127.0.0.1:${TOOL_BRIDGE_DEFAULT_PORT}`));
1045
+ const url = options.token ? `${base}${base.includes("?") ? "&" : "?"}token=${encodeURIComponent(options.token)}` : base;
1046
+ let status = "connecting";
1047
+ let editorId = null;
1048
+ let socket = null;
1049
+ let closed = false;
1050
+ let delay = 500;
1051
+ let retry = null;
1052
+ const running = /* @__PURE__ */ new Map();
1053
+ const setStatus = (next, detail) => {
1054
+ if (next === status && !detail) return;
1055
+ status = next;
1056
+ _optionalChain([options, 'access', _31 => _31.onStatus, 'optionalCall', _32 => _32(next, detail)]);
1057
+ };
1058
+ const send = (message) => {
1059
+ if (socket && socket.readyState === 1) socket.send(JSON.stringify(message));
1060
+ };
1061
+ const unsubscribe = registry.onListChanged((tools) => send({ t: "tools", tools }));
1062
+ const open = () => {
1063
+ if (closed) return;
1064
+ setStatus("connecting");
1065
+ const ws = new Ctor(url);
1066
+ socket = ws;
1067
+ ws.addEventListener("open", () => {
1068
+ delay = 500;
1069
+ send({ t: "hello", v: TOOL_BRIDGE_PROTOCOL, name: options.name, ...options.kind ? { kind: options.kind } : {}, tools: registry.listTools().tools });
1070
+ });
1071
+ ws.addEventListener("message", (event) => {
1072
+ let message;
1073
+ try {
1074
+ message = JSON.parse(String(event.data));
1075
+ } catch (e4) {
1076
+ return;
1077
+ }
1078
+ if (message.t === "welcome") {
1079
+ editorId = message.id;
1080
+ setStatus("connected");
1081
+ } else if (message.t === "call") {
1082
+ const controller = new AbortController();
1083
+ running.set(message.id, controller);
1084
+ void registry.callTool(message.name, _nullishCoalesce(message.arguments, () => ( {})), { signal: controller.signal }).then((result) => {
1085
+ running.delete(message.id);
1086
+ if (!controller.signal.aborted) send({ t: "result", id: message.id, result });
1087
+ });
1088
+ } else if (message.t === "cancel") {
1089
+ _optionalChain([running, 'access', _33 => _33.get, 'call', _34 => _34(message.id), 'optionalAccess', _35 => _35.abort, 'call', _36 => _36(_nullishCoalesce(message.reason, () => ( "Cancelled by the agent.")))]);
1090
+ running.delete(message.id);
1091
+ }
1092
+ });
1093
+ ws.addEventListener("close", (event) => {
1094
+ if (socket !== ws) return;
1095
+ socket = null;
1096
+ editorId = null;
1097
+ for (const controller of running.values()) controller.abort("The bridge disconnected.");
1098
+ running.clear();
1099
+ if (closed) return setStatus("closed");
1100
+ setStatus("disconnected", event.reason || void 0);
1101
+ if (options.reconnect === false) return;
1102
+ retry = setTimeout(open, delay);
1103
+ delay = Math.min(1e4, delay * 2);
1104
+ });
1105
+ ws.addEventListener("error", () => {
1106
+ });
1107
+ };
1108
+ open();
1109
+ return {
1110
+ get status() {
1111
+ return status;
1112
+ },
1113
+ get editorId() {
1114
+ return editorId;
1115
+ },
1116
+ close() {
1117
+ if (closed) return;
1118
+ closed = true;
1119
+ if (retry) clearTimeout(retry);
1120
+ unsubscribe();
1121
+ for (const controller of running.values()) controller.abort("The editor disconnected.");
1122
+ running.clear();
1123
+ const ws = socket;
1124
+ socket = null;
1125
+ _optionalChain([ws, 'optionalAccess', _37 => _37.close, 'call', _38 => _38(1e3, "Editor closed.")]);
1126
+ setStatus("closed");
1127
+ }
1128
+ };
1129
+ }
1130
+
1131
+
1132
+
1133
+
1134
+
1135
+
1136
+
1137
+
1138
+
1139
+
1140
+
1141
+
1142
+
1143
+
1144
+
1145
+
1146
+
1147
+
1148
+
1149
+
1150
+
1151
+
1152
+
1153
+
1154
+
1155
+
1156
+ exports.searchTokens = searchTokens; exports.summarize = summarize; exports.searchTools = searchTools; exports.parseSkillFrontmatter = parseSkillFrontmatter; exports.listSkills = listSkills; exports.readSkillFile = readSkillFile; exports.TOOL_DISCOVERY_TOOLS = TOOL_DISCOVERY_TOOLS; exports.ToolRegistryView = ToolRegistryView; exports.ToolRegistryError = ToolRegistryError; exports.TOOL_NAMESPACE_SEPARATOR = TOOL_NAMESPACE_SEPARATOR; exports.toolsetName = toolsetName; exports.baseToolName = baseToolName; exports.unknownToolMessage = unknownToolMessage; exports.errorResult = errorResult; exports.toToolResult = toToolResult; exports.ToolRegistry = ToolRegistry; exports.createToolRegistry = createToolRegistry; exports.MCP_PROTOCOL_VERSIONS = MCP_PROTOCOL_VERSIONS; exports.JSON_RPC_ERRORS = JSON_RPC_ERRORS; exports.toolsetInstructions = toolsetInstructions; exports.createMcpServer = createMcpServer; exports.TOOL_BRIDGE_PROTOCOL = TOOL_BRIDGE_PROTOCOL; exports.TOOL_BRIDGE_DEFAULT_PORT = TOOL_BRIDGE_DEFAULT_PORT; exports.connectToolBridge = connectToolBridge;