@amical/aiagent-sdk 0.1.10 → 0.1.12

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.
package/dist/index.js CHANGED
@@ -136,9 +136,11 @@ return (
136
136
  },
137
137
  {
138
138
  name: "agent_read_file" /* FILE_READ */,
139
- description: "Read the contents of a specific file.",
139
+ description: "Read the contents of a specific file. The returned content includes line numbers (e.g. '10|const x = 1;') so you can reference exact lines when editing.",
140
140
  params: {
141
- path: "(required) The absolute or relative file path to read."
141
+ path: "(required) The absolute or relative file path to read.",
142
+ start_line: "(optional) The 1-based line number to start reading from (inclusive). Defaults to 1.",
143
+ end_line: "(optional) The 1-based line number to stop reading at (inclusive). Defaults to the last line."
142
144
  }
143
145
  },
144
146
  {
@@ -285,6 +287,44 @@ var READ_ONLY_TOOLS = [
285
287
  "agent_load_skill" /* LOAD_SKILL */,
286
288
  "agent_use_subagents" /* USE_SUBAGENTS */
287
289
  ];
290
+
291
+ // src/shared/custom-tools.ts
292
+ function isCustomToolGroup(tool) {
293
+ return Array.isArray(tool.children) && tool.children.length > 0;
294
+ }
295
+ function collectCustomToolNames(tools) {
296
+ const names = [];
297
+ const visit = (list) => {
298
+ for (const tool of list) {
299
+ names.push(tool.name);
300
+ if (Array.isArray(tool.children) && tool.children.length > 0) {
301
+ visit(tool.children);
302
+ }
303
+ }
304
+ };
305
+ visit(tools);
306
+ return names;
307
+ }
308
+ function renderCustomToolPrompt(tool, cwd = "") {
309
+ const params = tool.params ? Object.entries(tool.params) : [];
310
+ const replaceCwd = (text) => String(text).replaceAll("${context.cwd}", cwd);
311
+ const paramLines = params.map(([name, description]) => `- ${name}: ${replaceCwd(description)}`).join("\n");
312
+ const usageLines = params.map(([name, description]) => `<${name}>${replaceCwd(description)}</${name}>`).join("\n");
313
+ return `### ${tool.name}
314
+ Description: ${tool.description}
315
+ Parameters:
316
+ ${paramLines}
317
+ - task_progress: (optional) A checklist showing task progress after this tool use is completed. The task_progress parameter must be included as a separate parameter inside of the parent tool call, it must be separate from other parameters such as content, arguments, etc. (See 'UPDATING TASK PROGRESS' section for more details)
318
+ Usage:
319
+ <${tool.name}>
320
+ ${usageLines}
321
+ <task_progress>Checklist here (optional)</task_progress>
322
+ </${tool.name}>
323
+ `;
324
+ }
325
+ function renderCustomToolsPrompt(tools, cwd = "") {
326
+ return tools.map((tool) => renderCustomToolPrompt(tool, cwd)).join("\n");
327
+ }
288
328
  function parseAssistantMessageV2(assistantMessage, toolParamNames2) {
289
329
  const contentBlocks = [];
290
330
  let currentTextContentStart = 0;
@@ -543,7 +583,17 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
543
583
  "dockerfile",
544
584
  "makefile",
545
585
  "properties",
546
- "lock"
586
+ "lock",
587
+ "node-version",
588
+ "nvmrc",
589
+ "editorconfig",
590
+ "prettierrc",
591
+ "eslintrc",
592
+ "babelrc",
593
+ "stylelintrc",
594
+ "huskyrc",
595
+ "commitlintrc",
596
+ "lintstagedrc"
547
597
  ]);
548
598
  var AgentState = /* @__PURE__ */ ((AgentState2) => {
549
599
  AgentState2["IDLE"] = "idle";
@@ -568,9 +618,19 @@ var ClineAgent = class {
568
618
  skillDir;
569
619
  rulesDir;
570
620
  customTools;
621
+ /** 当前可用(已展开集合)的顶层工具列表,传给 PromptManager 渲染 */
622
+ activeCustomTools;
623
+ /** 所有已知工具名(含未激活 children),便于查找定义 */
624
+ customToolsByName;
571
625
  customToolNames;
572
626
  allowDefaultTools;
573
627
  customInstructions;
628
+ /** 已激活的工具集入口名 */
629
+ activatedGroups = /* @__PURE__ */ new Set();
630
+ /** activeCustomTools 变更版本号,用于 Prompt 缓存失效 */
631
+ activeToolsVersion = 0;
632
+ /** 缓存 sysPrompt 时记录的版本号 */
633
+ promptActiveToolsVersion = -1;
574
634
  sysPrompt;
575
635
  noUseTollSysPrompt;
576
636
  // 简单的事件回调系统,供宿主环境注册以监听 Agent 内部事件
@@ -590,33 +650,47 @@ var ClineAgent = class {
590
650
  this.allowDefaultTools.push("agent_attempt_completion" /* ATTEMPT */);
591
651
  }
592
652
  this.customInstructions = options.customInstructions;
653
+ this.customToolsByName = /* @__PURE__ */ new Map();
654
+ this.activeCustomTools = [...this.customTools];
655
+ this.customToolNames = new Set(collectCustomToolNames(this.customTools));
593
656
  if (this.customTools.length > 0) {
594
- const dynamicToolNames = /* @__PURE__ */ new Set();
595
657
  const customToolParams = /* @__PURE__ */ new Set();
596
- for (const customTool of this.customTools) {
597
- dynamicToolNames.add(customTool.name);
598
- if (customTool.params) {
599
- for (const key in customTool.params) {
600
- if (!Object.hasOwn(customTool.params, key)) continue;
601
- customToolParams.add(key);
658
+ const visit = (tools) => {
659
+ for (const customTool of tools) {
660
+ this.customToolsByName.set(customTool.name, customTool);
661
+ if (customTool.params) {
662
+ for (const key in customTool.params) {
663
+ if (!Object.hasOwn(customTool.params, key)) continue;
664
+ customToolParams.add(key);
665
+ }
666
+ }
667
+ if (Array.isArray(customTool.children) && customTool.children.length > 0) {
668
+ visit(customTool.children);
602
669
  }
603
670
  }
604
- }
671
+ };
672
+ visit(this.customTools);
605
673
  const defaultToolNames = Object.values(AgentDefaultTool);
606
- const overlappingTools = Array.from(dynamicToolNames).filter((name) => defaultToolNames.includes(name));
674
+ const overlappingTools = Array.from(this.customToolNames).filter((name) => defaultToolNames.includes(name));
607
675
  if (overlappingTools.length > 0) {
608
676
  throw new Error(`Custom tools and default tools have overlapping names: ${overlappingTools.join(", ")}`);
609
677
  }
610
- if (dynamicToolNames.size > 0) {
611
- setDynamicToolUseNames("customUser", Array.from(dynamicToolNames));
678
+ if (this.customToolNames.size > 0) {
679
+ setDynamicToolUseNames("customUser", Array.from(this.customToolNames));
612
680
  }
613
681
  if (customToolParams.size > 0) {
614
682
  this.allToolParamNames = Array.from(/* @__PURE__ */ new Set([...this.allToolParamNames, ...Array.from(customToolParams)]));
615
683
  }
616
684
  }
617
- this.customToolNames = new Set(this.customTools.map((t) => t.name));
618
685
  this.skillDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/skills");
619
686
  this.rulesDir = this.runtime.path.join(this.runtime.env.cwd(), ".agents/rules");
687
+ const originalOnCondense = this.memory.onCondense;
688
+ this.memory.onCondense = async () => {
689
+ this.api.clearConversationId();
690
+ if (originalOnCondense) {
691
+ await originalOnCondense();
692
+ }
693
+ };
620
694
  }
621
695
  setState(newState) {
622
696
  this.state = newState;
@@ -638,10 +712,11 @@ var ClineAgent = class {
638
712
  shell: this.runtime.env.shell(),
639
713
  skills: await this.getSkills(),
640
714
  agentRules: await this.getAgentRules(),
641
- customTools: this.customTools,
715
+ customTools: this.activeCustomTools,
642
716
  allowDefaultTools: this.allowDefaultTools,
643
717
  customInstructions: this.customInstructions
644
718
  };
719
+ this.promptActiveToolsVersion = this.activeToolsVersion;
645
720
  this.sysPrompt = await this.promptManager.getSystemPrompt(context);
646
721
  this.noUseTollSysPrompt = this.promptManager.getNoUseToolInstructions(context);
647
722
  return { sysPrompt: this.sysPrompt, noUseTollSysPrompt: this.noUseTollSysPrompt };
@@ -673,16 +748,6 @@ var ClineAgent = class {
673
748
  async getAgentRules() {
674
749
  const cwd = this.runtime.env.cwd();
675
750
  const rules = [];
676
- const agentsMdPath = this.runtime.path.join(cwd, "Agents.md");
677
- try {
678
- if (await this.runtime.fs.fileExists(agentsMdPath)) {
679
- const content = await this.runtime.fs.readFile(agentsMdPath);
680
- if (content && content.trim()) {
681
- rules.push(content.trim());
682
- }
683
- }
684
- } catch (e) {
685
- }
686
751
  try {
687
752
  if (await this.runtime.fs.fileExists(this.rulesDir)) {
688
753
  const files = await this.runtime.fs.readDir(this.rulesDir);
@@ -708,6 +773,28 @@ ${content.trim()}\`\`\``);
708
773
  }
709
774
  } catch (e) {
710
775
  }
776
+ try {
777
+ const getAgentsMdPath = async () => {
778
+ const agentsMdPathList = [
779
+ this.runtime.path.join(cwd, "AGENTS.md"),
780
+ this.runtime.path.join(cwd, "Agents.md"),
781
+ this.runtime.path.join(cwd, "agents.md")
782
+ ];
783
+ for (const item of agentsMdPathList) {
784
+ if (await this.runtime.fs.fileExists(item)) {
785
+ return item;
786
+ }
787
+ }
788
+ };
789
+ const agentsMdPath = await getAgentsMdPath();
790
+ if (agentsMdPath) {
791
+ const content = await this.runtime.fs.readFile(agentsMdPath);
792
+ if (content && content.trim()) {
793
+ rules.push(content.trim());
794
+ }
795
+ }
796
+ } catch (e) {
797
+ }
711
798
  return rules.join("\n---\n");
712
799
  }
713
800
  async getSkills() {
@@ -750,9 +837,9 @@ ${content.trim()}\`\`\``);
750
837
  * 核心任务循环
751
838
  */
752
839
  async loop() {
753
- const { sysPrompt, noUseTollSysPrompt } = await this.getPrompt(true);
754
- const { customTools, customToolNames } = this;
840
+ const { customToolsByName, customToolNames } = this;
755
841
  while (this.state === "thinking" /* THINKING */) {
842
+ const { sysPrompt, noUseTollSysPrompt } = await this.getCurrentPrompt();
756
843
  this.loopCount++;
757
844
  if (this.loopCount > this.maxLoopCount) {
758
845
  await this.memory.appendMessage({
@@ -797,7 +884,33 @@ ${content.trim()}\`\`\``);
797
884
  }
798
885
  }
799
886
  if (customToolNames.has(tool.name)) {
800
- const customTool = customTools.find((t) => t.name === tool.name);
887
+ const customTool = customToolsByName.get(tool.name);
888
+ if (!customTool) {
889
+ toolResults.push({ tool, result: `Error: Custom tool "${tool.name}" not found.` });
890
+ continue;
891
+ }
892
+ if (isCustomToolGroup(customTool)) {
893
+ let groupResult;
894
+ if (customTool.onCall) {
895
+ try {
896
+ groupResult = await customTool.onCall(tool.params);
897
+ } catch (error) {
898
+ toolResults.push({ tool, result: `Error: ${error.message}` });
899
+ continue;
900
+ }
901
+ }
902
+ const childrenPrompt = this.activateCustomToolGroup(customTool);
903
+ const finalPrompt = `# New Available Tools
904
+
905
+ The following are instructions for using the new available tools.
906
+
907
+ ${childrenPrompt}`;
908
+ const promptBlock = groupResult !== void 0 ? `${typeof groupResult === "string" ? groupResult : JSON.stringify(groupResult)}
909
+
910
+ ${finalPrompt}` : finalPrompt;
911
+ toolResults.push({ tool, result: promptBlock });
912
+ continue;
913
+ }
801
914
  if (customTool.onCall) {
802
915
  try {
803
916
  const res = await customTool.onCall(tool.params);
@@ -819,7 +932,8 @@ ${content.trim()}\`\`\``);
819
932
  if (path) {
820
933
  try {
821
934
  const content = await this.runtime.fs.readFile(path);
822
- toolResults.push({ tool, result: content });
935
+ const result = this.formatFileContentWithLineNumbers(content, tool.params.start_line, tool.params.end_line);
936
+ toolResults.push({ tool, result });
823
937
  } catch (error) {
824
938
  toolResults.push({ tool, result: `Error: ${error.message}` });
825
939
  }
@@ -832,6 +946,8 @@ ${content.trim()}\`\`\``);
832
946
  const result = await this.runtime.terminal.execCommand(cmd);
833
947
  toolResults.push({ tool, result: `STDOUT: ${result.stdout}
834
948
  STDERR: ${result.stderr}` });
949
+ } else {
950
+ toolResults.push({ tool, result: "Error: No command provided." });
835
951
  }
836
952
  } else if (tool.name === "agent_write_to_file" /* FILE_NEW */) {
837
953
  const path = tool.params.path;
@@ -843,12 +959,18 @@ STDERR: ${result.stderr}` });
843
959
  } catch (error) {
844
960
  toolResults.push({ tool, result: `Error: ${error.message}` });
845
961
  }
962
+ } else {
963
+ toolResults.push({ tool, result: "Error: Missing path or content." });
846
964
  }
847
965
  } else if (tool.name === "agent_list_files" /* LIST_FILES */) {
848
966
  const path = tool.params.path || this.runtime.env.cwd();
967
+ const recursive = tool.params.recursive === "true";
849
968
  try {
850
- const files = await this.runtime.fs.readDir(path);
851
- toolResults.push({ tool, result: files.map((f) => f.name + (f.isDirectory() ? "/" : "")).join("\n") });
969
+ const files = await this.runtime.fs.readDir(path, { recursive });
970
+ toolResults.push({
971
+ tool,
972
+ result: files.map((f) => `${f.isDirectory() ? "[DIR] " : " "}${f.name}${f.isDirectory() ? "/" : ""}`).join("\n")
973
+ });
852
974
  } catch (e) {
853
975
  toolResults.push({ tool, result: `Error: ${e.message}` });
854
976
  }
@@ -988,10 +1110,15 @@ STDERR: ${result.stderr}` });
988
1110
  try {
989
1111
  const res = await fetch(url);
990
1112
  const text = await res.text();
991
- toolResults.push({ tool, result: text.substring(0, 3e3) });
1113
+ const MAX_LEN = 8e3;
1114
+ const truncated = text.length > MAX_LEN ? `${text.substring(0, MAX_LEN)}
1115
+ ...[truncated ${text.length - MAX_LEN} chars]` : text;
1116
+ toolResults.push({ tool, result: truncated });
992
1117
  } catch (e) {
993
1118
  toolResults.push({ tool, result: `Error: ${e.message}` });
994
1119
  }
1120
+ } else {
1121
+ toolResults.push({ tool, result: "Error: No url provided." });
995
1122
  }
996
1123
  } else if (tool.name === "agent_focus_chain" /* TODO */) {
997
1124
  toolResults.push({ tool, result: `Focus chain updated successfully.` });
@@ -1058,6 +1185,65 @@ ${this.promptManager.getUsedToolResultInstructions()}`;
1058
1185
  }
1059
1186
  }
1060
1187
  }
1188
+ /**
1189
+ * 激活自定义工具集:将入口的 children 注册到活跃工具列表并返回渲染后的 prompt。
1190
+ */
1191
+ activateCustomToolGroup(group) {
1192
+ if (this.activatedGroups.has(group.name)) {
1193
+ return renderCustomToolsPrompt(group.children || [], this.runtime.env.cwd());
1194
+ }
1195
+ this.activatedGroups.add(group.name);
1196
+ this.activeCustomTools = [...this.activeCustomTools, ...group.children || []];
1197
+ this.activeToolsVersion++;
1198
+ return renderCustomToolsPrompt(group.children || [], this.runtime.env.cwd());
1199
+ }
1200
+ /**
1201
+ * 检查自上次获取 prompt 后是否有工具集被激活,若有则刷新缓存。
1202
+ */
1203
+ async getCurrentPrompt() {
1204
+ if (this.promptActiveToolsVersion !== this.activeToolsVersion) {
1205
+ return this.getPrompt(true);
1206
+ }
1207
+ return this.getPrompt();
1208
+ }
1209
+ /**
1210
+ * 将文件内容格式化为带行号文本(模拟编辑器行号显示),
1211
+ * 支持通过 start_line / end_line 截取读取范围。
1212
+ */
1213
+ formatFileContentWithLineNumbers(content, startLineParam, endLineParam) {
1214
+ const lines = content.split(/\r?\n/);
1215
+ const normalized = lines.length > 0 && lines[lines.length - 1] === "" ? lines.slice(0, -1) : lines;
1216
+ const total = normalized.length;
1217
+ if (total === 0) {
1218
+ return "1|(empty file)";
1219
+ }
1220
+ const parseLine = (val, fallback) => {
1221
+ if (val === void 0 || val === null || val.trim() === "") {
1222
+ return fallback;
1223
+ }
1224
+ const num = Number.parseInt(val, 10);
1225
+ if (!Number.isFinite(num) || num <= 0) {
1226
+ return fallback;
1227
+ }
1228
+ return num;
1229
+ };
1230
+ const start = parseLine(startLineParam, 1);
1231
+ const end = parseLine(endLineParam, total);
1232
+ if (start > end) {
1233
+ return `Error: start_line (${start}) cannot be greater than end_line (${end}).`;
1234
+ }
1235
+ if (start > total) {
1236
+ return `Error: start_line (${start}) is out of range. The file has ${total} lines.`;
1237
+ }
1238
+ const from = Math.max(1, start);
1239
+ const to = Math.min(total, end);
1240
+ const width = String(total).length;
1241
+ const selected = normalized.slice(from - 1, to);
1242
+ const numbered = selected.map((line, idx) => `${String(from + idx).padStart(width)}|${line}`).join("\n");
1243
+ const header = from === 1 && to === total ? `1-${total}/${total}` : `${from}-${to}/${total}`;
1244
+ return `${header}
1245
+ ${numbered}`;
1246
+ }
1061
1247
  /**
1062
1248
  * 简单的 Diff 解析与替换辅助方法
1063
1249
  */
@@ -1230,9 +1416,13 @@ exports.ClineAgent = ClineAgent;
1230
1416
  exports.READ_ONLY_TOOLS = READ_ONLY_TOOLS;
1231
1417
  exports.TOOL_RESULT_BLOCK = TOOL_RESULT_BLOCK;
1232
1418
  exports.VERSION = VERSION;
1419
+ exports.collectCustomToolNames = collectCustomToolNames;
1233
1420
  exports.defaultTools = defaultTools;
1234
1421
  exports.getToolUseNames = getToolUseNames;
1422
+ exports.isCustomToolGroup = isCustomToolGroup;
1235
1423
  exports.parseAssistantMessageV2 = parseAssistantMessageV2;
1424
+ exports.renderCustomToolPrompt = renderCustomToolPrompt;
1425
+ exports.renderCustomToolsPrompt = renderCustomToolsPrompt;
1236
1426
  exports.setDynamicToolUseNames = setDynamicToolUseNames;
1237
1427
  exports.toolParamNames = toolParamNames;
1238
1428
  exports.toolUseNames = toolUseNames;