@saber2pr/ai-agent 0.0.10 → 0.0.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/README.md +1 -1
- package/lib/core/agent-chain.d.ts +1 -2
- package/lib/core/agent-chain.js +42 -51
- package/lib/core/agent.d.ts +1 -1
- package/lib/core/agent.js +15 -25
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -1
- package/lib/tools/builtin.d.ts +0 -2
- package/lib/tools/builtin.js +5 -91
- package/lib/tools/filesystem/index.d.ts +1 -0
- package/lib/tools/filesystem/index.js +338 -0
- package/lib/tools/filesystem/lib.d.ts +34 -0
- package/lib/tools/filesystem/lib.js +330 -0
- package/lib/tools/filesystem/path-utils.d.ts +18 -0
- package/lib/tools/filesystem/path-utils.js +111 -0
- package/lib/tools/filesystem/path-validation.d.ts +9 -0
- package/lib/tools/filesystem/path-validation.js +81 -0
- package/lib/tools/filesystem/roots-utils.d.ts +12 -0
- package/lib/tools/filesystem/roots-utils.js +76 -0
- package/lib/tools/ts-lsp/index.d.ts +1 -0
- package/lib/tools/ts-lsp/index.js +56 -0
- package/lib/types/type.d.ts +1 -8
- package/lib/utils/createTool.d.ts +1 -3
- package/lib/utils/createTool.js +0 -3
- package/lib/utils/jsonSchemaToZod.d.ts +1 -1
- package/lib/utils/jsonSchemaToZod.js +33 -9
- package/package.json +5 -2
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
|
-
| `
|
|
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
|
|
20
|
-
private injectCustomTools;
|
|
19
|
+
private initTools;
|
|
21
20
|
private calculateTokens;
|
|
22
21
|
private pruneMessages;
|
|
23
22
|
init(): Promise<void>;
|
package/lib/core/agent-chain.js
CHANGED
|
@@ -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
|
-
- 仅在需要分析具体逻辑或准备修复代码时,才使用 '
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
-
|
|
200
|
-
-
|
|
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
|
-
//
|
|
234
|
-
const thoughtMatch = log.match(/Thought:\s*([\s\S]*?)(?=(?:```json|\{
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
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;
|
package/lib/core/agent.d.ts
CHANGED
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
|
-
- 仅在需要深入分析具体业务逻辑、提取精准代码块或进行代码修改建议时,才使用 '
|
|
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
|
-
//
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
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; } });
|
package/lib/tools/builtin.d.ts
CHANGED
|
@@ -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[];
|
package/lib/tools/builtin.js
CHANGED
|
@@ -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
|
|
8
|
-
const
|
|
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
|
|
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,
|
|
18
|
-
|
|
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[];
|