@amical/aiagent-sdk 0.1.8 → 0.1.10

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.
@@ -6,7 +6,7 @@ var ai = require('ai');
6
6
  var openai = require('@ai-sdk/openai');
7
7
  var difyAiProvider = require('dify-ai-provider');
8
8
  var fs2 = require('fs/promises');
9
- var path2 = require('path');
9
+ var path = require('path');
10
10
  var os = require('os');
11
11
  var child_process = require('child_process');
12
12
  var util = require('util');
@@ -35,7 +35,7 @@ function _interopNamespace(e) {
35
35
 
36
36
  var matter__default = /*#__PURE__*/_interopDefault(matter);
37
37
  var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
38
- var path2__namespace = /*#__PURE__*/_interopNamespace(path2);
38
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
39
39
  var os__namespace = /*#__PURE__*/_interopNamespace(os);
40
40
 
41
41
  // src/shared/tools.ts
@@ -49,6 +49,7 @@ var AgentDefaultTool = /* @__PURE__ */ ((AgentDefaultTool2) => {
49
49
  AgentDefaultTool2["FILE_DELETE"] = "agent_delete_file";
50
50
  AgentDefaultTool2["DIR_DELETE"] = "agent_delete_directory";
51
51
  AgentDefaultTool2["SEARCH"] = "agent_search_files";
52
+ AgentDefaultTool2["SEARCH_CONTENT"] = "agent_search_file_content";
52
53
  AgentDefaultTool2["LIST_FILES"] = "agent_list_files";
53
54
  AgentDefaultTool2["LIST_CODE_DEF"] = "agent_list_code_definition_names";
54
55
  AgentDefaultTool2["BROWSER"] = "agent_browser_action";
@@ -222,11 +223,22 @@ return (
222
223
  },
223
224
  {
224
225
  name: "agent_search_files" /* SEARCH */,
225
- description: "Search for files in a directory that match a regex pattern or glob pattern.",
226
+ description: "Search for files or directories by name in a directory tree, matching the name against a regex (and optionally a glob). Directories are traversed level by level: all matches in the current level are returned before descending into deeper levels. Use this tool only to locate files/directories by name.",
226
227
  params: {
227
228
  path: "(required) The absolute or relative directory path to search.",
228
- regex: "(required) The regex pattern to match files against.",
229
- file_pattern: "(optional) glob pattern like *.ts."
229
+ regex: "(required) The regex pattern to match file or directory names against.",
230
+ file_pattern: "(optional) glob pattern like *.ts.",
231
+ include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped."
232
+ }
233
+ },
234
+ {
235
+ name: "agent_search_file_content" /* SEARCH_CONTENT */,
236
+ description: "Search the contents of files in a directory tree for a regex pattern. Use this tool when you need to find text or code inside files rather than matching file or directory names.",
237
+ params: {
238
+ path: "(required) The absolute or relative directory path to search.",
239
+ regex: "(required) The regex pattern to match file content against.",
240
+ file_pattern: "(optional) glob pattern like *.ts to limit which files are searched.",
241
+ include_node_modules: "(optional) set to true to include node_modules in the search. Defaults to false, so node_modules is skipped."
230
242
  }
231
243
  },
232
244
  {
@@ -295,6 +307,7 @@ var READ_ONLY_TOOLS = [
295
307
  "agent_list_files" /* LIST_FILES */,
296
308
  "agent_read_file" /* FILE_READ */,
297
309
  "agent_search_files" /* SEARCH */,
310
+ "agent_search_file_content" /* SEARCH_CONTENT */,
298
311
  "agent_list_code_definition_names" /* LIST_CODE_DEF */,
299
312
  "agent_browser_action" /* BROWSER */,
300
313
  "agent_ask_followup_question" /* ASK */,
@@ -464,6 +477,7 @@ var toolParamNames = [
464
477
  "diff",
465
478
  "regex",
466
479
  "file_pattern",
480
+ "include_node_modules",
467
481
  "recursive",
468
482
  "action",
469
483
  "url",
@@ -502,6 +516,66 @@ var toolParamNames = [
502
516
  "start_line",
503
517
  "end_line"
504
518
  ];
519
+ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
520
+ "txt",
521
+ "md",
522
+ "markdown",
523
+ "json",
524
+ "js",
525
+ "mjs",
526
+ "cjs",
527
+ "ts",
528
+ "tsx",
529
+ "jsx",
530
+ "html",
531
+ "htm",
532
+ "css",
533
+ "scss",
534
+ "sass",
535
+ "less",
536
+ "py",
537
+ "java",
538
+ "c",
539
+ "cpp",
540
+ "cc",
541
+ "cxx",
542
+ "h",
543
+ "hpp",
544
+ "cs",
545
+ "go",
546
+ "rs",
547
+ "rb",
548
+ "php",
549
+ "sh",
550
+ "bash",
551
+ "zsh",
552
+ "bat",
553
+ "cmd",
554
+ "ps1",
555
+ "yaml",
556
+ "yml",
557
+ "toml",
558
+ "ini",
559
+ "cfg",
560
+ "conf",
561
+ "xml",
562
+ "svg",
563
+ "csv",
564
+ "log",
565
+ "sql",
566
+ "vue",
567
+ "svelte",
568
+ "astro",
569
+ "graphql",
570
+ "gql",
571
+ "prisma",
572
+ "env",
573
+ "gitignore",
574
+ "dockerfile",
575
+ "makefile",
576
+ "properties",
577
+ "lock"
578
+ ]);
505
579
  var AgentState = /* @__PURE__ */ ((AgentState2) => {
506
580
  AgentState2["IDLE"] = "idle";
507
581
  AgentState2["THINKING"] = "thinking";
@@ -582,7 +656,7 @@ var ClineAgent = class {
582
656
  }
583
657
  }
584
658
  /**
585
- * 获取当前 Agent 的系统提示词
659
+ * 获取当前 Agent 的系统提示词
586
660
  * 如果 force 为 true,则强制重新获取系统提示词
587
661
  */
588
662
  async getPrompt(force = false) {
@@ -857,15 +931,41 @@ STDERR: ${result.stderr}` });
857
931
  const path3 = tool.params.path || this.runtime.env.cwd();
858
932
  const regexStr = tool.params.regex;
859
933
  const filePattern = tool.params.file_pattern;
860
- if (regexStr && this.runtime.fs.searchFiles) {
934
+ const includeNodeModules = tool.params.include_node_modules === "true";
935
+ if (regexStr && path3) {
861
936
  try {
862
- const result = await this.runtime.fs.searchFiles(path3, regexStr, filePattern);
863
- toolResults.push({ tool, result: `${result}` });
937
+ let result;
938
+ if (typeof this.runtime.fs.searchFiles === "function") {
939
+ result = await this.runtime.fs.searchFiles(path3, regexStr, filePattern);
940
+ } else {
941
+ result = await this.searchFiles(path3, regexStr, filePattern, includeNodeModules);
942
+ }
943
+ toolResults.push({ tool, result });
864
944
  } catch (e) {
865
945
  toolResults.push({ tool, result: `Error: ${e.message}` });
866
946
  }
867
947
  } else {
868
- toolResults.push({ tool, result: `Error: searchFiles not supported by runtime or missing regex.` });
948
+ toolResults.push({ tool, result: `Error: miss path or regex params` });
949
+ }
950
+ } else if (tool.name === "agent_search_file_content" /* SEARCH_CONTENT */) {
951
+ const path3 = tool.params.path || this.runtime.env.cwd();
952
+ const regexStr = tool.params.regex;
953
+ const filePattern = tool.params.file_pattern;
954
+ const includeNodeModules = tool.params.include_node_modules === "true";
955
+ if (regexStr && path3) {
956
+ try {
957
+ let result;
958
+ if (typeof this.runtime.fs.searchFileContent === "function") {
959
+ result = await this.runtime.fs.searchFileContent(path3, regexStr, filePattern);
960
+ } else {
961
+ result = await this.searchFileContent(path3, regexStr, filePattern, includeNodeModules);
962
+ }
963
+ toolResults.push({ tool, result });
964
+ } catch (e) {
965
+ toolResults.push({ tool, result: `Error: ${e.message}` });
966
+ }
967
+ } else {
968
+ toolResults.push({ tool, result: `Error: miss path or regex params` });
869
969
  }
870
970
  } else if (tool.name === "agent_use_mcp_tool" /* MCP_USE */) {
871
971
  const serverName = tool.params.server_name;
@@ -970,9 +1070,14 @@ IMPORTANT: The skill is now loaded. Do NOT call agent_load_skill again for this
970
1070
  }
971
1071
  }
972
1072
  if (this.state === "thinking" /* THINKING */ && toolResults.length > 0) {
973
- const toolResultsContext = toolResults.map((tr) => `<${TOOL_RESULT_BLOCK} tool_name="${tr.tool.name}">
1073
+ let toolResultsContext = toolResults.map((tr) => `<${TOOL_RESULT_BLOCK} tool_name="${tr.tool.name}">
974
1074
  ${typeof tr.result === "string" ? tr.result.trim() : JSON.stringify(tr.result)}
975
1075
  </${TOOL_RESULT_BLOCK}>`).join("\n\n");
1076
+ const lastResult = toolResults[toolResults.length - 1];
1077
+ if (lastResult && lastResult.tool.name !== "agent_attempt_completion" /* ATTEMPT */) {
1078
+ toolResultsContext += `
1079
+ ${this.promptManager.getUsedToolResultInstructions()}`;
1080
+ }
976
1081
  if (this.onStreamChunk) {
977
1082
  this.onStreamChunk({ type: "text", text: toolResultsContext });
978
1083
  }
@@ -1030,6 +1135,120 @@ ${searchBlock.substring(0, 100)}...`);
1030
1135
  async getHistory() {
1031
1136
  return this.memory.getHistory();
1032
1137
  }
1138
+ /**
1139
+ * 判断文件是否为可进行内容搜索的文本类文件
1140
+ */
1141
+ isTextFile(name) {
1142
+ const ext = name.includes(".") ? String(name.split(".").pop()).toLowerCase() : "";
1143
+ if (!ext) {
1144
+ return true;
1145
+ }
1146
+ return TEXT_FILE_EXTENSIONS.has(ext);
1147
+ }
1148
+ /**
1149
+ * 按文件名/目录名搜索,层级优先(BFS):
1150
+ * 每一层先匹配完当前层级的所有条目,再进入更深的子目录。
1151
+ */
1152
+ async searchFiles(dirpath, regex, filePattern, includeNodeModules = false) {
1153
+ const matches = [];
1154
+ const MAX_RESULTS = 100;
1155
+ const MAX_DEPTH = 8;
1156
+ const SKIP_DIRS = /* @__PURE__ */ new Set([".git", "dist", ".agent", ".turbopack"]);
1157
+ const shouldSkipDir = (name) => SKIP_DIRS.has(name) || name === "node_modules" && !includeNodeModules;
1158
+ const nameRe = new RegExp(regex);
1159
+ const globRe = filePattern ? new RegExp(filePattern.replace(/\./g, "\\.").replace(/\*/g, ".*")) : null;
1160
+ const queue = [{ dir: dirpath, depth: 0 }];
1161
+ while (queue.length > 0 && matches.length < MAX_RESULTS) {
1162
+ const { dir, depth } = queue.shift();
1163
+ if (depth > MAX_DEPTH) {
1164
+ continue;
1165
+ }
1166
+ let entries;
1167
+ try {
1168
+ entries = await this.runtime.fs.readDir(dir, { recursive: false });
1169
+ } catch {
1170
+ continue;
1171
+ }
1172
+ for (const entry of entries) {
1173
+ if (matches.length >= MAX_RESULTS) {
1174
+ break;
1175
+ }
1176
+ const child = dir === "." ? entry.name : `${dir}/${entry.name}`;
1177
+ if (entry.isDirectory()) {
1178
+ if (!shouldSkipDir(entry.name)) {
1179
+ if (nameRe.test(entry.name)) {
1180
+ matches.push(`${child}/`);
1181
+ }
1182
+ queue.push({ dir: child, depth: depth + 1 });
1183
+ }
1184
+ } else if (entry.isFile()) {
1185
+ if (globRe && !globRe.test(entry.name)) {
1186
+ continue;
1187
+ }
1188
+ if (nameRe.test(entry.name)) {
1189
+ matches.push(child);
1190
+ }
1191
+ }
1192
+ }
1193
+ }
1194
+ if (matches.length === 0) {
1195
+ return "No files found.";
1196
+ }
1197
+ return matches.join("\n");
1198
+ }
1199
+ /**
1200
+ * 按文件内容搜索:正则匹配文件内容,返回匹配文件路径。
1201
+ */
1202
+ async searchFileContent(dirpath, regex, filePattern, includeNodeModules = false) {
1203
+ const matches = [];
1204
+ const MAX_RESULTS = 100;
1205
+ const MAX_DEPTH = 8;
1206
+ const SKIP_DIRS = /* @__PURE__ */ new Set([".git", "dist", ".agent", ".turbopack"]);
1207
+ const shouldSkipDir = (name) => SKIP_DIRS.has(name) || name === "node_modules" && !includeNodeModules;
1208
+ const contentRe = new RegExp(regex);
1209
+ const globRe = filePattern ? new RegExp(filePattern.replace(/\./g, "\\.").replace(/\*/g, ".*")) : null;
1210
+ const walk = async (dir, depth) => {
1211
+ if (depth > MAX_DEPTH || matches.length >= MAX_RESULTS) {
1212
+ return;
1213
+ }
1214
+ let entries;
1215
+ try {
1216
+ entries = await this.runtime.fs.readDir(dir, { recursive: false });
1217
+ } catch {
1218
+ return;
1219
+ }
1220
+ for (const entry of entries) {
1221
+ if (matches.length >= MAX_RESULTS) {
1222
+ break;
1223
+ }
1224
+ const child = dir === "." ? entry.name : `${dir}/${entry.name}`;
1225
+ if (entry.isDirectory()) {
1226
+ if (!shouldSkipDir(entry.name)) {
1227
+ await walk(child, depth + 1);
1228
+ }
1229
+ } else if (entry.isFile()) {
1230
+ if (!this.isTextFile(entry.name)) {
1231
+ continue;
1232
+ }
1233
+ if (globRe && !globRe.test(entry.name)) {
1234
+ continue;
1235
+ }
1236
+ try {
1237
+ const content = await this.runtime.fs.readFile(child);
1238
+ if (typeof content === "string" && contentRe.test(content)) {
1239
+ matches.push(child);
1240
+ }
1241
+ } catch {
1242
+ }
1243
+ }
1244
+ }
1245
+ };
1246
+ await walk(dirpath, 0);
1247
+ if (matches.length === 0) {
1248
+ return "No files found with matching content.";
1249
+ }
1250
+ return matches.join("\n");
1251
+ }
1033
1252
  };
1034
1253
  var NodeApiHandler = class {
1035
1254
  config;
@@ -1404,6 +1623,21 @@ Otherwise, if you have not completed the task and do not need additional informa
1404
1623
  (This is an automated message, so do not respond to it conversationally.)
1405
1624
  ${this?.getEnvironmentInfoPrompt?.(context)}`;
1406
1625
  }
1626
+ getUsedToolResultInstructions() {
1627
+ return `The above are the results of the tool call. Please continue to complete the remaining tasks.
1628
+
1629
+ # TODO LIST UPDATE REQUIRED - You MUST include the task_progress parameter in your NEXT tool call.
1630
+
1631
+ 1. To create or update a todo list, include the task_progress parameter in the next tool call
1632
+ 2. Review each item and update its status:
1633
+ - Mark completed items with: - [x]
1634
+ - Keep incomplete items as: - [ ]
1635
+ - Add new items if you discover additional steps
1636
+ 3. Modify the list as needed:
1637
+ - Add any new steps you've discovered
1638
+ - Reorder if the sequence has changed
1639
+ 4. Ensure the list accurately reflects the current state`;
1640
+ }
1407
1641
  };
1408
1642
  var NodeMemoryManager = class {
1409
1643
  filePath;
@@ -1413,14 +1647,14 @@ var NodeMemoryManager = class {
1413
1647
  * @param maxMessages 保留的最大消息数量,用于简单的历史截断(默认 150)
1414
1648
  */
1415
1649
  constructor(dbPath, maxMessages = 150) {
1416
- this.filePath = path2__namespace.resolve(dbPath);
1650
+ this.filePath = path__namespace.resolve(dbPath);
1417
1651
  this.maxMessages = maxMessages;
1418
1652
  }
1419
1653
  async ensureFile() {
1420
1654
  try {
1421
1655
  await fs2__namespace.access(this.filePath);
1422
1656
  } catch {
1423
- const dir = path2__namespace.dirname(this.filePath);
1657
+ const dir = path__namespace.dirname(this.filePath);
1424
1658
  await fs2__namespace.mkdir(dir, { recursive: true });
1425
1659
  await fs2__namespace.writeFile(this.filePath, "[]", "utf-8");
1426
1660
  }
@@ -1596,10 +1830,13 @@ var NodeMcpManager = class {
1596
1830
  var execAsync = util.promisify(child_process.exec);
1597
1831
  var DefaultNodeRuntime = class {
1598
1832
  fs = {
1599
- readFile: async (filepath) => fs2__namespace.readFile(filepath, "utf-8"),
1833
+ readFile: async (filepath, options = "utf8") => fs2__namespace.readFile(filepath, options),
1600
1834
  writeFile: async (filepath, content) => {
1601
- await fs2__namespace.mkdir(path2__namespace.dirname(filepath), { recursive: true });
1602
- await fs2__namespace.writeFile(filepath, content, "utf-8");
1835
+ const dir = filepath.substring(0, filepath.lastIndexOf("/"));
1836
+ if (dir && dir !== filepath) {
1837
+ await fs2__namespace.mkdir(dir, { recursive: true });
1838
+ }
1839
+ await fs2__namespace.writeFile(filepath, content, "utf8");
1603
1840
  },
1604
1841
  deleteFile: (filepath) => fs2__namespace.unlink(filepath),
1605
1842
  deleteDirectory: (dirpath) => fs2__namespace.rm(dirpath, { recursive: true, force: true }),
@@ -1611,13 +1848,13 @@ var DefaultNodeRuntime = class {
1611
1848
  return false;
1612
1849
  }
1613
1850
  },
1614
- readDir: (dirpath) => fs2__namespace.readdir(dirpath, { withFileTypes: true }),
1851
+ readDir: (dirpath, options) => fs2__namespace.readdir(dirpath, { withFileTypes: true, ...options || {} }),
1615
1852
  mkdir: async (dirpath) => {
1616
1853
  await fs2__namespace.mkdir(dirpath, { recursive: true });
1617
1854
  }
1618
1855
  };
1619
1856
  path = {
1620
- join: (...paths) => path2__namespace.join(...paths)
1857
+ join: (...paths) => path__namespace.join(...paths)
1621
1858
  };
1622
1859
  env = {
1623
1860
  get: (key) => process.env[key],
@@ -1639,7 +1876,7 @@ var DefaultNodeRuntime = class {
1639
1876
  function createNodeAgent(options) {
1640
1877
  const runtime = new DefaultNodeRuntime();
1641
1878
  const api = new NodeApiHandler(options.apiConfig);
1642
- const memory = options.memory || new NodeMemoryManager(options.dbPath || path2__namespace.join(runtime.env.cwd(), ".cline_memory.json"));
1879
+ const memory = options.memory || new NodeMemoryManager(options.dbPath || path__namespace.join(runtime.env.cwd(), ".cline_memory.json"));
1643
1880
  const customTools = options.customTools || [];
1644
1881
  const customToolNames = new Set(customTools.map((t) => t.name));
1645
1882
  const promptManager = new NodePromptManager();