@saber2pr/ai-agent 0.0.10 → 0.0.11

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/README.md CHANGED
@@ -111,7 +111,7 @@ class MyPrivateLLM extends BaseChatModel {
111
111
  | ----------------- | ------------------------------------------------------------------------ |
112
112
  | `generate_review` | Finalizes the process by submitting a structured violation report. |
113
113
  | `get_repo_map` | Generates a high-level map of the project files and exports. |
114
- | `read_full_code` | Reads file content with line numbers for precise auditing. |
114
+ | `read_text_file` | Reads file content with line numbers for precise auditing. |
115
115
  | `read_skeleton` | Extracts class/function signatures without full logic (Token efficient). |
116
116
 
117
117
  ---
@@ -16,8 +16,7 @@ export default class McpChainAgent {
16
16
  * 工具处理器包装逻辑:增加日志打印和 Token 监控
17
17
  */
18
18
  private wrapHandler;
19
- private registerBuiltinTools;
20
- private injectCustomTools;
19
+ private initTools;
21
20
  private calculateTokens;
22
21
  private pruneMessages;
23
22
  init(): Promise<void>;
@@ -43,12 +43,11 @@ const readline = __importStar(require("readline"));
43
43
  const prompts_1 = require("@langchain/core/prompts");
44
44
  const tools_1 = require("@langchain/core/tools");
45
45
  const openai_1 = require("@langchain/openai");
46
- const builtin_1 = require("../tools/builtin");
47
46
  const config_1 = require("../config/config");
48
47
  const jsonSchemaToZod_1 = require("../utils/jsonSchemaToZod");
48
+ const builtin_1 = require("../tools/builtin");
49
49
  class McpChainAgent {
50
50
  constructor(options) {
51
- var _a;
52
51
  this.allTools = [];
53
52
  this.messages = [];
54
53
  this.encoder = (0, js_tiktoken_1.getEncoding)("cl100k_base");
@@ -66,7 +65,7 @@ class McpChainAgent {
66
65
  1. **全局扫描(强制首选)**:在开始任何分析任务前,你【必须】首先调用 'get_repo_map'。这是理解项目结构、技术栈及模块关系的唯一来源。
67
66
  2. **循序渐进的分析路径**:
68
67
  - 优先使用 'read_skeleton' 提取接口和函数签名。
69
- - 仅在需要分析具体逻辑或准备修复代码时,才使用 'read_full_code'。
68
+ - 仅在需要分析具体逻辑或准备修复代码时,才使用 'read_text_file'。
70
69
  3. **真实性原则**:所有的代码分析必须基于工具返回的真实内容,严禁虚假猜测。`;
71
70
  this.messages.push({
72
71
  role: "system",
@@ -74,20 +73,7 @@ class McpChainAgent {
74
73
  ? `${baseSystemPrompt}\n\n[额外指令]:\n${JSON.stringify(options.extraSystemPrompt)}`
75
74
  : baseSystemPrompt,
76
75
  });
77
- if ((_a = options === null || options === void 0 ? void 0 : options.builtinTools) === null || _a === void 0 ? void 0 : _a.length) {
78
- this.allTools.push(...options.builtinTools.map((t) => ({
79
- type: t.type,
80
- function: t.function,
81
- _handler: this.wrapHandler(t.function.name, t._handler),
82
- })));
83
- }
84
- else {
85
- this.registerBuiltinTools({
86
- ...options,
87
- ...this,
88
- });
89
- }
90
- this.injectCustomTools();
76
+ this.initTools(options);
91
77
  }
92
78
  /**
93
79
  * 工具处理器包装逻辑:增加日志打印和 Token 监控
@@ -108,24 +94,23 @@ class McpChainAgent {
108
94
  return content;
109
95
  };
110
96
  }
111
- registerBuiltinTools(options) {
112
- const defaults = (0, builtin_1.createDefaultBuiltinTools)({
113
- options,
114
- getCurrentTokens: () => this.calculateTokens(),
115
- });
116
- this.allTools.push(...defaults.map((t) => ({
117
- type: t.type,
118
- function: t.function,
119
- _handler: this.wrapHandler(t.function.name, t._handler),
120
- })));
121
- }
122
- injectCustomTools() {
123
- for (const tool of this.extraTools) {
124
- this.allTools.push({
125
- type: "function",
126
- function: { name: tool.name, description: tool.description, parameters: tool.parameters },
127
- _handler: this.wrapHandler(tool.name, tool.handler),
128
- });
97
+ initTools(options) {
98
+ const allTools = [
99
+ // 注册内置工具
100
+ ...(0, builtin_1.createDefaultBuiltinTools)({
101
+ options: {
102
+ ...options,
103
+ ...this
104
+ }
105
+ }),
106
+ ...this.extraTools
107
+ ];
108
+ if (allTools === null || allTools === void 0 ? void 0 : allTools.length) {
109
+ this.allTools.push(...allTools.map((t) => ({
110
+ type: t.type,
111
+ function: t.function,
112
+ _handler: this.wrapHandler(t.function.name, t._handler),
113
+ })));
129
114
  }
130
115
  }
131
116
  calculateTokens() {
@@ -174,6 +159,7 @@ class McpChainAgent {
174
159
  },
175
160
  });
176
161
  });
162
+ // src/core/agent-chain.ts 中的 prompt 修改
177
163
  const prompt = prompts_1.PromptTemplate.fromTemplate(`
178
164
  {system_prompt}
179
165
 
@@ -187,7 +173,7 @@ class McpChainAgent {
187
173
  --------------------
188
174
  你必须严格遵守以下回复格式:
189
175
 
190
- Thought: [此处写下你的思考过程]
176
+ Thought: 首先,我会[此处用中文简述你的分析思路和下一步目标]
191
177
  \`\`\`json
192
178
  {{
193
179
  "action": "工具名称",
@@ -196,8 +182,8 @@ Thought: [此处写下你的思考过程]
196
182
  \`\`\`
197
183
 
198
184
  注意:
199
- - 每次调用工具前必须先写 Thought。
200
- - 最终结论请使用 "action": "Final Answer"。
185
+ - 严禁直接输出 JSON,必须先写 Thought。
186
+ - Thought 必须包含具体的分析意图,不少于 10 个字。
201
187
 
202
188
  Begin!
203
189
  Question: {input}
@@ -227,29 +213,34 @@ Thought: {agent_scratchpad}`);
227
213
  // --- 新增:使用回调函数捕获 Thought ---
228
214
  callbacks: [{
229
215
  handleAgentAction: (action, runId) => {
230
- // action.log 包含了 LLM 输出的所有文本(包含 Thought 和 JSON)
231
216
  if (action.log) {
232
217
  const log = action.log.trim();
233
- // 正则说明:匹配 Thought: ```json 或 { 之间的内容
234
- const thoughtMatch = log.match(/Thought:\s*([\s\S]*?)(?=(?:```json|\{|$))/i);
218
+ // 1. 提取 Thought 部分:取 Thought: 之后,直到遇到 ```json 或 { 之前的内容
219
+ const thoughtMatch = log.match(/Thought:\s*([\s\S]*?)(?=(?:```json|\{|Action:|$))/i);
220
+ let thought = "";
235
221
  if (thoughtMatch && thoughtMatch[1]) {
236
- const thought = thoughtMatch[1].trim();
237
- if (thought) {
238
- console.log(`\n💭 [思考]: ${thought}`);
239
- }
222
+ thought = thoughtMatch[1].trim();
223
+ }
224
+ else {
225
+ // 备选方案:如果没有 Thought 标签,直接截取 JSON 之前的文本
226
+ thought = log.split(/```json|\{/)[0].replace(/Thought:/i, "").trim();
240
227
  }
241
- else if (!log.startsWith('{') && !log.startsWith('```')) {
242
- // 备选方案:如果没匹配到 Thought 标签,但有非 JSON 开头的文字,也打印出来
243
- const rawThought = log.split(/```json|\{/)[0].trim();
244
- if (rawThought) {
245
- console.log(`\n💭 [思考]: ${rawThought}`);
228
+ // 2. 只有当 thought 真的有文字内容(且不是 JSON)时才打印
229
+ if (thought && thought.length > 0 && !thought.startsWith('{')) {
230
+ // 进一步清洗:如果 thought 包含多行,只取非空的第一行,避免打印太长
231
+ const displayThought = thought.split('\n').find(line => line.trim().length > 0);
232
+ if (displayThought) {
233
+ console.log(`\n💭 [思考]: ${displayThought}`);
246
234
  }
247
235
  }
248
236
  }
249
237
  }
250
238
  }]
251
239
  });
252
- let output = response.output;
240
+ // 修复点:确保 output 是字符串
241
+ let output = typeof response.output === 'string'
242
+ ? response.output
243
+ : JSON.stringify(response.output);
253
244
  // 清洗 ReAct 冗余标签
254
245
  if (output.includes("Final Answer:")) {
255
246
  output = ((_a = output.split("Final Answer:").pop()) === null || _a === void 0 ? void 0 : _a.trim()) || output;
@@ -15,7 +15,7 @@ export default class McpAgent {
15
15
  * 兼容多模态内容 (Content Parts) 和 工具调用 (Tool Calls)
16
16
  */
17
17
  private calculateTokens;
18
- private injectCustomTools;
18
+ private initTools;
19
19
  /**
20
20
  * 核心功能:内置代码分析工具(基于 engine/targetDir,可被外部通过 createDefaultBuiltinTools 替代)
21
21
  */
package/lib/core/agent.js CHANGED
@@ -48,7 +48,6 @@ const builtin_1 = require("../tools/builtin");
48
48
  const config_1 = require("../config/config");
49
49
  class McpAgent {
50
50
  constructor(options) {
51
- var _a;
52
51
  this.modelName = "";
53
52
  this.allTools = [];
54
53
  this.messages = [];
@@ -64,7 +63,7 @@ class McpAgent {
64
63
  1. **全局扫描(强制首选)**:在开始任何分析任务前,你【必须】首先调用 'get_repo_map'。这是你理解项目目录结构、技术栈及模块关系的唯一权威来源。
65
64
  2. **循序渐进的分析路径**:
66
65
  - 优先使用 'read_skeleton' 提取接口、类和函数签名,以最低的 Token 成本建立代码逻辑视图。
67
- - 仅在需要深入分析具体业务逻辑、提取精准代码块或进行代码修改建议时,才使用 'read_full_code' 或 'get_method_body'。
66
+ - 仅在需要深入分析具体业务逻辑、提取精准代码块或进行代码修改建议时,才使用 'read_text_file' 或 'get_method_body'。
68
67
  3. **真实性原则**:
69
68
  - 所有的代码分析、行号定位和逻辑推断必须基于工具返回的真实内容,严禁基于文件名进行虚假猜测。
70
69
  - 如果工具返回结果为空或报错,应尝试调整路径或更换工具。
@@ -87,17 +86,7 @@ class McpAgent {
87
86
  role: "system",
88
87
  content: baseSystemPrompt,
89
88
  });
90
- // 初始化内置工具:外部传入则使用,否则走默认 registerBuiltinTools
91
- if ((_a = options === null || options === void 0 ? void 0 : options.builtinTools) === null || _a === void 0 ? void 0 : _a.length) {
92
- this.allTools.push(...options.builtinTools);
93
- }
94
- else {
95
- this.registerBuiltinTools({
96
- ...options,
97
- ...this,
98
- });
99
- }
100
- this.injectCustomTools(); // 注入外部工具
89
+ this.initTools(options); // 注入外部工具
101
90
  }
102
91
  /**
103
92
  * 计算当前消息列表的总 Token 消耗
@@ -141,17 +130,19 @@ class McpAgent {
141
130
  }
142
131
  return total;
143
132
  }
144
- injectCustomTools() {
145
- for (const tool of this.extraTools) {
146
- this.allTools.push({
147
- type: "function",
148
- function: {
149
- name: tool.name,
150
- description: tool.description,
151
- parameters: tool.parameters,
152
- },
153
- _handler: tool.handler,
154
- });
133
+ initTools(options) {
134
+ const allTools = [
135
+ // 注册内置工具
136
+ ...(0, builtin_1.createDefaultBuiltinTools)({
137
+ options: {
138
+ ...options,
139
+ ...this
140
+ }
141
+ }),
142
+ ...this.extraTools
143
+ ];
144
+ if (allTools === null || allTools === void 0 ? void 0 : allTools.length) {
145
+ this.allTools.push(...allTools);
155
146
  }
156
147
  }
157
148
  /**
@@ -160,7 +151,6 @@ class McpAgent {
160
151
  registerBuiltinTools(options) {
161
152
  this.allTools.push(...(0, builtin_1.createDefaultBuiltinTools)({
162
153
  options,
163
- getCurrentTokens: () => this.calculateTokens(),
164
154
  }));
165
155
  }
166
156
  // --- 初始化与环境准备 (API Config & MCP Servers) ---
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './core/agent';
2
2
  export { default as McpChainAgent } from './core/agent-chain';
3
3
  export { default } from './core/agent';
4
+ export { createTool } from './utils/createTool';
package/lib/index.js CHANGED
@@ -17,9 +17,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.default = exports.McpChainAgent = void 0;
20
+ exports.createTool = exports.default = exports.McpChainAgent = void 0;
21
21
  __exportStar(require("./core/agent"), exports);
22
22
  var agent_chain_1 = require("./core/agent-chain");
23
23
  Object.defineProperty(exports, "McpChainAgent", { enumerable: true, get: function () { return __importDefault(agent_chain_1).default; } });
24
24
  var agent_1 = require("./core/agent");
25
25
  Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(agent_1).default; } });
26
+ var createTool_1 = require("./utils/createTool");
27
+ Object.defineProperty(exports, "createTool", { enumerable: true, get: function () { return createTool_1.createTool; } });
@@ -1,7 +1,5 @@
1
1
  import { AgentOptions, ToolInfo } from '../types/type';
2
2
  export interface BuiltinToolsContext {
3
3
  options?: AgentOptions;
4
- /** 可选:获取当前已用 token 数 */
5
- getCurrentTokens?: () => number;
6
4
  }
7
5
  export declare function createDefaultBuiltinTools(context: BuiltinToolsContext): ToolInfo[];
@@ -1,98 +1,12 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.createDefaultBuiltinTools = createDefaultBuiltinTools;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- const ts_context_mcp_1 = require("@saber2pr/ts-context-mcp");
10
- const createTool_1 = require("../utils/createTool");
4
+ const filesystem_1 = require("./filesystem");
5
+ const ts_lsp_1 = require("./ts-lsp");
11
6
  function createDefaultBuiltinTools(context) {
12
- const { options, getCurrentTokens } = context;
13
- const engine = new ts_context_mcp_1.PromptEngine((options === null || options === void 0 ? void 0 : options.targetDir) || process.cwd());
14
- const maxTokens = (options === null || options === void 0 ? void 0 : options.maxTokens) || 100000;
15
- const rootDir = () => engine.getRootDir();
7
+ const { options } = context;
16
8
  return [
17
- (0, createTool_1.createTool)({
18
- maxTokens,
19
- getCurrentTokens,
20
- name: "get_repo_map",
21
- description: "获取项目全局文件结构及导出清单,用于快速定位代码",
22
- parameters: { type: "object", properties: {} },
23
- handler: async () => {
24
- engine.refresh();
25
- return engine.getRepoMap();
26
- },
27
- }),
28
- (0, createTool_1.createTool)({
29
- maxTokens,
30
- getCurrentTokens,
31
- name: "analyze_deps",
32
- description: "分析指定文件的依赖关系,支持 tsconfig 路径别名解析",
33
- parameters: { type: "object", properties: { filePath: { type: "string", description: "文件相对路径", required: true } } },
34
- validateParams: ["filePath"],
35
- handler: async ({ filePath }) => engine.getDeps(filePath),
36
- }),
37
- (0, createTool_1.createTool)({
38
- maxTokens,
39
- getCurrentTokens,
40
- name: "read_skeleton",
41
- description: "提取文件的结构定义(接口、类、方法签名),忽略具体实现以节省 Token",
42
- parameters: { type: "object", properties: { filePath: { type: "string", description: "文件相对路径" } } },
43
- handler: async (args) => {
44
- const pathArg = args === null || args === void 0 ? void 0 : args.filePath;
45
- if (typeof pathArg !== "string" || pathArg.trim() === "") {
46
- return `Error: 参数 'filePath' 无效。收到的是: ${JSON.stringify(pathArg)}`;
47
- }
48
- try {
49
- engine.refresh();
50
- const result = engine.getSkeleton(pathArg);
51
- return result || `// Warning: 文件 ${pathArg} 存在但未找到任何可提取的结构。`;
52
- }
53
- catch (error) {
54
- return `Error: 解析文件 ${pathArg} 时发生内部错误: ${error.message}`;
55
- }
56
- },
57
- }),
58
- (0, createTool_1.createTool)({
59
- maxTokens,
60
- getCurrentTokens,
61
- name: "get_method_body",
62
- description: "获取指定文件内某个方法或函数的完整实现代码",
63
- parameters: { type: "object", properties: { filePath: { type: "string", description: "文件相对路径" }, methodName: { type: "string", description: "方法名或函数名" } } },
64
- handler: async ({ filePath, methodName }) => {
65
- return engine.getMethodImplementation(filePath, methodName);
66
- },
67
- }),
68
- (0, createTool_1.createTool)({
69
- maxTokens,
70
- getCurrentTokens,
71
- name: "read_full_code",
72
- description: "读取指定文件的完整源代码内容。当需要分析具体实现逻辑或查找硬编码字符串时使用。",
73
- parameters: { type: "object", properties: { filePath: { type: "string", description: "文件相对路径" } } },
74
- handler: async ({ filePath }) => {
75
- try {
76
- if (typeof filePath !== "string" || !filePath) {
77
- return "Error: filePath 不能为空";
78
- }
79
- const fullPath = path_1.default.resolve(rootDir(), filePath);
80
- if (!fullPath.startsWith(rootDir())) {
81
- return "Error: 权限拒绝,禁止访问项目目录外的文件。";
82
- }
83
- if (!fs_1.default.existsSync(fullPath)) {
84
- return `Error: 文件不存在: ${filePath}`;
85
- }
86
- const content = fs_1.default.readFileSync(fullPath, "utf-8");
87
- return content
88
- .split("\n")
89
- .map((line, i) => `${i + 1} | ${line}`)
90
- .join("\n");
91
- }
92
- catch (err) {
93
- return `Error: 读取文件失败: ${err.message}`;
94
- }
95
- },
96
- }),
9
+ ...(0, ts_lsp_1.getTsLspTools)((options === null || options === void 0 ? void 0 : options.targetDir) || process.cwd()),
10
+ ...(0, filesystem_1.getFilesystemTools)((options === null || options === void 0 ? void 0 : options.targetDir) || process.cwd()),
97
11
  ];
98
12
  }
@@ -0,0 +1 @@
1
+ export declare const getFilesystemTools: (targetDir: string) => import("../../types/type").ToolInfo[];