@saber2pr/ai-agent 0.0.23 → 0.0.24

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.
@@ -23,6 +23,7 @@ const AgentState = langgraph_1.Annotation.Root({
23
23
  default: () => [],
24
24
  }),
25
25
  auditedFiles: (0, langgraph_1.Annotation)({
26
+ // 确保列表是累加且去重的
26
27
  reducer: (x, y) => Array.from(new Set([...x, ...y])),
27
28
  default: () => [],
28
29
  }),
@@ -39,15 +40,15 @@ class McpGraphAgent {
39
40
  constructor(options = {}) {
40
41
  this.checkpointer = new langgraph_1.MemorySaver();
41
42
  this.langchainTools = [];
42
- // ✅ 用于存储清理 loading 的函数
43
+ // ✅ 存储清理 loading 的函数
43
44
  this.stopLoadingFunc = null;
44
45
  this.options = options;
45
46
  this.targetDir = options.targetDir || process.cwd();
46
47
  process.setMaxListeners(100);
47
- // ✅ 退出清理
48
+ // ✅ 全局退出处理:清理动画并恢复光标
48
49
  const cleanup = () => {
49
50
  this.stopLoading();
50
- process.stdout.write('\u001B[?25h'); // 显示光标
51
+ process.stdout.write('\u001B[?25h');
51
52
  process.exit(0);
52
53
  };
53
54
  process.on("SIGINT", cleanup);
@@ -56,11 +57,11 @@ class McpGraphAgent {
56
57
  this.langchainTools = [...builtinToolInfos, ...(options.tools || [])].map((t) => (0, convertToLangChainTool_1.convertToLangChainTool)(t));
57
58
  this.toolNode = new prebuilt_1.ToolNode(this.langchainTools);
58
59
  }
59
- // ✅ 1. 复现你提供的 showLoading 效果
60
+ // ✅ 1. 核心 Loading 动画效果
60
61
  showLoading(text) {
61
62
  const chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
62
63
  let i = 0;
63
- // 隐藏光标,防止闪烁
64
+ // 隐藏光标
64
65
  process.stdout.write('\u001B[?25l');
65
66
  const timer = setInterval(() => {
66
67
  process.stdout.write(`\r\x1b[36m${chars[i]}\x1b[0m ${text}`);
@@ -68,11 +69,10 @@ class McpGraphAgent {
68
69
  }, 80);
69
70
  return () => {
70
71
  clearInterval(timer);
71
- process.stdout.write('\r\x1b[K'); // 清除这一行
72
+ process.stdout.write('\r\x1b[K'); // 清行
72
73
  process.stdout.write('\u001B[?25h'); // 恢复光标
73
74
  };
74
75
  }
75
- // ✅ 2. 封装内部调用方法
76
76
  startLoading(text) {
77
77
  this.stopLoading();
78
78
  this.stopLoadingFunc = this.showLoading(text);
@@ -96,6 +96,7 @@ class McpGraphAgent {
96
96
  temperature: 0,
97
97
  });
98
98
  }
99
+ // 绑定工具,使模型具备调用能力
99
100
  this.model = modelInstance.bindTools(this.langchainTools);
100
101
  return this.model;
101
102
  }
@@ -110,6 +111,7 @@ class McpGraphAgent {
110
111
  if (!config.baseURL || !config.apiKey) {
111
112
  const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
112
113
  const question = (q) => new Promise((res) => rl.question(q, res));
114
+ console.log(`💡 首次运行请配置信息:`);
113
115
  config.baseURL = config.baseURL || await question(`? API Base URL: `);
114
116
  config.apiKey = config.apiKey || await question(`? API Key: `);
115
117
  config.model = config.model || await question(`? Model Name: `) || "gpt-4o";
@@ -128,6 +130,7 @@ class McpGraphAgent {
128
130
  }, { configurable: { thread_id: "auto_worker" }, recursionLimit: 100 });
129
131
  for await (const output of stream)
130
132
  this.renderOutput(output);
133
+ console.log("\n✅ 审计任务已完成。");
131
134
  }
132
135
  async start() {
133
136
  await this.getModel();
@@ -139,6 +142,7 @@ class McpGraphAgent {
139
142
  process.stdout.write('\u001B[?25h');
140
143
  process.exit(0);
141
144
  });
145
+ console.log(`\n💬 已进入交互审计模式 (Thread: session)`);
142
146
  const ask = () => {
143
147
  rl.question("> ", async (input) => {
144
148
  if (input.toLowerCase() === "exit") {
@@ -155,7 +159,8 @@ class McpGraphAgent {
155
159
  }
156
160
  renderOutput(output) {
157
161
  var _a, _b;
158
- this.stopLoading(); // 收到任何输出前,先关掉转圈圈
162
+ this.stopLoading(); // 收到输出的第一时间关掉 Loading
163
+ // 1. 处理 Agent 节点的输出
159
164
  const agentNode = output.agent;
160
165
  if (agentNode) {
161
166
  const msg = agentNode.messages[0];
@@ -171,18 +176,29 @@ class McpGraphAgent {
171
176
  });
172
177
  }
173
178
  }
179
+ // 2. 处理工具节点的简要反馈(防止大数据量锁死终端)
180
+ if (output.tools) {
181
+ console.log(`✅ [工具执行完毕]`);
182
+ }
174
183
  }
175
184
  async callModel(state) {
185
+ const auditedListStr = state.auditedFiles.length > 0
186
+ ? state.auditedFiles.map(f => `\n - ${f}`).join("")
187
+ : "暂无";
176
188
  const prompt = prompts_1.ChatPromptTemplate.fromMessages([
177
189
  ["system", `你是一个代码专家。工作目录:${this.targetDir}。
178
- 当前模式:{mode}。进度:{doneCount}/{targetCount}。
190
+ 当前模式:{mode}
191
+ 进度:{doneCount}/{targetCount}
179
192
  已审计文件:{auditedList}
180
193
 
194
+ # 指令
195
+ 1. 优先通过 directory_tree 了解结构。
196
+ 2. 发现问题后,先用 apply_fix 修复,再用 generate_review 提交。
197
+ 3. 严禁反复执行同一个失败的操作。
181
198
  {extraPrompt}`],
182
199
  new prompts_1.MessagesPlaceholder("messages"),
183
200
  ]);
184
- // ✅ 调用 AI 前开启转圈圈
185
- this.startLoading("AI 正在分析并思考中...");
201
+ this.startLoading("AI 正在分析并思考中");
186
202
  try {
187
203
  const chain = prompt.pipe(this.model);
188
204
  const response = await chain.invoke({
@@ -190,7 +206,7 @@ class McpGraphAgent {
190
206
  mode: state.mode,
191
207
  targetCount: state.targetCount,
192
208
  doneCount: state.auditedFiles.length,
193
- auditedList: state.auditedFiles.join(", "),
209
+ auditedList: auditedListStr,
194
210
  extraPrompt: this.options.extraSystemPrompt || "",
195
211
  });
196
212
  this.stopLoading();
@@ -204,15 +220,18 @@ class McpGraphAgent {
204
220
  async trackProgress(state) {
205
221
  var _a;
206
222
  const lastAiMsg = state.messages[state.messages.length - 1];
207
- const newFiles = [];
223
+ const currentAudited = [...state.auditedFiles];
208
224
  if ((_a = lastAiMsg === null || lastAiMsg === void 0 ? void 0 : lastAiMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length) {
209
225
  for (const tc of lastAiMsg.tool_calls) {
226
+ // 兼容不同的参数命名习惯
210
227
  const file = tc.args.path || tc.args.filePath || tc.args.file;
211
- if (file && typeof file === 'string')
212
- newFiles.push(file);
228
+ if (file && typeof file === 'string') {
229
+ currentAudited.push(file);
230
+ }
213
231
  }
214
232
  }
215
- return { auditedFiles: newFiles };
233
+ // 注意:这里的 reducer 会自动帮我们处理去重
234
+ return { auditedFiles: currentAudited };
216
235
  }
217
236
  async createGraph() {
218
237
  const workflow = new langgraph_1.StateGraph(AgentState)
@@ -223,14 +242,23 @@ class McpGraphAgent {
223
242
  .addConditionalEdges("agent", (state) => {
224
243
  var _a;
225
244
  const lastMsg = state.messages[state.messages.length - 1];
245
+ // 1. 有工具调用,必须去 tools
226
246
  if ((_a = lastMsg.tool_calls) === null || _a === void 0 ? void 0 : _a.length)
227
247
  return "tools";
228
- if (state.mode === "auto" && state.auditedFiles.length < state.targetCount)
229
- return "agent";
248
+ // 2. 自动模式下,如果审计文件数未达标,继续循环
249
+ if (state.mode === "auto") {
250
+ if (state.auditedFiles.length < state.targetCount)
251
+ return "agent";
252
+ }
253
+ // 3. 默认结束(对话模式或任务已达标)
230
254
  return langgraph_1.END;
255
+ }, {
256
+ tools: "tools",
257
+ agent: "agent",
258
+ [langgraph_1.END]: langgraph_1.END,
231
259
  })
232
260
  .addEdge("tools", "progress")
233
- .addEdge("progress", "agent");
261
+ .addEdge("progress", "agent"); // 闭环回到 agent 进行下一轮决策
234
262
  return workflow.compile({ checkpointer: this.checkpointer });
235
263
  }
236
264
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saber2pr/ai-agent",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "description": "AI Assistant CLI.",
5
5
  "author": "saber2pr",
6
6
  "license": "ISC",