kagent-ts 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/core/agent.d.ts +53 -10
  2. package/dist/core/agent.d.ts.map +1 -1
  3. package/dist/core/agent.js +45 -12
  4. package/dist/core/agent.js.map +1 -1
  5. package/dist/core/plan-solve-agent.d.ts.map +1 -1
  6. package/dist/core/plan-solve-agent.js +2 -0
  7. package/dist/core/plan-solve-agent.js.map +1 -1
  8. package/dist/core/react-agent.d.ts.map +1 -1
  9. package/dist/core/react-agent.js +2 -0
  10. package/dist/core/react-agent.js.map +1 -1
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +6 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/mcp/index.d.ts +4 -0
  16. package/dist/mcp/index.d.ts.map +1 -0
  17. package/dist/mcp/index.js +8 -0
  18. package/dist/mcp/index.js.map +1 -0
  19. package/dist/mcp/mcp-client-manager.d.ts +64 -0
  20. package/dist/mcp/mcp-client-manager.d.ts.map +1 -0
  21. package/dist/mcp/mcp-client-manager.js +224 -0
  22. package/dist/mcp/mcp-client-manager.js.map +1 -0
  23. package/dist/mcp/mcp-types.d.ts +58 -0
  24. package/dist/mcp/mcp-types.d.ts.map +1 -0
  25. package/dist/mcp/mcp-types.js +20 -0
  26. package/dist/mcp/mcp-types.js.map +1 -0
  27. package/dist/skills/file-skill-loader.d.ts +5 -14
  28. package/dist/skills/file-skill-loader.d.ts.map +1 -1
  29. package/dist/skills/file-skill-loader.js +28 -146
  30. package/dist/skills/file-skill-loader.js.map +1 -1
  31. package/dist/skills/skill-manager.d.ts +6 -23
  32. package/dist/skills/skill-manager.d.ts.map +1 -1
  33. package/dist/skills/skill-manager.js +9 -53
  34. package/dist/skills/skill-manager.js.map +1 -1
  35. package/dist/skills/types.d.ts +1 -8
  36. package/dist/skills/types.d.ts.map +1 -1
  37. package/dist/trace/trace-logger.js +1 -1
  38. package/dist/trace/trace-logger.js.map +1 -1
  39. package/package.json +2 -1
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.McpClientManager = void 0;
4
+ const mcp_types_1 = require("./mcp-types");
5
+ const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
6
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/client/stdio.js");
7
+ const sse_js_1 = require("@modelcontextprotocol/sdk/client/sse.js");
8
+ // ─── Config resolver ────────────────────────────────────────────────────────
9
+ function resolveTransportConfig(config) {
10
+ if (config.command && config.url) {
11
+ throw new mcp_types_1.McpConnectionError("MCP server config must specify either 'command' (stdio) or 'url' (SSE), not both.");
12
+ }
13
+ if (config.command) {
14
+ return { type: "stdio", command: config.command, args: config.args, env: config.env };
15
+ }
16
+ if (config.url) {
17
+ return { type: "sse", url: config.url };
18
+ }
19
+ throw new mcp_types_1.McpConnectionError("MCP server config must specify either 'command' (stdio) or 'url' (SSE).");
20
+ }
21
+ /**
22
+ * Manages connections to MCP (Model Context Protocol) servers.
23
+ *
24
+ * Lifecycle:
25
+ * 1. `connectToServer()` / `connectAll()` — connect, discover tools,
26
+ * register adapted tools in ToolRegistry
27
+ * 2. Tools execute via the adapted `Tool` wrapper (delegates to MCP client)
28
+ * 3. `disconnect()` / `disconnectAll()` — unregister tools, close connections
29
+ */
30
+ class McpClientManager {
31
+ /** Active connections keyed by server name. */
32
+ connections = new Map();
33
+ /** The ToolRegistry where adapted tools are registered. */
34
+ toolRegistry;
35
+ constructor(toolRegistry) {
36
+ this.toolRegistry = toolRegistry;
37
+ }
38
+ // ─── Connection Management ────────────────────────────────────────────────
39
+ /**
40
+ * Connect to a single MCP server, discover its tools, and register them
41
+ * in the ToolRegistry with a `{serverName}_` prefix.
42
+ *
43
+ * @throws McpConnectionError if the config is invalid or the
44
+ * connection/tool-discovery fails.
45
+ */
46
+ async connectToServer(serverName, config) {
47
+ if (this.connections.has(serverName)) {
48
+ throw new mcp_types_1.McpConnectionError(`MCP server "${serverName}" is already connected.`);
49
+ }
50
+ const transportConfig = resolveTransportConfig(config);
51
+ // Build transport
52
+ let transport;
53
+ if (transportConfig.type === "stdio") {
54
+ transport = new stdio_js_1.StdioClientTransport({
55
+ command: transportConfig.command,
56
+ args: transportConfig.args,
57
+ env: transportConfig.env,
58
+ });
59
+ }
60
+ else {
61
+ transport = new sse_js_1.SSEClientTransport(new URL(transportConfig.url));
62
+ }
63
+ // Create client and connect
64
+ const client = new index_js_1.Client({ name: "kagent-ts", version: "0.1.3" }, { capabilities: {} });
65
+ try {
66
+ await client.connect(transport);
67
+ }
68
+ catch (err) {
69
+ throw new mcp_types_1.McpConnectionError(`Failed to connect to MCP server "${serverName}": ${err instanceof Error ? err.message : String(err)}`);
70
+ }
71
+ // Discover tools
72
+ let mcpToolDescriptors;
73
+ try {
74
+ const result = await client.listTools();
75
+ mcpToolDescriptors = result.tools;
76
+ }
77
+ catch (err) {
78
+ await client.close().catch(() => { });
79
+ throw new mcp_types_1.McpConnectionError(`Failed to list tools from MCP server "${serverName}": ${err instanceof Error ? err.message : String(err)}`);
80
+ }
81
+ // Adapt and register tools
82
+ const adaptedTools = [];
83
+ for (const mcpTool of mcpToolDescriptors) {
84
+ const adapted = this.adaptTool(serverName, mcpTool, client);
85
+ if (this.toolRegistry.has(adapted.name)) {
86
+ console.warn(`[MCP] Tool "${adapted.name}" is already registered. ` +
87
+ `Skipping MCP tool "${serverName}/${mcpTool.name}".`);
88
+ continue;
89
+ }
90
+ adaptedTools.push(adapted);
91
+ }
92
+ if (adaptedTools.length === 0) {
93
+ console.warn(`[MCP] Server "${serverName}" exposed no usable tools.`);
94
+ }
95
+ else {
96
+ this.toolRegistry.registerMany(adaptedTools);
97
+ console.log(`[MCP] Registered ${adaptedTools.length} tool(s) from server "${serverName}".`);
98
+ }
99
+ this.connections.set(serverName, { serverName, client, tools: adaptedTools });
100
+ }
101
+ /**
102
+ * Connect to multiple MCP servers. Failures for individual servers are
103
+ * collected and logged; other servers still connect.
104
+ *
105
+ * @returns An array of connection error reports (empty if all succeeded).
106
+ */
107
+ async connectAll(servers) {
108
+ const errors = [];
109
+ await Promise.allSettled(Object.entries(servers).map(async ([name, config]) => {
110
+ try {
111
+ await this.connectToServer(name, config);
112
+ }
113
+ catch (err) {
114
+ const message = err instanceof Error ? err.message : String(err);
115
+ errors.push({ serverName: name, error: message });
116
+ console.error(`[MCP] Failed to connect to server "${name}": ${message}`);
117
+ }
118
+ }));
119
+ return errors;
120
+ }
121
+ /**
122
+ * Disconnect from a single server and unregister its tools.
123
+ */
124
+ async disconnect(serverName) {
125
+ const conn = this.connections.get(serverName);
126
+ if (!conn)
127
+ return;
128
+ // Unregister tools
129
+ for (const tool of conn.tools) {
130
+ this.toolRegistry.remove(tool.name);
131
+ }
132
+ // Close the client
133
+ try {
134
+ await conn.client.close();
135
+ }
136
+ catch (err) {
137
+ console.warn(`[MCP] Error while closing connection to "${serverName}": ${err instanceof Error ? err.message : String(err)}`);
138
+ }
139
+ this.connections.delete(serverName);
140
+ console.log(`[MCP] Disconnected from server "${serverName}".`);
141
+ }
142
+ /**
143
+ * Disconnect from all MCP servers and unregister all MCP-provided tools.
144
+ */
145
+ async disconnectAll() {
146
+ const names = Array.from(this.connections.keys());
147
+ await Promise.allSettled(names.map((name) => this.disconnect(name)));
148
+ }
149
+ // ─── Queries ──────────────────────────────────────────────────────────────
150
+ /**
151
+ * Get connection status for all servers.
152
+ */
153
+ getStatus() {
154
+ return Array.from(this.connections.entries()).map(([name, conn]) => ({
155
+ serverName: name,
156
+ connected: true,
157
+ toolCount: conn.tools.length,
158
+ }));
159
+ }
160
+ /**
161
+ * Whether at least one server is connected.
162
+ */
163
+ get isConnected() {
164
+ return this.connections.size > 0;
165
+ }
166
+ /**
167
+ * Get the number of connected servers.
168
+ */
169
+ get connectedCount() {
170
+ return this.connections.size;
171
+ }
172
+ // ─── Tool Adapter ─────────────────────────────────────────────────────────
173
+ /**
174
+ * Adapt an MCP SDK tool descriptor into the framework's Tool interface.
175
+ *
176
+ * The adapted tool's name is `{serverName}_{mcpToolName}` to avoid
177
+ * collisions between servers and with locally-registered tools.
178
+ *
179
+ * The `execute` function closes over the client instance and handles
180
+ * result content parsing (extracting text from MCP's Content[] array).
181
+ */
182
+ adaptTool(serverName, mcpTool, client) {
183
+ const prefixedName = `${serverName}_${mcpTool.name}`;
184
+ return {
185
+ name: prefixedName,
186
+ description: mcpTool.description ?? "",
187
+ parameters: (mcpTool.inputSchema ?? { type: "object", properties: {} }),
188
+ execute: async (args) => {
189
+ // Check connection is still alive
190
+ if (!this.connections.has(serverName)) {
191
+ return `Error: MCP server "${serverName}" is no longer connected. Tool "${prefixedName}" is unavailable.`;
192
+ }
193
+ try {
194
+ const result = await client.callTool({
195
+ name: mcpTool.name,
196
+ arguments: args,
197
+ });
198
+ // MCP results carry an array of content items (text, image, etc.)
199
+ let output = "(MCP tool returned no content)";
200
+ if (result.content && Array.isArray(result.content)) {
201
+ const textParts = result.content
202
+ .map((c) => (c.text ? c.text : JSON.stringify(c)))
203
+ .filter(Boolean);
204
+ if (textParts.length > 0) {
205
+ output = textParts.join("\n");
206
+ }
207
+ }
208
+ // Prefix with "Error:" so the framework's circuit-breaker /
209
+ // retry-guidance path handles it correctly.
210
+ if (result.isError) {
211
+ return `Error: ${output}`;
212
+ }
213
+ return output;
214
+ }
215
+ catch (err) {
216
+ const message = err instanceof Error ? err.message : String(err);
217
+ return `Error executing MCP tool "${serverName}/${mcpTool.name}": ${message}`;
218
+ }
219
+ },
220
+ };
221
+ }
222
+ }
223
+ exports.McpClientManager = McpClientManager;
224
+ //# sourceMappingURL=mcp-client-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client-manager.js","sourceRoot":"","sources":["../../src/mcp/mcp-client-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * MCP (Model Context Protocol) type definitions.
3
+ *
4
+ * Users define MCP servers via `McpServerConfig`. The framework
5
+ * connects, discovers tools, adapts them, and registers with ToolRegistry.
6
+ */
7
+ /**
8
+ * Configuration for connecting to an MCP server.
9
+ *
10
+ * Two mutually exclusive modes:
11
+ * - stdio: set `command` (optionally `args` and `env`)
12
+ * - SSE: set `url`
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // stdio: local filesystem server
17
+ * { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "."] }
18
+ *
19
+ * // SSE: remote server
20
+ * { url: "http://localhost:3001/sse" }
21
+ * ```
22
+ */
23
+ export interface McpServerConfig {
24
+ /** Command for stdio transport (e.g., "npx", "uvx"). Exclusive with `url`. */
25
+ command?: string;
26
+ /** Arguments passed to the stdio command. */
27
+ args?: string[];
28
+ /** Environment variables injected into the stdio process. */
29
+ env?: Record<string, string>;
30
+ /**
31
+ * URL for SSE transport (e.g., "http://localhost:3000/sse").
32
+ * Exclusive with `command`.
33
+ */
34
+ url?: string;
35
+ }
36
+ /**
37
+ * Snapshot of a single MCP server's connection state.
38
+ */
39
+ export interface McpConnectionStatus {
40
+ serverName: string;
41
+ connected: boolean;
42
+ toolCount: number;
43
+ error?: string;
44
+ }
45
+ /**
46
+ * Report from a failed connection attempt.
47
+ */
48
+ export interface McpConnectionErrorReport {
49
+ serverName: string;
50
+ error: string;
51
+ }
52
+ /**
53
+ * Specialized error for MCP connection/issues.
54
+ */
55
+ export declare class McpConnectionError extends Error {
56
+ constructor(message: string);
57
+ }
58
+ //# sourceMappingURL=mcp-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-types.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * MCP (Model Context Protocol) type definitions.
4
+ *
5
+ * Users define MCP servers via `McpServerConfig`. The framework
6
+ * connects, discovers tools, adapts them, and registers with ToolRegistry.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.McpConnectionError = void 0;
10
+ /**
11
+ * Specialized error for MCP connection/issues.
12
+ */
13
+ class McpConnectionError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "McpConnectionError";
17
+ }
18
+ }
19
+ exports.McpConnectionError = McpConnectionError;
20
+ //# sourceMappingURL=mcp-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-types.js","sourceRoot":"","sources":["../../src/mcp/mcp-types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAkDH;;GAEG;AACH,MAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC"}
@@ -1,5 +1,4 @@
1
1
  import { Skill } from "./types";
2
- import { Tool } from "../tools/types";
3
2
  /**
4
3
  * Parse YAML-like frontmatter from a Markdown file.
5
4
  *
@@ -32,15 +31,14 @@ export declare function parseKeywords(raw?: string): string[] | undefined;
32
31
  * skills/
33
32
  * ├── <skill-name>/
34
33
  * │ ├── SKILL.md # Frontmatter (metadata) + body (system prompt)
35
- * │ ├── reference/ # Optional reference docs (*.md, *.txt)
36
- * │ └── scripts/ # Optional executable scripts
34
+ * │ └── reference/ # Optional reference docs (*.md, *.txt)
37
35
  * ├── <another-skill>/
38
36
  * │ └── ...
39
37
  * ```
40
38
  *
41
39
  * Loading is two-phase:
42
40
  * 1. `scan()` — reads only frontmatter, returns metadata-only `Skill[]`
43
- * 2. `loadSystemPrompt()` / `loadScriptsAsTools()` full content, called
41
+ * 2. `loadSystemPrompt()` full content (body + reference docs), called
44
42
  * lazily on activation.
45
43
  */
46
44
  export declare class FileSkillLoader {
@@ -55,14 +53,15 @@ export declare class FileSkillLoader {
55
53
  getDirectory(): string;
56
54
  /**
57
55
  * Get the absolute path to a named skill's subdirectory.
56
+ * Throws if the skill name contains path traversal characters.
58
57
  */
59
58
  getSkillDir(name: string): string;
60
59
  /**
61
60
  * Scan the skills directory and return metadata-only Skill objects.
62
61
  *
63
62
  * Reads only the frontmatter from each SKILL.md — the body (systemPrompt)
64
- * and scripts are NOT loaded at this stage. They are loaded lazily when
65
- * the skill is activated.
63
+ * is NOT loaded at this stage. It is loaded lazily when the skill is
64
+ * activated.
66
65
  *
67
66
  * Subdirectories without a valid SKILL.md (or without a `name` in
68
67
  * frontmatter) are skipped with a warning.
@@ -76,13 +75,5 @@ export declare class FileSkillLoader {
76
75
  * Throws if the skill directory does not exist.
77
76
  */
78
77
  loadSystemPrompt(name: string): string;
79
- /**
80
- * Create Tool objects from scripts in the scripts/ subdirectory.
81
- *
82
- * Each recognized script file becomes a Tool named `{skillName}_{scriptName}`.
83
- *
84
- * Returns an empty array if the scripts/ directory is missing or empty.
85
- */
86
- loadScriptsAsTools(name: string): Tool[];
87
78
  }
88
79
  //# sourceMappingURL=file-skill-loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-skill-loader.d.ts","sourceRoot":"","sources":["../../src/skills/file-skill-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAItC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd,CAkBA;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAOhE;AAiFD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAS;IAE1B;;OAEG;gBACS,SAAS,CAAC,EAAE,MAAM;IAI9B;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIjC;;;;;;;;;OASG;IACH,IAAI,IAAI,KAAK,EAAE;IAmDf;;;;;;OAMG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAsCtC;;;;;;OAMG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE;CAgFzC"}
1
+ {"version":3,"file":"file-skill-loader.d.ts","sourceRoot":"","sources":["../../src/skills/file-skill-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAIhC;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd,CAuBA;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAOhE;AAwBD;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAS;IAE1B;;OAEG;gBACS,SAAS,CAAC,EAAE,MAAM;IAI9B;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;;OAGG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAKjC;;;;;;;;;OASG;IACH,IAAI,IAAI,KAAK,EAAE;IA0Df;;;;;;OAMG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAsCvC"}
@@ -38,7 +38,6 @@ exports.parseFrontmatter = parseFrontmatter;
38
38
  exports.parseKeywords = parseKeywords;
39
39
  const fs = __importStar(require("fs"));
40
40
  const path = __importStar(require("path"));
41
- const child_process_1 = require("child_process");
42
41
  // ─── Frontmatter Parsing ───────────────────────────────────────────────────
43
42
  /**
44
43
  * Parse YAML-like frontmatter from a Markdown file.
@@ -69,7 +68,12 @@ function parseFrontmatter(raw) {
69
68
  if (colonIdx <= 0)
70
69
  continue;
71
70
  const key = trimmed.slice(0, colonIdx).trim();
72
- const value = trimmed.slice(colonIdx + 1).trim();
71
+ let value = trimmed.slice(colonIdx + 1).trim();
72
+ // Strip surrounding quotes if both ends match
73
+ if ((value.startsWith('"') && value.endsWith('"')) ||
74
+ (value.startsWith("'") && value.endsWith("'"))) {
75
+ value = value.slice(1, -1).trim();
76
+ }
73
77
  if (key)
74
78
  frontmatter[key] = value;
75
79
  }
@@ -89,78 +93,19 @@ function parseKeywords(raw) {
89
93
  return keywords.length > 0 ? keywords : undefined;
90
94
  }
91
95
  // ─── Script Execution ──────────────────────────────────────────────────────
92
- const SUPPORTED_SCRIPT_EXTENSIONS = new Set([
93
- ".sh",
94
- ".bat",
95
- ".cmd",
96
- ".ps1",
97
- ".js",
98
- ".py",
99
- ]);
100
96
  const REFERENCE_EXTENSIONS = new Set([".md", ".txt"]);
101
97
  /**
102
- * Determine the interpreter and arguments needed to run a script file.
103
- * Returns `null` if the file extension is not supported.
104
- */
105
- function getInterpreter(filePath) {
106
- const ext = path.extname(filePath).toLowerCase();
107
- switch (ext) {
108
- case ".sh":
109
- return ["bash", filePath];
110
- case ".bat":
111
- case ".cmd":
112
- return ["cmd.exe", "/c", filePath];
113
- case ".ps1":
114
- return ["powershell.exe", "-File", filePath];
115
- case ".js":
116
- return ["node", filePath];
117
- case ".py":
118
- // Try python3 first, fall back to python
119
- try {
120
- (0, child_process_1.execFileSync)("python3", ["--version"], { timeout: 5000, stdio: "ignore" });
121
- return ["python3", filePath];
122
- }
123
- catch {
124
- return ["python", filePath];
125
- }
126
- default:
127
- return null;
128
- }
129
- }
130
- /**
131
- * Extract a description for a script tool from the first comment/line
132
- * of the file, or fall back to a generic description.
98
+ * Validate a skill name to prevent path traversal.
99
+ * Only allows alphanumeric characters, hyphens, underscores, and dots.
133
100
  */
134
- function getScriptDescription(filePath) {
135
- try {
136
- const content = fs.readFileSync(filePath, "utf-8");
137
- const firstLine = content.split("\n")[0]?.trim();
138
- if (firstLine) {
139
- // Strip common comment markers and shebang
140
- const clean = firstLine
141
- .replace(/^#!\s*/, "")
142
- .replace(/^#\s*/, "")
143
- .replace(/^\/\/\s*/, "")
144
- .replace(/^--\s*/, "")
145
- .trim();
146
- if (clean && !clean.startsWith("/"))
147
- return clean;
148
- }
101
+ function validateSkillName(name) {
102
+ if (!name) {
103
+ throw new Error(`Skill name must not be empty.`);
149
104
  }
150
- catch {
151
- // Ignore read errors
105
+ // Prevent path traversal: reject names with slashes, backslashes, "..", or null bytes
106
+ if (/[/\\]|\.\.|\0/.test(name)) {
107
+ throw new Error(`Invalid skill name "${name}": contains path traversal characters.`);
152
108
  }
153
- return `Execute the ${path.basename(filePath)} script`;
154
- }
155
- /**
156
- * Sanitize a string for use as a tool name (alphanumeric + underscores only).
157
- */
158
- function sanitizeToolName(name) {
159
- return name
160
- .toLowerCase()
161
- .replace(/[^a-z0-9_]/g, "_")
162
- .replace(/_+/g, "_")
163
- .replace(/^_|_$/g, "");
164
109
  }
165
110
  // ─── FileSkillLoader ───────────────────────────────────────────────────────
166
111
  /**
@@ -171,15 +116,14 @@ function sanitizeToolName(name) {
171
116
  * skills/
172
117
  * ├── <skill-name>/
173
118
  * │ ├── SKILL.md # Frontmatter (metadata) + body (system prompt)
174
- * │ ├── reference/ # Optional reference docs (*.md, *.txt)
175
- * │ └── scripts/ # Optional executable scripts
119
+ * │ └── reference/ # Optional reference docs (*.md, *.txt)
176
120
  * ├── <another-skill>/
177
121
  * │ └── ...
178
122
  * ```
179
123
  *
180
124
  * Loading is two-phase:
181
125
  * 1. `scan()` — reads only frontmatter, returns metadata-only `Skill[]`
182
- * 2. `loadSystemPrompt()` / `loadScriptsAsTools()` full content, called
126
+ * 2. `loadSystemPrompt()` full content (body + reference docs), called
183
127
  * lazily on activation.
184
128
  */
185
129
  class FileSkillLoader {
@@ -198,16 +142,18 @@ class FileSkillLoader {
198
142
  }
199
143
  /**
200
144
  * Get the absolute path to a named skill's subdirectory.
145
+ * Throws if the skill name contains path traversal characters.
201
146
  */
202
147
  getSkillDir(name) {
148
+ validateSkillName(name);
203
149
  return path.join(this.directory, name);
204
150
  }
205
151
  /**
206
152
  * Scan the skills directory and return metadata-only Skill objects.
207
153
  *
208
154
  * Reads only the frontmatter from each SKILL.md — the body (systemPrompt)
209
- * and scripts are NOT loaded at this stage. They are loaded lazily when
210
- * the skill is activated.
155
+ * is NOT loaded at this stage. It is loaded lazily when the skill is
156
+ * activated.
211
157
  *
212
158
  * Subdirectories without a valid SKILL.md (or without a `name` in
213
159
  * frontmatter) are skipped with a warning.
@@ -244,12 +190,19 @@ class FileSkillLoader {
244
190
  console.warn(`[Skills] Skipping "${entry.name}": SKILL.md has no "name" in frontmatter`);
245
191
  continue;
246
192
  }
193
+ try {
194
+ validateSkillName(name);
195
+ }
196
+ catch (err) {
197
+ const message = err instanceof Error ? err.message : String(err);
198
+ console.warn(`[Skills] Skipping "${entry.name}": ${message}`);
199
+ continue;
200
+ }
247
201
  skills.push({
248
202
  name,
249
203
  description: frontmatter.description?.trim() ?? "",
250
204
  systemPrompt: "", // Loaded lazily on activation
251
205
  keywords: parseKeywords(frontmatter.keywords),
252
- tools: [], // Loaded lazily on activation
253
206
  });
254
207
  }
255
208
  return skills;
@@ -289,77 +242,6 @@ class FileSkillLoader {
289
242
  }
290
243
  return parts.join("").trim();
291
244
  }
292
- /**
293
- * Create Tool objects from scripts in the scripts/ subdirectory.
294
- *
295
- * Each recognized script file becomes a Tool named `{skillName}_{scriptName}`.
296
- *
297
- * Returns an empty array if the scripts/ directory is missing or empty.
298
- */
299
- loadScriptsAsTools(name) {
300
- const scriptsDir = path.join(this.getSkillDir(name), "scripts");
301
- const tools = [];
302
- let entries;
303
- try {
304
- entries = fs.readdirSync(scriptsDir, { withFileTypes: true });
305
- }
306
- catch {
307
- // No scripts/ directory — return empty
308
- return [];
309
- }
310
- for (const entry of entries) {
311
- if (!entry.isFile())
312
- continue;
313
- const ext = path.extname(entry.name).toLowerCase();
314
- if (!SUPPORTED_SCRIPT_EXTENSIONS.has(ext))
315
- continue;
316
- const scriptPath = path.join(scriptsDir, entry.name);
317
- const scriptName = path.basename(entry.name, ext);
318
- const toolName = sanitizeToolName(`${name}_${scriptName}`);
319
- const toolDescription = getScriptDescription(scriptPath);
320
- const tool = {
321
- name: toolName,
322
- description: toolDescription,
323
- parameters: {
324
- type: "object",
325
- properties: {
326
- args: {
327
- type: "string",
328
- description: `Arguments to pass to the ${entry.name} script. ` +
329
- `Use shell-style syntax (space-separated).`,
330
- },
331
- },
332
- required: ["args"],
333
- },
334
- async execute(params) {
335
- const argsStr = String(params.args ?? "");
336
- const interpreter = getInterpreter(scriptPath);
337
- if (!interpreter) {
338
- return `Error: Unsupported script type "${ext}" for ${entry.name}`;
339
- }
340
- const [cmd, ...cmdArgs] = interpreter;
341
- // Append the script argument after the interpreter args
342
- const allArgs = argsStr
343
- ? [...cmdArgs, ...argsStr.split(/\s+/).filter(Boolean)]
344
- : cmdArgs;
345
- return new Promise((resolve) => {
346
- const child = (0, child_process_1.execFile)(cmd, allArgs, { timeout: 30000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
347
- if (err) {
348
- const details = stderr
349
- ? `\nstderr:\n${stderr.trim()}`
350
- : "";
351
- resolve(`Error executing ${entry.name}: ${err.message}${details}`);
352
- return;
353
- }
354
- resolve(stdout.trim() || "(no output)");
355
- });
356
- });
357
- },
358
- };
359
- tools.push(tool);
360
- }
361
- return tools;
362
- }
363
245
  }
364
246
  exports.FileSkillLoader = FileSkillLoader;
365
247
  //# sourceMappingURL=file-skill-loader.js.map