@agentscope-ai/agentscope 0.0.2

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 (136) hide show
  1. package/dist/agent/index.d.mts +234 -0
  2. package/dist/agent/index.d.ts +234 -0
  3. package/dist/agent/index.js +1412 -0
  4. package/dist/agent/index.js.map +1 -0
  5. package/dist/agent/index.mjs +1375 -0
  6. package/dist/agent/index.mjs.map +1 -0
  7. package/dist/base-BOx3UzOl.d.mts +41 -0
  8. package/dist/base-BoIps2RL.d.ts +41 -0
  9. package/dist/base-C7jwyH4Z.d.mts +52 -0
  10. package/dist/base-Cwi4bjze.d.ts +127 -0
  11. package/dist/base-DYlBMCy_.d.mts +127 -0
  12. package/dist/base-NX-knWOv.d.ts +52 -0
  13. package/dist/block-VsnHrllL.d.mts +48 -0
  14. package/dist/block-VsnHrllL.d.ts +48 -0
  15. package/dist/event/index.d.mts +181 -0
  16. package/dist/event/index.d.ts +181 -0
  17. package/dist/event/index.js +58 -0
  18. package/dist/event/index.js.map +1 -0
  19. package/dist/event/index.mjs +33 -0
  20. package/dist/event/index.mjs.map +1 -0
  21. package/dist/formatter/index.d.mts +187 -0
  22. package/dist/formatter/index.d.ts +187 -0
  23. package/dist/formatter/index.js +647 -0
  24. package/dist/formatter/index.js.map +1 -0
  25. package/dist/formatter/index.mjs +616 -0
  26. package/dist/formatter/index.mjs.map +1 -0
  27. package/dist/index-BTJDlKvQ.d.mts +195 -0
  28. package/dist/index-BcatlwXQ.d.ts +195 -0
  29. package/dist/index-CAxQAkiP.d.mts +21 -0
  30. package/dist/index-CAxQAkiP.d.ts +21 -0
  31. package/dist/mcp/index.d.mts +9 -0
  32. package/dist/mcp/index.d.ts +9 -0
  33. package/dist/mcp/index.js +432 -0
  34. package/dist/mcp/index.js.map +1 -0
  35. package/dist/mcp/index.mjs +408 -0
  36. package/dist/mcp/index.mjs.map +1 -0
  37. package/dist/message/index.d.mts +10 -0
  38. package/dist/message/index.d.ts +10 -0
  39. package/dist/message/index.js +67 -0
  40. package/dist/message/index.js.map +1 -0
  41. package/dist/message/index.mjs +37 -0
  42. package/dist/message/index.mjs.map +1 -0
  43. package/dist/message-CkN21KaY.d.mts +99 -0
  44. package/dist/message-CzLeTlua.d.ts +99 -0
  45. package/dist/model/index.d.mts +377 -0
  46. package/dist/model/index.d.ts +377 -0
  47. package/dist/model/index.js +1880 -0
  48. package/dist/model/index.js.map +1 -0
  49. package/dist/model/index.mjs +1849 -0
  50. package/dist/model/index.mjs.map +1 -0
  51. package/dist/storage/index.d.mts +68 -0
  52. package/dist/storage/index.d.ts +68 -0
  53. package/dist/storage/index.js +250 -0
  54. package/dist/storage/index.js.map +1 -0
  55. package/dist/storage/index.mjs +212 -0
  56. package/dist/storage/index.mjs.map +1 -0
  57. package/dist/tool/index.d.mts +311 -0
  58. package/dist/tool/index.d.ts +311 -0
  59. package/dist/tool/index.js +1494 -0
  60. package/dist/tool/index.js.map +1 -0
  61. package/dist/tool/index.mjs +1447 -0
  62. package/dist/tool/index.mjs.map +1 -0
  63. package/dist/toolkit-CEpulFi0.d.ts +99 -0
  64. package/dist/toolkit-CGEZSZPa.d.mts +99 -0
  65. package/jest.config.js +11 -0
  66. package/package.json +92 -0
  67. package/src/_utils/common.ts +104 -0
  68. package/src/_utils/index.ts +1 -0
  69. package/src/agent/agent-base.ts +0 -0
  70. package/src/agent/agent.test.ts +1028 -0
  71. package/src/agent/agent.ts +1032 -0
  72. package/src/agent/index.ts +2 -0
  73. package/src/agent/interfaces.ts +23 -0
  74. package/src/agent/test-compression.ts +72 -0
  75. package/src/event/index.ts +250 -0
  76. package/src/formatter/base.ts +133 -0
  77. package/src/formatter/dashscope-chat-formatter.test.ts +372 -0
  78. package/src/formatter/dashscope-chat-formatter.ts +163 -0
  79. package/src/formatter/deepseek-chat-formatter.ts +130 -0
  80. package/src/formatter/index.ts +5 -0
  81. package/src/formatter/ollama-chat-formatter.ts +67 -0
  82. package/src/formatter/openai-chat-formatter.test.ts +263 -0
  83. package/src/formatter/openai-chat-formatter.ts +301 -0
  84. package/src/formatter/openai.md +767 -0
  85. package/src/mcp/base.ts +114 -0
  86. package/src/mcp/http.test.ts +303 -0
  87. package/src/mcp/http.ts +224 -0
  88. package/src/mcp/index.ts +2 -0
  89. package/src/mcp/stdio.test.ts +91 -0
  90. package/src/mcp/stdio.ts +119 -0
  91. package/src/message/block.ts +60 -0
  92. package/src/message/enums.ts +4 -0
  93. package/src/message/index.ts +12 -0
  94. package/src/message/message.test.ts +80 -0
  95. package/src/message/message.ts +131 -0
  96. package/src/model/base.ts +226 -0
  97. package/src/model/dashscope-model.test.ts +335 -0
  98. package/src/model/dashscope-model.ts +441 -0
  99. package/src/model/deepseek-model.test.ts +279 -0
  100. package/src/model/deepseek-model.ts +401 -0
  101. package/src/model/index.ts +7 -0
  102. package/src/model/ollama-model.test.ts +307 -0
  103. package/src/model/ollama-model.ts +356 -0
  104. package/src/model/openai-model.ts +327 -0
  105. package/src/model/response.ts +22 -0
  106. package/src/model/usage.ts +12 -0
  107. package/src/storage/base.ts +52 -0
  108. package/src/storage/file-system.test.ts +587 -0
  109. package/src/storage/file-system.ts +269 -0
  110. package/src/storage/index.ts +2 -0
  111. package/src/tool/base.ts +23 -0
  112. package/src/tool/bash.test.ts +174 -0
  113. package/src/tool/bash.ts +152 -0
  114. package/src/tool/edit.test.ts +83 -0
  115. package/src/tool/edit.ts +95 -0
  116. package/src/tool/glob.test.ts +63 -0
  117. package/src/tool/glob.ts +166 -0
  118. package/src/tool/grep.test.ts +74 -0
  119. package/src/tool/grep.ts +256 -0
  120. package/src/tool/index.ts +10 -0
  121. package/src/tool/read.test.ts +77 -0
  122. package/src/tool/read.ts +117 -0
  123. package/src/tool/response.ts +82 -0
  124. package/src/tool/task.test.ts +299 -0
  125. package/src/tool/task.ts +399 -0
  126. package/src/tool/toolkit.test.ts +636 -0
  127. package/src/tool/toolkit.ts +601 -0
  128. package/src/tool/write.test.ts +52 -0
  129. package/src/tool/write.ts +57 -0
  130. package/src/type/index.ts +52 -0
  131. package/tsconfig.build.json +4 -0
  132. package/tsconfig.cjs.json +11 -0
  133. package/tsconfig.esm.json +10 -0
  134. package/tsconfig.json +14 -0
  135. package/tsup.config.ts +20 -0
  136. package/typedoc.json +52 -0
@@ -0,0 +1,1447 @@
1
+ // src/tool/response.ts
2
+ function createToolResponse({
3
+ content,
4
+ state,
5
+ id = crypto.randomUUID(),
6
+ createdAt = (/* @__PURE__ */ new Date()).toISOString(),
7
+ metadata = {},
8
+ stream = false,
9
+ isLast = true,
10
+ isInterrupted = false
11
+ }) {
12
+ return {
13
+ content,
14
+ id,
15
+ createdAt,
16
+ metadata,
17
+ state,
18
+ stream,
19
+ isLast,
20
+ isInterrupted
21
+ };
22
+ }
23
+ function isToolResponse(obj) {
24
+ return obj && typeof obj === "object" && typeof obj.id === "string" && typeof obj.createdAt === "string" && Array.isArray(obj.content) && typeof obj.metadata === "object" && typeof obj.stream === "boolean" && typeof obj.isLast === "boolean" && typeof obj.isInterrupted === "boolean" && ["success", "error", "interrupted", "running"].includes(obj.state);
25
+ }
26
+
27
+ // src/tool/toolkit.ts
28
+ import * as fs from "fs";
29
+ import * as path from "path";
30
+ import { Validator } from "@cfworker/json-schema";
31
+ import matter from "gray-matter";
32
+ import { z } from "zod";
33
+
34
+ // src/_utils/common.ts
35
+ import { jsonrepair } from "jsonrepair";
36
+ function _jsonLoadsWithRepair(input) {
37
+ try {
38
+ const jsonObj = JSON.parse(input);
39
+ if (typeof jsonObj === "object" && jsonObj !== null && !Array.isArray(jsonObj)) {
40
+ return jsonObj;
41
+ } else {
42
+ return {};
43
+ }
44
+ } catch {
45
+ try {
46
+ const repairedString = jsonrepair(input);
47
+ const jsonObj = JSON.parse(repairedString);
48
+ if (typeof jsonObj === "object" && jsonObj !== null && !Array.isArray(jsonObj)) {
49
+ return jsonObj;
50
+ } else {
51
+ return {};
52
+ }
53
+ } catch (e) {
54
+ console.error(`Failed to parse JSON "${input}" with error:`, e);
55
+ return {};
56
+ }
57
+ }
58
+ }
59
+
60
+ // src/tool/toolkit.ts
61
+ var Toolkit = class {
62
+ tools;
63
+ skills;
64
+ skillDirs;
65
+ // The cache mapping from the skill name to its corresponding tool name in the toolkit.
66
+ _skillCache;
67
+ /**
68
+ * Initializes a new instance of the Toolkit class.
69
+ * @param config - The configuration object for initializing the toolkit, which can include an array of tools, an array of skill paths, an array of skill directory paths, and a boolean indicating whether to include the built-in skill tool for reading SKILL.md files.
70
+ * @param config.tools - An array of tool definitions to register in the toolkit.
71
+ * @param config.skills - An array of file paths pointing to individual skills.
72
+ * @param config.skillDirs - An array of directory paths, where each directory can contain multiple skills in its subdirectories.
73
+ * @param config.builtInSkillTool - A boolean flag indicating whether to include the built-in skill tool for reading SKILL.md files.
74
+ */
75
+ constructor(config) {
76
+ const { tools = [], skills = [], skillDirs = [], builtInSkillTool = true } = config || {};
77
+ this.tools = [];
78
+ if (builtInSkillTool) {
79
+ this.tools.push({
80
+ type: "function",
81
+ name: "Skill",
82
+ description: `Retrieves the full content of a skill by reading its SKILL.md file. Skills are packages of domain expertise that extend agent capabilities. Use this tool to access detailed instructions, examples, and guidelines for a specific skill.
83
+
84
+ Usage:
85
+ - Provide the skill name as the input parameter
86
+ - The tool will return the complete SKILL.md file content for that skill
87
+ - If the skill is not found, an error message with available skills will be returned
88
+ - Available skills are listed in the skills-system section of the agent prompt`,
89
+ inputSchema: z.object({ name: z.string().describe("The name of the skill") }),
90
+ call: this._skillTool.bind(this),
91
+ requireUserConfirm: false
92
+ });
93
+ }
94
+ tools.map((tool) => {
95
+ this.tools.push({
96
+ type: "function",
97
+ ...tool
98
+ });
99
+ });
100
+ this.skills = skills;
101
+ this.skillDirs = skillDirs;
102
+ this._skillCache = {};
103
+ }
104
+ /**
105
+ * Registers a tool function to the toolkit. The function can be either a plain function that adheres to the ToolFunction type, or an instance of a class that extends ToolBase. When registering a plain function, the name, description, and input schema must be provided explicitly. When registering a ToolBase instance, these properties will be extracted from the instance itself.
106
+ *
107
+ * @params tool - The tool function to register, which can be either a plain function with explicit properties or an instance of a class that extends ToolBase.
108
+ * @returns The Toolkit instance with the new tool function registered
109
+ * @param tool
110
+ */
111
+ registerToolFunction(tool) {
112
+ this.tools.push({
113
+ type: "function",
114
+ ...tool
115
+ });
116
+ return this;
117
+ }
118
+ /**
119
+ * Registers functions from a given MCP client.
120
+ *
121
+ * @param root0
122
+ * @param root0.client
123
+ * @param root0.enabledTools
124
+ * @param root0.disabledTools
125
+ * @param root0.requireUserConfirm
126
+ * @returns The Toolkit instance with the new tools registered
127
+ */
128
+ async registerMCPClient({
129
+ client,
130
+ enabledTools,
131
+ disabledTools = [],
132
+ requireUserConfirm = false
133
+ }) {
134
+ const tools = await client.listTools();
135
+ const appendTools = [];
136
+ tools.filter(
137
+ (tool) => !(enabledTools && !enabledTools.includes(tool.name)) && !disabledTools.includes(tool.name)
138
+ ).forEach((tool) => {
139
+ this.tools.push({
140
+ type: "mcp",
141
+ mcpName: client.name,
142
+ ...tool,
143
+ requireUserConfirm
144
+ });
145
+ appendTools.push(tool.name);
146
+ });
147
+ console.log(`Registered tools from MCP client '${client.name}': ${appendTools.join(", ")}`);
148
+ return this;
149
+ }
150
+ /**
151
+ * Executes a registered tool function based on the provided ToolUseBlock.
152
+ * Note this method always returns an AsyncGenerator of ToolResponse, regardless of the tool function type.
153
+ *
154
+ * @param toolCall - The ToolUseBlock containing the tool name and input arguments
155
+ * @yields Incremental ToolResponse objects as they are produced by the tool function
156
+ * @returns The final complete ToolResponse after the tool function execution is finished
157
+ */
158
+ async *callToolFunction(toolCall) {
159
+ const tool = this.tools.find((tool2) => tool2.name === toolCall.name);
160
+ if (!tool) {
161
+ const notFoundRes = createToolResponse({
162
+ content: [
163
+ {
164
+ id: crypto.randomUUID(),
165
+ type: "text",
166
+ text: `FunctionNotFoundError: Cannot find the function named ${toolCall.name}`
167
+ }
168
+ ],
169
+ state: "error"
170
+ });
171
+ yield notFoundRes;
172
+ return notFoundRes;
173
+ }
174
+ let parsedInput;
175
+ try {
176
+ parsedInput = _jsonLoadsWithRepair(toolCall.input);
177
+ if (tool.inputSchema instanceof z.ZodObject) {
178
+ tool.inputSchema.parse(parsedInput);
179
+ } else {
180
+ const validator = new Validator(tool.inputSchema);
181
+ const validation = validator.validate(parsedInput);
182
+ if (!validation.valid) {
183
+ throw new Error(`Invalid input arguments: ${validation.errors}`);
184
+ }
185
+ }
186
+ } catch (error) {
187
+ const parseErrorRes = createToolResponse({
188
+ content: [
189
+ {
190
+ id: crypto.randomUUID(),
191
+ type: "text",
192
+ text: `InvalidArgumentError: ${String(error)}`
193
+ }
194
+ ],
195
+ state: "error"
196
+ });
197
+ yield parseErrorRes;
198
+ return parseErrorRes;
199
+ }
200
+ if (!tool.call) {
201
+ throw new Error(
202
+ `Cannot execute external tool '${toolCall.name}' because no call method is defined for it in the toolkit.`
203
+ );
204
+ }
205
+ let finalRes = null;
206
+ try {
207
+ const res = await tool.call(parsedInput);
208
+ if (typeof res === "string") {
209
+ const textRes = createToolResponse({
210
+ content: [
211
+ {
212
+ id: crypto.randomUUID(),
213
+ type: "text",
214
+ text: res
215
+ }
216
+ ],
217
+ state: "success"
218
+ });
219
+ yield textRes;
220
+ finalRes = textRes;
221
+ } else if (isToolResponse(res)) {
222
+ yield res;
223
+ finalRes = res;
224
+ } else if (Symbol.asyncIterator in res) {
225
+ const accContent = [];
226
+ let nextResult = await res.next();
227
+ while (!nextResult.done) {
228
+ const currentValue = nextResult.value;
229
+ nextResult = await res.next();
230
+ const isLastValue = nextResult.done;
231
+ if (typeof currentValue === "string") {
232
+ const itemRes = createToolResponse({
233
+ content: [
234
+ {
235
+ id: crypto.randomUUID(),
236
+ type: "text",
237
+ text: currentValue
238
+ }
239
+ ],
240
+ isLast: isLastValue,
241
+ state: "running"
242
+ });
243
+ yield itemRes;
244
+ accContent.push({
245
+ id: crypto.randomUUID(),
246
+ type: "text",
247
+ text: currentValue
248
+ });
249
+ } else if (isToolResponse(currentValue)) {
250
+ currentValue.isLast = currentValue.isLast ?? isLastValue;
251
+ yield currentValue;
252
+ accContent.push(...currentValue.content);
253
+ }
254
+ }
255
+ finalRes = createToolResponse({
256
+ content: accContent,
257
+ state: "success"
258
+ });
259
+ } else if (Symbol.iterator in res) {
260
+ const accContent = [];
261
+ let nextResult = res.next();
262
+ while (!nextResult.done) {
263
+ const currentValue = nextResult.value;
264
+ nextResult = res.next();
265
+ const isLastValue = nextResult.done;
266
+ if (typeof currentValue === "string") {
267
+ const itemRes = createToolResponse({
268
+ content: [
269
+ {
270
+ id: crypto.randomUUID(),
271
+ type: "text",
272
+ text: currentValue
273
+ }
274
+ ],
275
+ isLast: isLastValue,
276
+ state: "running"
277
+ });
278
+ yield itemRes;
279
+ accContent.push({
280
+ id: crypto.randomUUID(),
281
+ type: "text",
282
+ text: currentValue
283
+ });
284
+ } else if (isToolResponse(currentValue)) {
285
+ currentValue.isLast = currentValue.isLast ?? isLastValue;
286
+ yield currentValue;
287
+ accContent.push(...currentValue.content);
288
+ }
289
+ }
290
+ finalRes = createToolResponse({
291
+ content: accContent,
292
+ state: "success"
293
+ });
294
+ } else {
295
+ const invalidRes = createToolResponse({
296
+ content: [
297
+ {
298
+ id: crypto.randomUUID(),
299
+ type: "text",
300
+ text: String(res)
301
+ }
302
+ ],
303
+ state: "running"
304
+ });
305
+ yield invalidRes;
306
+ finalRes = invalidRes;
307
+ }
308
+ } catch (error) {
309
+ const errorRes = createToolResponse({
310
+ content: [
311
+ {
312
+ id: crypto.randomUUID(),
313
+ type: "text",
314
+ text: `ToolExecutionError: ${String(error)}`
315
+ }
316
+ ],
317
+ state: "error"
318
+ });
319
+ yield errorRes;
320
+ finalRes = errorRes;
321
+ }
322
+ if (!finalRes) {
323
+ return createToolResponse({
324
+ content: [
325
+ {
326
+ id: crypto.randomUUID(),
327
+ type: "text",
328
+ text: `Tool ${toolCall.name} executed successfully.`
329
+ }
330
+ ],
331
+ state: "success"
332
+ });
333
+ }
334
+ const cleanedContent = [];
335
+ let textBuffer = "";
336
+ for (const block of finalRes.content) {
337
+ if (block.type === "text") {
338
+ textBuffer += block.text;
339
+ } else {
340
+ if (textBuffer) {
341
+ cleanedContent.push({
342
+ id: crypto.randomUUID(),
343
+ type: "text",
344
+ text: textBuffer
345
+ });
346
+ textBuffer = "";
347
+ }
348
+ cleanedContent.push(block);
349
+ }
350
+ }
351
+ if (textBuffer) {
352
+ cleanedContent.push({
353
+ id: crypto.randomUUID(),
354
+ type: "text",
355
+ text: textBuffer
356
+ });
357
+ }
358
+ return {
359
+ ...finalRes,
360
+ content: cleanedContent
361
+ };
362
+ }
363
+ /**
364
+ * Returns the JSON schemas for all registered tools in a format compatible with LLM APIs.
365
+ *
366
+ * @returns An array of ToolJSONSchema objects
367
+ */
368
+ getJSONSchemas() {
369
+ return this.tools.map((tool) => {
370
+ const inputSchema = tool.inputSchema instanceof z.ZodObject ? tool.inputSchema.toJSONSchema({ target: "openapi-3.0" }) : tool.inputSchema;
371
+ return {
372
+ type: "function",
373
+ function: {
374
+ name: tool.name,
375
+ description: tool.description,
376
+ parameters: inputSchema
377
+ }
378
+ };
379
+ });
380
+ }
381
+ /**
382
+ * Get the instruction prompt for the agent to use the skills.
383
+ *
384
+ * @returns A string containing the instruction prompt of the available skills and how to use them.
385
+ */
386
+ getSkillsPrompt() {
387
+ this._skillCache = {};
388
+ if (this.skills.length === 0 && this.skillDirs.length === 0) return "";
389
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
390
+ const skillsInfo = [];
391
+ this.skills.forEach((skillPath) => {
392
+ const absSkillPath = path.resolve(skillPath);
393
+ if (!fs.existsSync(absSkillPath) || !fs.statSync(absSkillPath).isDirectory()) {
394
+ return;
395
+ }
396
+ const skillMdPath = path.join(absSkillPath, "SKILL.md");
397
+ if (!fs.existsSync(skillMdPath)) return;
398
+ try {
399
+ const content = fs.readFileSync(skillMdPath, "utf-8");
400
+ const { data } = matter(content);
401
+ const name = data.name || path.basename(skillPath);
402
+ const description = data.description || "No description provided";
403
+ skillsInfo.push({
404
+ name,
405
+ description,
406
+ location: absSkillPath
407
+ });
408
+ this._skillCache[name] = absSkillPath;
409
+ } catch (e) {
410
+ console.error(`Error reading SKILL.md for skill at ${skillPath}:`, e);
411
+ }
412
+ });
413
+ this.skillDirs.forEach((skillDir) => {
414
+ const absSkillDir = path.resolve(skillDir);
415
+ if (!fs.existsSync(absSkillDir) || !fs.statSync(absSkillDir).isDirectory()) {
416
+ return;
417
+ }
418
+ const subdirs = fs.readdirSync(absSkillDir).filter((subdir) => {
419
+ const subdirPath = path.join(absSkillDir, subdir);
420
+ return fs.statSync(subdirPath).isDirectory();
421
+ });
422
+ subdirs.forEach((subdir) => {
423
+ const skillMdPath = path.join(absSkillDir, subdir, "SKILL.md");
424
+ if (!fs.existsSync(skillMdPath)) return;
425
+ try {
426
+ const content = fs.readFileSync(skillMdPath, "utf-8");
427
+ const { data } = matter(content);
428
+ const name = data.name || subdir;
429
+ const description = data.description || "No description provided";
430
+ skillsInfo.push({
431
+ name,
432
+ description,
433
+ location: path.join(skillDir, subdir)
434
+ });
435
+ this._skillCache[name] = path.join(absSkillDir, subdir);
436
+ } catch (e) {
437
+ console.error(
438
+ `Error reading SKILL.md for skill at ${path.join(skillDir, subdir)}:`,
439
+ e
440
+ );
441
+ }
442
+ });
443
+ });
444
+ if (skillsInfo.length === 0) return "";
445
+ const skillsXml = skillsInfo.map(
446
+ (skill) => `<skill>
447
+ <name>${skill.name}</name>
448
+ <description>${skill.description}</description>
449
+ <location>${skill.location}</location>
450
+ </skill>`
451
+ ).reduce((acc, skillInfo) => acc + `
452
+ ${skillInfo}
453
+ `, "");
454
+ return `<skills-system>
455
+ ## What are Skills?
456
+ Skills are packages of domain expertise that extend your capabilities.
457
+
458
+ ## Important: How to Use Skills
459
+ **Skill names are NOT callable functions.** You cannot call a skill directly by its name.
460
+ ${skillsXml}
461
+ </skills-system>`;
462
+ }
463
+ return "";
464
+ }
465
+ /**
466
+ * The agent skill tool to read SKILL.md file content based on the skill name.
467
+ * @param root0
468
+ * @param root0.name
469
+ * @returns The content of the SKILL.md file for the specified skill, or an error message if the skill is not
470
+ * found or the SKILL.md file cannot be read.
471
+ */
472
+ async _skillTool({ name }) {
473
+ if (this._skillCache[name]) {
474
+ const skillDir = this._skillCache[name];
475
+ const skillMdPath = path.join(skillDir, "SKILL.md");
476
+ if (!fs.existsSync(skillMdPath)) {
477
+ try {
478
+ const fileContent = fs.readFileSync(skillMdPath, "utf-8");
479
+ return createToolResponse({
480
+ content: [
481
+ {
482
+ id: crypto.randomUUID(),
483
+ type: "text",
484
+ text: fileContent
485
+ }
486
+ ],
487
+ state: "success"
488
+ });
489
+ } catch {
490
+ }
491
+ }
492
+ }
493
+ this.getSkillsPrompt();
494
+ const refreshedSkillDir = this._skillCache[name];
495
+ if (refreshedSkillDir) {
496
+ const skillMdPath = path.join(refreshedSkillDir, "SKILL.md");
497
+ try {
498
+ const fileContent = fs.readFileSync(skillMdPath, "utf-8");
499
+ return createToolResponse({
500
+ content: [
501
+ {
502
+ id: crypto.randomUUID(),
503
+ type: "text",
504
+ text: fileContent
505
+ }
506
+ ],
507
+ state: "success"
508
+ });
509
+ } catch {
510
+ }
511
+ }
512
+ return createToolResponse({
513
+ content: [
514
+ {
515
+ id: crypto.randomUUID(),
516
+ type: "text",
517
+ text: `SkillNotFoundError: Cannot find the skill named ${name}, current available skills are ${Object.keys(this._skillCache).join(", ")}`
518
+ }
519
+ ],
520
+ state: "error"
521
+ });
522
+ }
523
+ /**
524
+ * Checks if a tool requires user confirmation before execution based on its name.
525
+ * @param toolName The name of the tool to check for user confirmation requirement.
526
+ * @returns A boolean indicating whether the specified tool requires user confirmation before execution. If the tool is not found, it returns false.
527
+ */
528
+ requireUserConfirm(toolName) {
529
+ const tool = this.tools.find((tool2) => tool2.name === toolName);
530
+ return tool ? tool.requireUserConfirm ?? false : false;
531
+ }
532
+ /**
533
+ * Checks if a tool requires external execution (e.g., by an MCP client) based on its name.
534
+ * @param toolName
535
+ * @returns A boolean indicating whether the specified tool requires external execution. If the tool is not found, it returns false.
536
+ */
537
+ requireExternalExecution(toolName) {
538
+ const tool = this.tools.find((tool2) => tool2.name === toolName);
539
+ return tool ? !tool.call : false;
540
+ }
541
+ };
542
+
543
+ // src/tool/bash.ts
544
+ import { exec } from "child_process";
545
+ import { promisify } from "util";
546
+ import { z as z2 } from "zod";
547
+ var execAsync = promisify(exec);
548
+ function Bash() {
549
+ return {
550
+ name: "Bash",
551
+ description: `Executes a given bash command and returns its output.
552
+
553
+ The working directory persists between commands, but shell state does not. The shell environment is initialized from the user's profile (bash or zsh).
554
+
555
+ IMPORTANT: Avoid using this tool to run \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands, unless explicitly instructed or after you have verified that a dedicated tool cannot accomplish your task. Instead, use the appropriate dedicated tool as this will provide a much better experience for the user:
556
+
557
+ - File search: Use Glob (NOT find or ls)
558
+ - Content search: Use Grep (NOT grep or rg)
559
+ - Read files: Use Read (NOT cat/head/tail)
560
+ - Edit files: Use Edit (NOT sed/awk)
561
+ - Write files: Use Write (NOT echo >/cat <<EOF)
562
+ - Communication: Output text directly (NOT echo/printf)
563
+
564
+ While the Bash tool can do similar things, it's better to use the built-in tools as they provide a better user experience and make it easier to review tool calls and give permission.
565
+
566
+ # Instructions
567
+ - If your command will create new directories or files, first use this tool to run \`ls\` to verify the parent directory exists and is the correct location.
568
+ - Always quote file paths that contain spaces with double quotes in your command (e.g., cd "path with spaces/file.txt")
569
+ - Try to maintain your current working directory throughout the session by using absolute paths and avoiding usage of \`cd\`. You may use \`cd\` if the User explicitly requests it.
570
+ - You may specify an optional timeout in milliseconds (up to 600000ms / 10 minutes). By default, your command will timeout after 120000ms (2 minutes).
571
+ - Write a clear, concise description of what your command does. For simple commands, keep it brief (5-10 words). For complex commands (piped commands, obscure flags, or anything hard to understand at a glance), include enough context so that the user can understand what your command will do.
572
+ - When issuing multiple commands:
573
+ - If the commands are independent and can run in parallel, make multiple Bash tool calls in a single message. Example: if you need to run "git status" and "git diff", send a single message with two Bash tool calls in parallel.
574
+ - If the commands depend on each other and must run sequentially, use a single Bash call with '&&' to chain them together.
575
+ - Use ';' only when you need to run commands sequentially but don't care if earlier commands fail.
576
+ - DO NOT use newlines to separate commands (newlines are ok in quoted strings).
577
+ - For git commands:
578
+ - Prefer to create a new commit rather than amending an existing commit.
579
+ - Before running destructive operations (e.g., git reset --hard, git push --force, git checkout --), consider whether there is a safer alternative that achieves the same goal. Only use destructive operations when they are truly the best approach.
580
+ - Never skip hooks (--no-verify) or bypass signing (--no-gpg-sign, -c commit.gpgsign=false) unless the user has explicitly asked for it. If a hook fails, investigate and fix the underlying issue.
581
+ - Avoid unnecessary \`sleep\` commands:
582
+ - Do not sleep between commands that can run immediately \u2014 just run them.
583
+ - Do not retry failing commands in a sleep loop \u2014 diagnose the root cause or consider an alternative approach.
584
+ - If you must sleep, keep the duration short (1-5 seconds) to avoid blocking the user.`,
585
+ inputSchema: z2.object({
586
+ command: z2.string().describe("The bash command to execute"),
587
+ description: z2.string().optional().describe(
588
+ "Clear, concise description of what this command does. For simple commands, keep it brief (5-10 words). For complex commands, include enough context."
589
+ ),
590
+ timeout: z2.number().int().min(0).max(6e5).optional().describe("Optional timeout in milliseconds (default: 120000, max: 600000)")
591
+ }),
592
+ requireUserConfirm: true,
593
+ /**
594
+ * Executes a bash command and returns its output.
595
+ *
596
+ * @param root0 - The parameters object
597
+ * @param root0.command - The bash command to execute
598
+ * @param root0.description - Optional description of what the command does
599
+ * @param root0.timeout - Optional timeout in milliseconds (default: 120000, max: 600000)
600
+ * @returns The stdout of the command, or an error message if the command fails
601
+ */
602
+ async call({
603
+ command,
604
+ description: _description,
605
+ timeout = 12e4
606
+ }) {
607
+ try {
608
+ const maxTimeout = 6e5;
609
+ const effectiveTimeout = Math.min(timeout, maxTimeout);
610
+ let shell;
611
+ if (process.platform === "win32") {
612
+ shell = process.env.COMSPEC || "cmd.exe";
613
+ } else {
614
+ shell = process.env.SHELL || "/bin/bash";
615
+ }
616
+ const { stdout } = await execAsync(command, {
617
+ encoding: "utf-8",
618
+ timeout: effectiveTimeout,
619
+ maxBuffer: 3e4 * 1024,
620
+ shell
621
+ });
622
+ const normalizedOutput = stdout.replace(/\r\n/g, "\n");
623
+ const maxOutputLength = 3e4;
624
+ if (normalizedOutput.length > maxOutputLength) {
625
+ return createToolResponse({
626
+ content: [
627
+ {
628
+ id: crypto.randomUUID(),
629
+ type: "text",
630
+ text: normalizedOutput.substring(0, maxOutputLength) + "\n\n[Output truncated - exceeded 30000 characters]"
631
+ }
632
+ ],
633
+ state: "success"
634
+ });
635
+ }
636
+ return createToolResponse({
637
+ content: [{ id: crypto.randomUUID(), type: "text", text: normalizedOutput }],
638
+ state: "success"
639
+ });
640
+ } catch (error) {
641
+ const errorMessage = error.message || "Unknown error";
642
+ const stderr = error.stderr?.toString().replace(/\r\n/g, "\n") || "";
643
+ const stdout = error.stdout?.toString().replace(/\r\n/g, "\n") || "";
644
+ let result = `Command failed: ${command}
645
+ `;
646
+ if (stdout) result += `
647
+ Stdout:
648
+ ${stdout}`;
649
+ if (stderr) result += `
650
+ Stderr:
651
+ ${stderr}`;
652
+ if (errorMessage && !stderr) result += `
653
+ Error: ${errorMessage}`;
654
+ return createToolResponse({
655
+ content: [{ id: crypto.randomUUID(), type: "text", text: result }],
656
+ state: "error"
657
+ });
658
+ }
659
+ }
660
+ };
661
+ }
662
+
663
+ // src/tool/read.ts
664
+ import * as fs2 from "fs";
665
+ import * as path2 from "path";
666
+ import { z as z3 } from "zod";
667
+ function Read() {
668
+ return {
669
+ name: "Read",
670
+ description: `Reads a file from the local filesystem. You can access any file directly by using this tool.
671
+ Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
672
+
673
+ Usage:
674
+ - The file_path parameter must be an absolute path, not a relative path
675
+ - By default, it reads up to 2000 lines starting from the beginning of the file
676
+ - You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
677
+ - Any lines longer than 2000 characters will be truncated
678
+ - Results are returned using cat -n format, with line numbers starting at 1
679
+ - This tool can only read files, not directories. To read a directory, use an ls command via the Bash tool.
680
+ - You can call multiple tools in a single response. It is always better to speculatively read multiple potentially useful files in parallel.
681
+ - If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents.`,
682
+ inputSchema: z3.object({
683
+ file_path: z3.string().describe("The absolute path to the file to read"),
684
+ offset: z3.number().int().positive().optional().describe(
685
+ "The line number to start reading from. Only provide if the file is too large to read at once"
686
+ ),
687
+ limit: z3.number().int().positive().optional().describe(
688
+ "The number of lines to read. Only provide if the file is too large to read at once"
689
+ )
690
+ }),
691
+ requireUserConfirm: true,
692
+ /**
693
+ * Reads a file and returns its contents with line numbers.
694
+ *
695
+ * @param root0 - The parameters object
696
+ * @param root0.file_path - Absolute path to the file to read
697
+ * @param root0.offset - Line number to start reading from (1-based)
698
+ * @param root0.limit - Maximum number of lines to read (capped at 2000)
699
+ * @returns The file contents formatted with line numbers, or a warning if the file is empty
700
+ * @throws If the path is not absolute, the file does not exist, or the path is a directory
701
+ */
702
+ call({
703
+ file_path,
704
+ offset,
705
+ limit
706
+ }) {
707
+ if (!path2.isAbsolute(file_path)) {
708
+ throw new Error(`file_path must be an absolute path, got: ${file_path}`);
709
+ }
710
+ if (!fs2.existsSync(file_path)) {
711
+ throw new Error(`File not found: ${file_path}`);
712
+ }
713
+ const stat = fs2.statSync(file_path);
714
+ if (stat.isDirectory()) {
715
+ throw new Error(
716
+ `${file_path} is a directory, not a file. Use Bash with ls to read directories.`
717
+ );
718
+ }
719
+ const rawContent = fs2.readFileSync(file_path, "utf-8");
720
+ if (rawContent.length === 0) {
721
+ return createToolResponse({
722
+ content: [{ id: crypto.randomUUID(), type: "text", text: rawContent }],
723
+ state: "success"
724
+ });
725
+ }
726
+ const allLines = rawContent.split("\n");
727
+ const startLine = offset !== void 0 ? offset - 1 : 0;
728
+ const maxLines = 2e3;
729
+ const effectiveLimit = limit !== void 0 ? Math.min(limit, maxLines) : maxLines;
730
+ const selectedLines = allLines.slice(startLine, startLine + effectiveLimit);
731
+ const maxLineLength = 2e3;
732
+ const formatted = selectedLines.map((line, i) => {
733
+ const lineNum = startLine + i + 1;
734
+ const truncated = line.length > maxLineLength ? line.substring(0, maxLineLength) + "[truncated]" : line;
735
+ return `${String(lineNum).padStart(6)} ${truncated}`;
736
+ }).join("\n");
737
+ return createToolResponse({
738
+ content: [{ id: crypto.randomUUID(), type: "text", text: formatted }],
739
+ state: "success"
740
+ });
741
+ }
742
+ };
743
+ }
744
+
745
+ // src/tool/write.ts
746
+ import * as fs3 from "fs";
747
+ import * as path3 from "path";
748
+ import { z as z4 } from "zod";
749
+ function Write() {
750
+ return {
751
+ name: "Write",
752
+ description: `Writes a file to the local filesystem.
753
+
754
+ Usage:
755
+ - This tool will overwrite the existing file if there is one at the provided path.
756
+ - If this is an existing file, you MUST use the Read tool first to read the file's contents. This tool will fail if you did not read the file first.
757
+ - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
758
+ - NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
759
+ - Only use emojis if the user explicitly requests it. Avoid writing emojis to files unless asked.`,
760
+ inputSchema: z4.object({
761
+ file_path: z4.string().describe(
762
+ "The absolute path to the file to write (must be absolute, not relative)"
763
+ ),
764
+ content: z4.string().describe("The content to write to the file")
765
+ }),
766
+ requireUserConfirm: true,
767
+ /**
768
+ * Writes content to a file, creating parent directories if necessary.
769
+ *
770
+ * @param root0 - The parameters object
771
+ * @param root0.file_path - Absolute path to the file to write
772
+ * @param root0.content - The content to write to the file
773
+ * @returns A success message including the number of lines written
774
+ * @throws If the path is not absolute
775
+ */
776
+ call({ file_path, content }) {
777
+ if (!path3.isAbsolute(file_path)) {
778
+ throw new Error(`file_path must be an absolute path, got: ${file_path}`);
779
+ }
780
+ const dir = path3.dirname(file_path);
781
+ if (!fs3.existsSync(dir)) {
782
+ fs3.mkdirSync(dir, { recursive: true });
783
+ }
784
+ fs3.writeFileSync(file_path, content, "utf-8");
785
+ const lineCount = content.split("\n").length;
786
+ return `The file ${file_path} has been written successfully (${lineCount} lines).`;
787
+ }
788
+ };
789
+ }
790
+
791
+ // src/tool/edit.ts
792
+ import * as fs4 from "fs";
793
+ import * as path4 from "path";
794
+ import { z as z5 } from "zod";
795
+ function Edit() {
796
+ return {
797
+ name: "Edit",
798
+ description: `Performs exact string replacements in files.
799
+
800
+ Usage:
801
+ - You must use your Read tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file.
802
+ - When editing text from Read tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.
803
+ - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.
804
+ - Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.
805
+ - The edit will FAIL if \`old_string\` is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use \`replace_all\` to change every instance of \`old_string\`.
806
+ - Use \`replace_all\` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.`,
807
+ inputSchema: z5.object({
808
+ file_path: z5.string().describe("The absolute path to the file to modify"),
809
+ old_string: z5.string().describe("The text to replace"),
810
+ new_string: z5.string().describe("The text to replace it with (must be different from old_string)"),
811
+ replace_all: z5.boolean().optional().default(false).describe("Replace all occurrences of old_string (default false)")
812
+ }),
813
+ requireUserConfirm: true,
814
+ /**
815
+ * Performs an exact string replacement in the specified file.
816
+ *
817
+ * @param root0 - The parameters object
818
+ * @param root0.file_path - Absolute path to the file to modify
819
+ * @param root0.old_string - The exact text to find and replace
820
+ * @param root0.new_string - The text to replace old_string with
821
+ * @param root0.replace_all - If true, replaces all occurrences; otherwise only the first
822
+ * @returns A success message indicating the file was updated
823
+ * @throws If the path is not absolute, file does not exist, strings are identical,
824
+ * old_string is not found, or old_string is not unique and replace_all is false
825
+ */
826
+ call({
827
+ file_path,
828
+ old_string,
829
+ new_string,
830
+ replace_all = false
831
+ }) {
832
+ if (!path4.isAbsolute(file_path)) {
833
+ throw new Error(`file_path must be an absolute path, got: ${file_path}`);
834
+ }
835
+ if (!fs4.existsSync(file_path)) {
836
+ throw new Error(`File not found: ${file_path}`);
837
+ }
838
+ if (old_string === new_string) {
839
+ throw new Error("old_string and new_string must be different");
840
+ }
841
+ const content = fs4.readFileSync(file_path, "utf-8");
842
+ const occurrences = content.split(old_string).length - 1;
843
+ if (occurrences === 0) {
844
+ throw new Error(`old_string not found in file: ${file_path}`);
845
+ }
846
+ if (!replace_all && occurrences > 1) {
847
+ throw new Error(
848
+ `old_string is not unique in the file (found ${occurrences} occurrences). Provide more surrounding context to make it unique, or use replace_all=true.`
849
+ );
850
+ }
851
+ const newContent = replace_all ? content.split(old_string).join(new_string) : content.replace(old_string, new_string);
852
+ fs4.writeFileSync(file_path, newContent, "utf-8");
853
+ return `The file ${file_path} has been updated successfully.`;
854
+ }
855
+ };
856
+ }
857
+
858
+ // src/tool/glob.ts
859
+ import * as fs5 from "fs";
860
+ import * as path5 from "path";
861
+ import { z as z6 } from "zod";
862
+ function Glob() {
863
+ const globMatch = (pattern, baseDir) => {
864
+ const results = [];
865
+ const parts = pattern.split("/");
866
+ matchParts(parts, 0, baseDir, results);
867
+ return results;
868
+ };
869
+ const matchParts = (parts, partIndex, currentDir, results) => {
870
+ if (partIndex >= parts.length) return;
871
+ const part = parts[partIndex];
872
+ const isLast = partIndex === parts.length - 1;
873
+ if (part === "**") {
874
+ if (isLast) {
875
+ collectAll(currentDir, results);
876
+ } else {
877
+ matchParts(parts, partIndex + 1, currentDir, results);
878
+ try {
879
+ const entries = fs5.readdirSync(currentDir, { withFileTypes: true });
880
+ for (const entry of entries) {
881
+ if (entry.isDirectory()) {
882
+ const subDir = path5.join(currentDir, entry.name);
883
+ matchParts(parts, partIndex, subDir, results);
884
+ }
885
+ }
886
+ } catch {
887
+ }
888
+ }
889
+ } else {
890
+ const regex = globPartToRegex(part);
891
+ try {
892
+ const entries = fs5.readdirSync(currentDir, { withFileTypes: true });
893
+ for (const entry of entries) {
894
+ if (regex.test(entry.name)) {
895
+ const fullPath = path5.join(currentDir, entry.name);
896
+ if (isLast) {
897
+ if (entry.isFile()) results.push(fullPath);
898
+ } else if (entry.isDirectory()) {
899
+ matchParts(parts, partIndex + 1, fullPath, results);
900
+ }
901
+ }
902
+ }
903
+ } catch {
904
+ }
905
+ }
906
+ };
907
+ const collectAll = (dir, results) => {
908
+ try {
909
+ const entries = fs5.readdirSync(dir, { withFileTypes: true });
910
+ for (const entry of entries) {
911
+ const fullPath = path5.join(dir, entry.name);
912
+ if (entry.isFile()) {
913
+ results.push(fullPath);
914
+ } else if (entry.isDirectory()) {
915
+ collectAll(fullPath, results);
916
+ }
917
+ }
918
+ } catch {
919
+ }
920
+ };
921
+ const globPartToRegex = (part) => {
922
+ const escaped = part.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
923
+ return new RegExp(`^${escaped}$`);
924
+ };
925
+ return {
926
+ name: "Glob",
927
+ description: `Fast file pattern matching tool that works with any codebase size.
928
+ - Supports glob patterns like "**/*.js" or "src/**/*.ts"
929
+ - Returns matching file paths sorted by modification time
930
+ - Use this tool when you need to find files by name patterns
931
+ - When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead
932
+ - You can call multiple tools in a single response. It is always better to speculatively perform multiple searches in parallel if they are potentially useful.`,
933
+ inputSchema: z6.object({
934
+ pattern: z6.string().describe("The glob pattern to match files against"),
935
+ path: z6.string().optional().describe(
936
+ 'The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.'
937
+ )
938
+ }),
939
+ requireUserConfirm: true,
940
+ /**
941
+ * Finds files matching a glob pattern under the given directory.
942
+ *
943
+ * @param root0 - The parameters object
944
+ * @param root0.pattern - The glob pattern to match files against
945
+ * @param root0.path - The base directory to search in; defaults to cwd
946
+ * @returns A newline-separated list of matching file paths sorted by modification time,
947
+ * or a no-matches message if nothing is found
948
+ * @throws If the base directory does not exist
949
+ */
950
+ call({ pattern, path: searchPath }) {
951
+ const baseDir = searchPath ? searchPath : process.cwd();
952
+ if (!fs5.existsSync(baseDir)) {
953
+ throw new Error(`Directory not found: ${baseDir}`);
954
+ }
955
+ const matches = globMatch(pattern, baseDir);
956
+ matches.sort((a, b) => {
957
+ const statA = fs5.statSync(a);
958
+ const statB = fs5.statSync(b);
959
+ return statB.mtimeMs - statA.mtimeMs;
960
+ });
961
+ if (matches.length === 0) {
962
+ return `No files found matching pattern: ${pattern}`;
963
+ }
964
+ return matches.join("\n");
965
+ }
966
+ };
967
+ }
968
+
969
+ // src/tool/grep.ts
970
+ import * as fs6 from "fs";
971
+ import * as path6 from "path";
972
+ import { z as z7 } from "zod";
973
+ var TYPE_EXTENSIONS = {
974
+ js: [".js", ".mjs", ".cjs"],
975
+ ts: [".ts", ".mts", ".cts"],
976
+ tsx: [".tsx"],
977
+ jsx: [".jsx"],
978
+ py: [".py"],
979
+ rust: [".rs"],
980
+ go: [".go"],
981
+ java: [".java"],
982
+ cpp: [".cpp", ".cc", ".cxx", ".h", ".hpp"],
983
+ c: [".c", ".h"],
984
+ css: [".css"],
985
+ html: [".html", ".htm"],
986
+ json: [".json"],
987
+ md: [".md", ".markdown"],
988
+ yaml: [".yaml", ".yml"],
989
+ toml: [".toml"],
990
+ sh: [".sh", ".bash"]
991
+ };
992
+ function Grep() {
993
+ const collectFiles = (baseDir, glob, type) => {
994
+ const results = [];
995
+ const extensions = type ? TYPE_EXTENSIONS[type] : void 0;
996
+ const walk = (dir) => {
997
+ let entries;
998
+ try {
999
+ entries = fs6.readdirSync(dir, { withFileTypes: true });
1000
+ } catch {
1001
+ return;
1002
+ }
1003
+ for (const entry of entries) {
1004
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
1005
+ const fullPath = path6.join(dir, entry.name);
1006
+ if (entry.isDirectory()) {
1007
+ walk(fullPath);
1008
+ } else if (entry.isFile()) {
1009
+ if (extensions) {
1010
+ const ext = path6.extname(entry.name);
1011
+ if (extensions.includes(ext)) results.push(fullPath);
1012
+ } else if (glob) {
1013
+ if (matchGlob(glob, entry.name)) results.push(fullPath);
1014
+ } else {
1015
+ results.push(fullPath);
1016
+ }
1017
+ }
1018
+ }
1019
+ };
1020
+ if (fs6.existsSync(baseDir) && fs6.statSync(baseDir).isFile()) {
1021
+ results.push(baseDir);
1022
+ } else {
1023
+ walk(baseDir);
1024
+ }
1025
+ return results;
1026
+ };
1027
+ const matchGlob = (pattern, filename) => {
1028
+ const braceMatch = pattern.match(/\*\.\\{(.+)\\}/);
1029
+ if (braceMatch) {
1030
+ const exts = braceMatch[1].split(",").map((e) => `.${e.trim()}`);
1031
+ return exts.includes(path6.extname(filename));
1032
+ }
1033
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
1034
+ return new RegExp(`^${escaped}$`).test(filename);
1035
+ };
1036
+ return {
1037
+ name: "Grep",
1038
+ description: `A powerful search tool built on regular expressions.
1039
+
1040
+ Usage:
1041
+ - ALWAYS use Grep for search tasks. NEVER invoke \`grep\` or \`rg\` as a Bash command. The Grep tool has been optimized for correct permissions and access.
1042
+ - Supports full regex syntax (e.g., "log.*Error", "function\\s+\\w+")
1043
+ - Filter files with glob parameter (e.g., "*.js", "**/*.tsx") or type parameter (e.g., "js", "py", "rust")
1044
+ - Output modes: "content" shows matching lines, "files_with_matches" shows only file paths (default), "count" shows match counts
1045
+ - Use Task tool for open-ended searches requiring multiple rounds
1046
+ - Pattern syntax: Uses ripgrep-style regex - literal braces need escaping (use \`interface\\{\\}\` to find \`interface{}\` in Go code)
1047
+ - Multiline matching: By default patterns match within single lines only. For cross-line patterns, use \`multiline: true\``,
1048
+ inputSchema: z7.object({
1049
+ pattern: z7.string().describe("The regular expression pattern to search for in file contents"),
1050
+ path: z7.string().optional().describe("File or directory to search in. Defaults to current working directory."),
1051
+ glob: z7.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}")'),
1052
+ type: z7.string().optional().describe(
1053
+ 'File type to search (e.g., "js", "ts", "py"). More efficient than glob for standard file types.'
1054
+ ),
1055
+ output_mode: z7.enum(["content", "files_with_matches", "count"]).optional().describe(
1056
+ 'Output mode: "content" | "files_with_matches" | "count". Defaults to "files_with_matches".'
1057
+ ),
1058
+ multiline: z7.boolean().optional().describe(
1059
+ "Enable multiline mode where . matches newlines and patterns can span lines. Default: false."
1060
+ ),
1061
+ case_insensitive: z7.boolean().optional().describe("Case insensitive search. Default: false."),
1062
+ context: z7.number().int().optional().describe(
1063
+ 'Number of lines to show before and after each match. Requires output_mode: "content".'
1064
+ ),
1065
+ head_limit: z7.number().int().optional().describe("Limit output to first N lines/entries.")
1066
+ }),
1067
+ requireUserConfirm: true,
1068
+ /**
1069
+ * Searches files for a regex pattern and returns results in the specified output mode.
1070
+ *
1071
+ * @param root0 - The parameters object
1072
+ * @param root0.pattern - The regular expression pattern to search for
1073
+ * @param root0.path - File or directory to search in; defaults to cwd
1074
+ * @param root0.glob - Glob pattern to filter which files are searched
1075
+ * @param root0.type - File type shorthand (e.g. "ts", "py") to filter files
1076
+ * @param root0.output_mode - How to format results: "content", "files_with_matches", or "count"
1077
+ * @param root0.multiline - Whether the pattern can span multiple lines
1078
+ * @param root0.case_insensitive - Whether the search is case-insensitive
1079
+ * @param root0.context - Number of surrounding lines to include with each match
1080
+ * @param root0.head_limit - Maximum number of result entries to return
1081
+ * @returns A newline-separated string of results, or a no-matches message
1082
+ */
1083
+ call({
1084
+ pattern,
1085
+ path: searchPath,
1086
+ glob,
1087
+ type,
1088
+ output_mode = "files_with_matches",
1089
+ multiline = false,
1090
+ case_insensitive = false,
1091
+ context,
1092
+ head_limit
1093
+ }) {
1094
+ const baseDir = searchPath ? searchPath : process.cwd();
1095
+ let flags = multiline ? "gms" : "gm";
1096
+ if (case_insensitive) flags += "i";
1097
+ const regex = new RegExp(pattern, flags);
1098
+ const files = collectFiles(baseDir, glob, type);
1099
+ if (files.length === 0) {
1100
+ return "No files found to search.";
1101
+ }
1102
+ const results = [];
1103
+ for (const file of files) {
1104
+ try {
1105
+ const content = fs6.readFileSync(file, "utf-8");
1106
+ if (output_mode === "files_with_matches") {
1107
+ if (regex.test(content)) {
1108
+ results.push(file);
1109
+ }
1110
+ regex.lastIndex = 0;
1111
+ } else if (output_mode === "count") {
1112
+ const matches = content.match(regex);
1113
+ if (matches) {
1114
+ results.push(`${file}: ${matches.length}`);
1115
+ }
1116
+ regex.lastIndex = 0;
1117
+ } else if (output_mode === "content") {
1118
+ const lines = content.split("\n");
1119
+ for (let i = 0; i < lines.length; i++) {
1120
+ const lineRegex = new RegExp(pattern, case_insensitive ? "i" : "");
1121
+ if (lineRegex.test(lines[i])) {
1122
+ const start = context !== void 0 ? Math.max(0, i - context) : i;
1123
+ const end = context !== void 0 ? Math.min(lines.length - 1, i + context) : i;
1124
+ for (let j = start; j <= end; j++) {
1125
+ results.push(`${file}:${j + 1}:${lines[j]}`);
1126
+ }
1127
+ }
1128
+ }
1129
+ }
1130
+ } catch {
1131
+ }
1132
+ }
1133
+ if (results.length === 0) {
1134
+ return `No matches found for pattern: ${pattern}`;
1135
+ }
1136
+ const output = head_limit !== void 0 ? results.slice(0, head_limit) : results;
1137
+ return output.join("\n");
1138
+ }
1139
+ };
1140
+ }
1141
+
1142
+ // src/tool/task.ts
1143
+ import { z as z8 } from "zod";
1144
+ var taskStore = /* @__PURE__ */ new Map();
1145
+ var nextId = 1;
1146
+ function generateId() {
1147
+ return String(nextId++);
1148
+ }
1149
+ function TaskCreate() {
1150
+ return {
1151
+ name: "TaskCreate",
1152
+ description: `Use this tool to create a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
1153
+ It also helps the user understand the progress of the task and overall progress of their requests.
1154
+
1155
+ ## When to Use This Tool
1156
+
1157
+ Use this tool proactively in these scenarios:
1158
+
1159
+ - Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
1160
+ - Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
1161
+ - User explicitly requests todo list - When the user directly asks you to use the todo list
1162
+ - User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
1163
+
1164
+ All tasks are created with status 'pending'.`,
1165
+ inputSchema: z8.object({
1166
+ subject: z8.string().describe(
1167
+ 'A brief, actionable title in imperative form (e.g., "Fix authentication bug in login flow")'
1168
+ ),
1169
+ description: z8.string().describe(
1170
+ "Detailed description of what needs to be done, including context and acceptance criteria"
1171
+ ),
1172
+ activeForm: z8.string().optional().describe(
1173
+ 'Present continuous form shown in the spinner when the task is in_progress (e.g., "Fixing authentication bug"). If omitted, the spinner shows the subject instead.'
1174
+ ),
1175
+ metadata: z8.record(z8.string(), z8.unknown()).optional().describe("Arbitrary metadata to attach to the task")
1176
+ }),
1177
+ requireUserConfirm: false,
1178
+ call({
1179
+ subject,
1180
+ description,
1181
+ activeForm,
1182
+ metadata
1183
+ }) {
1184
+ const id = generateId();
1185
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1186
+ const task = {
1187
+ id,
1188
+ subject,
1189
+ description,
1190
+ status: "pending",
1191
+ activeForm,
1192
+ metadata,
1193
+ blocks: [],
1194
+ blockedBy: [],
1195
+ createdAt: now,
1196
+ updatedAt: now
1197
+ };
1198
+ taskStore.set(id, task);
1199
+ return createToolResponse({
1200
+ content: [
1201
+ {
1202
+ id: crypto.randomUUID(),
1203
+ type: "text",
1204
+ text: `Task #${id} created successfully: ${subject}`
1205
+ }
1206
+ ],
1207
+ state: "success"
1208
+ });
1209
+ }
1210
+ };
1211
+ }
1212
+ function TaskUpdate() {
1213
+ return {
1214
+ name: "TaskUpdate",
1215
+ description: `Use this tool to update a task in the task list.
1216
+
1217
+ ## When to Use This Tool
1218
+
1219
+ **Mark tasks as resolved:**
1220
+ - When you have completed the work described in a task
1221
+ - When a task is no longer needed or has been superseded
1222
+ - IMPORTANT: Always mark your assigned tasks as resolved when you finish them
1223
+
1224
+ **Delete tasks:**
1225
+ - When a task is no longer relevant or was created in error
1226
+ - Setting status to 'deleted' permanently removes the task
1227
+
1228
+ **Update task details:**
1229
+ - When requirements change or become clearer
1230
+ - When establishing dependencies between tasks`,
1231
+ inputSchema: z8.object({
1232
+ taskId: z8.string().describe("The ID of the task to update"),
1233
+ status: z8.enum(["pending", "in_progress", "completed", "deleted"]).optional().describe("New status for the task"),
1234
+ subject: z8.string().optional().describe("New subject for the task"),
1235
+ description: z8.string().optional().describe("New description for the task"),
1236
+ activeForm: z8.string().optional().describe(
1237
+ 'Present continuous form shown in spinner when in_progress (e.g., "Running tests")'
1238
+ ),
1239
+ owner: z8.string().optional().describe("New owner for the task"),
1240
+ metadata: z8.record(z8.string(), z8.unknown()).optional().describe("Metadata keys to merge into the task. Set a key to null to delete it."),
1241
+ addBlocks: z8.array(z8.string()).optional().describe("Task IDs that this task blocks"),
1242
+ addBlockedBy: z8.array(z8.string()).optional().describe("Task IDs that block this task")
1243
+ }),
1244
+ requireUserConfirm: false,
1245
+ call({
1246
+ taskId,
1247
+ status,
1248
+ subject,
1249
+ description,
1250
+ activeForm,
1251
+ owner,
1252
+ metadata,
1253
+ addBlocks,
1254
+ addBlockedBy
1255
+ }) {
1256
+ const task = taskStore.get(taskId);
1257
+ if (!task) {
1258
+ throw new Error(`Task not found: ${taskId}`);
1259
+ }
1260
+ if (addBlocks) {
1261
+ for (const depId of addBlocks) {
1262
+ if (!taskStore.has(depId)) {
1263
+ throw new Error(`Cannot add dependency: task ${depId} does not exist`);
1264
+ }
1265
+ }
1266
+ }
1267
+ if (addBlockedBy) {
1268
+ for (const depId of addBlockedBy) {
1269
+ if (!taskStore.has(depId)) {
1270
+ throw new Error(`Cannot add dependency: task ${depId} does not exist`);
1271
+ }
1272
+ }
1273
+ }
1274
+ if (subject !== void 0) task.subject = subject;
1275
+ if (description !== void 0) task.description = description;
1276
+ if (activeForm !== void 0) task.activeForm = activeForm;
1277
+ if (owner !== void 0) task.owner = owner;
1278
+ if (metadata !== void 0) {
1279
+ if (!task.metadata) {
1280
+ task.metadata = {};
1281
+ }
1282
+ for (const [key, value] of Object.entries(metadata)) {
1283
+ if (value === null) {
1284
+ delete task.metadata[key];
1285
+ } else {
1286
+ task.metadata[key] = value;
1287
+ }
1288
+ }
1289
+ }
1290
+ if (addBlocks) {
1291
+ task.blocks = [.../* @__PURE__ */ new Set([...task.blocks, ...addBlocks])];
1292
+ }
1293
+ if (addBlockedBy) {
1294
+ task.blockedBy = [.../* @__PURE__ */ new Set([...task.blockedBy, ...addBlockedBy])];
1295
+ }
1296
+ task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1297
+ if (status !== void 0) {
1298
+ if (status === "deleted") {
1299
+ taskStore.delete(taskId);
1300
+ return createToolResponse({
1301
+ content: [
1302
+ {
1303
+ id: crypto.randomUUID(),
1304
+ type: "text",
1305
+ text: `Task #${taskId} deleted successfully`
1306
+ }
1307
+ ],
1308
+ state: "success"
1309
+ });
1310
+ }
1311
+ task.status = status;
1312
+ }
1313
+ return createToolResponse({
1314
+ content: [
1315
+ {
1316
+ id: crypto.randomUUID(),
1317
+ type: "text",
1318
+ text: `Task #${taskId} updated successfully`
1319
+ }
1320
+ ],
1321
+ state: "success"
1322
+ });
1323
+ }
1324
+ };
1325
+ }
1326
+ function TaskGet() {
1327
+ return {
1328
+ name: "TaskGet",
1329
+ description: `Use this tool to retrieve a task by its ID from the task list.
1330
+
1331
+ ## When to Use This Tool
1332
+
1333
+ - When you need the full description and context before starting work on a task
1334
+ - To understand task dependencies (what it blocks, what blocks it)
1335
+ - After being assigned a task, to get complete requirements`,
1336
+ inputSchema: z8.object({
1337
+ taskId: z8.string().describe("The ID of the task to retrieve")
1338
+ }),
1339
+ requireUserConfirm: false,
1340
+ call({ taskId }) {
1341
+ const task = taskStore.get(taskId);
1342
+ if (!task) {
1343
+ throw new Error(`Task not found: ${taskId}`);
1344
+ }
1345
+ let text = `Task #${task.id}: ${task.subject}
1346
+ `;
1347
+ text += `Status: ${task.status}
1348
+ `;
1349
+ text += `Description: ${task.description}
1350
+ `;
1351
+ if (task.activeForm) {
1352
+ text += `Active Form: ${task.activeForm}
1353
+ `;
1354
+ }
1355
+ if (task.owner) {
1356
+ text += `Owner: ${task.owner}
1357
+ `;
1358
+ }
1359
+ if (task.blocks.length > 0) {
1360
+ text += `Blocks: ${task.blocks.join(", ")}
1361
+ `;
1362
+ }
1363
+ if (task.blockedBy.length > 0) {
1364
+ text += `Blocked By: ${task.blockedBy.join(", ")}
1365
+ `;
1366
+ }
1367
+ if (task.metadata && Object.keys(task.metadata).length > 0) {
1368
+ text += `Metadata: ${JSON.stringify(task.metadata, null, 2)}
1369
+ `;
1370
+ }
1371
+ text += `Created: ${task.createdAt}
1372
+ `;
1373
+ text += `Updated: ${task.updatedAt}`;
1374
+ return createToolResponse({
1375
+ content: [
1376
+ {
1377
+ id: crypto.randomUUID(),
1378
+ type: "text",
1379
+ text
1380
+ }
1381
+ ],
1382
+ state: "success"
1383
+ });
1384
+ }
1385
+ };
1386
+ }
1387
+ function TaskList() {
1388
+ return {
1389
+ name: "TaskList",
1390
+ description: `Use this tool to list all tasks in the task list.
1391
+
1392
+ ## When to Use This Tool
1393
+
1394
+ - To see what tasks are available to work on (status: 'pending', no owner, not blocked)
1395
+ - To check overall progress on the project
1396
+ - To find tasks that are blocked and need dependencies resolved
1397
+ - After completing a task, to check for newly unblocked work or claim the next available task`,
1398
+ inputSchema: z8.object({}),
1399
+ requireUserConfirm: false,
1400
+ call() {
1401
+ const activeTasks = Array.from(taskStore.values()).filter((task) => task.status === "pending" || task.status === "in_progress").sort((a, b) => Number(a.id) - Number(b.id));
1402
+ if (activeTasks.length === 0) {
1403
+ return createToolResponse({
1404
+ content: [
1405
+ {
1406
+ id: crypto.randomUUID(),
1407
+ type: "text",
1408
+ text: "No active tasks found"
1409
+ }
1410
+ ],
1411
+ state: "success"
1412
+ });
1413
+ }
1414
+ const lines = activeTasks.map((task) => {
1415
+ let line = `#${task.id} [${task.status}] ${task.subject}`;
1416
+ if (task.blockedBy.length > 0) {
1417
+ line += ` (blocked by: #${task.blockedBy.join(", #")})`;
1418
+ }
1419
+ return line;
1420
+ });
1421
+ return createToolResponse({
1422
+ content: [
1423
+ {
1424
+ id: crypto.randomUUID(),
1425
+ type: "text",
1426
+ text: lines.join("\n")
1427
+ }
1428
+ ],
1429
+ state: "success"
1430
+ });
1431
+ }
1432
+ };
1433
+ }
1434
+ export {
1435
+ Bash,
1436
+ Edit,
1437
+ Glob,
1438
+ Grep,
1439
+ Read,
1440
+ TaskCreate,
1441
+ TaskGet,
1442
+ TaskList,
1443
+ TaskUpdate,
1444
+ Toolkit,
1445
+ Write
1446
+ };
1447
+ //# sourceMappingURL=index.mjs.map