@mingyuxu/core 0.1.0

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.
Files changed (66) hide show
  1. package/README.md +7 -0
  2. package/dist/coding/agent.d.ts +64 -0
  3. package/dist/coding/agent.d.ts.map +1 -0
  4. package/dist/coding/agent.js +179 -0
  5. package/dist/coding/agent.js.map +1 -0
  6. package/dist/coding/tools.d.ts +45 -0
  7. package/dist/coding/tools.d.ts.map +1 -0
  8. package/dist/coding/tools.js +327 -0
  9. package/dist/coding/tools.js.map +1 -0
  10. package/dist/commander.d.ts +108 -0
  11. package/dist/commander.d.ts.map +1 -0
  12. package/dist/commander.js +246 -0
  13. package/dist/commander.js.map +1 -0
  14. package/dist/extensions.d.ts +26 -0
  15. package/dist/extensions.d.ts.map +1 -0
  16. package/dist/extensions.js +66 -0
  17. package/dist/extensions.js.map +1 -0
  18. package/dist/goal-loop.d.ts +30 -0
  19. package/dist/goal-loop.d.ts.map +1 -0
  20. package/dist/goal-loop.js +96 -0
  21. package/dist/goal-loop.js.map +1 -0
  22. package/dist/hooks.d.ts +17 -0
  23. package/dist/hooks.d.ts.map +1 -0
  24. package/dist/hooks.js +44 -0
  25. package/dist/hooks.js.map +1 -0
  26. package/dist/index.d.ts +33 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +20 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/loop.d.ts +42 -0
  31. package/dist/loop.d.ts.map +1 -0
  32. package/dist/loop.js +86 -0
  33. package/dist/loop.js.map +1 -0
  34. package/dist/orchestrator.d.ts +48 -0
  35. package/dist/orchestrator.d.ts.map +1 -0
  36. package/dist/orchestrator.js +194 -0
  37. package/dist/orchestrator.js.map +1 -0
  38. package/dist/reconcile.d.ts +5 -0
  39. package/dist/reconcile.d.ts.map +1 -0
  40. package/dist/reconcile.js +28 -0
  41. package/dist/reconcile.js.map +1 -0
  42. package/dist/sandbox.d.ts +14 -0
  43. package/dist/sandbox.d.ts.map +1 -0
  44. package/dist/sandbox.js +36 -0
  45. package/dist/sandbox.js.map +1 -0
  46. package/dist/session.d.ts +43 -0
  47. package/dist/session.d.ts.map +1 -0
  48. package/dist/session.js +104 -0
  49. package/dist/session.js.map +1 -0
  50. package/dist/subagents/dispatch.d.ts +34 -0
  51. package/dist/subagents/dispatch.d.ts.map +1 -0
  52. package/dist/subagents/dispatch.js +80 -0
  53. package/dist/subagents/dispatch.js.map +1 -0
  54. package/dist/task-graph.d.ts +32 -0
  55. package/dist/task-graph.d.ts.map +1 -0
  56. package/dist/task-graph.js +77 -0
  57. package/dist/task-graph.js.map +1 -0
  58. package/dist/tools.d.ts +24 -0
  59. package/dist/tools.d.ts.map +1 -0
  60. package/dist/tools.js +54 -0
  61. package/dist/tools.js.map +1 -0
  62. package/dist/workspace.d.ts +92 -0
  63. package/dist/workspace.d.ts.map +1 -0
  64. package/dist/workspace.js +346 -0
  65. package/dist/workspace.js.map +1 -0
  66. package/package.json +34 -0
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @mingyuxu/core
2
+
3
+ doccheck-agent 的通用 Agent 运行时,提供 Agent Loop、工具调用、审批门、Sandbox、工作空间、任务计划、会话持久化和子 Agent 调度。
4
+
5
+ 该包不包含核查领域知识;核查领域能力由 `@mingyuxu/check` 注入。使用完整产品请安装 [`doccheck-agent`](https://www.npmjs.com/package/doccheck-agent)。
6
+
7
+ 完整文档与源码:[doccheck-agent](https://github.com/Mingyu-Xu-98/doccheck-agent)
@@ -0,0 +1,64 @@
1
+ import type { LLMClient, Message } from "@mingyuxu/ai";
2
+ import { type CodingTool, type ApprovalGate, type ExecFn } from "./tools.js";
3
+ export declare const CODE_AGENT_SYSTEM: string;
4
+ export interface CodeAgentEvent {
5
+ kind: "text" | "tool_start" | "tool_end" | "stop" | "notice";
6
+ text?: string;
7
+ tool?: string;
8
+ args?: Record<string, unknown>;
9
+ result?: string;
10
+ }
11
+ export interface CodeAgentOptions {
12
+ llm: LLMClient;
13
+ root: string;
14
+ approve: ApprovalGate;
15
+ tools?: CodingTool[];
16
+ extraTools?: CodingTool[];
17
+ exec?: ExecFn;
18
+ instructions?: string;
19
+ maxSteps?: number;
20
+ historyPath?: string;
21
+ onEvent?: (e: CodeAgentEvent) => void;
22
+ stream?: boolean;
23
+ workerLlm?: LLMClient;
24
+ maxDepth?: number;
25
+ depth?: number;
26
+ }
27
+ export declare class CodeAgent {
28
+ private llm;
29
+ private root;
30
+ private ctx;
31
+ private toolMap;
32
+ private schemas;
33
+ private instructions;
34
+ private maxSteps;
35
+ private historyPath?;
36
+ private onEvent;
37
+ private stream;
38
+ private opts;
39
+ private depth;
40
+ private maxDepth;
41
+ messages: Message[];
42
+ constructor(opts: CodeAgentOptions);
43
+ /** 一次性 LLM 文本调用(无工具、单轮);优先 worker 模型省钱。 */
44
+ private askModel;
45
+ /** 派一个子 agent(完整工具集、独立历史、共享审批门)跑完子任务,回其结论文本。 */
46
+ private runSubAgent;
47
+ /** 当前挂载的工具名(通用 + 领域),供 UI 展示。 */
48
+ toolNames(): string[];
49
+ /** 把长历史压成一个可恢复摘要;工作产物和工具轨迹仍保留在工作空间磁盘中。 */
50
+ compactHistory(): Promise<{
51
+ before: number;
52
+ after: number;
53
+ compacted: boolean;
54
+ }>;
55
+ private system;
56
+ private save;
57
+ /** 跑一整个用户回合:接一句话 → 自主调工具推进 → 直到模型只说话(收尾)或到步数上限。 */
58
+ turn(userMsg: string): Promise<{
59
+ stop: string;
60
+ steps: number;
61
+ }>;
62
+ private runTool;
63
+ }
64
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/coding/agent.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAc,MAAM,cAAc,CAAC;AAEnE,OAAO,EAA4B,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,MAAM,EAAoB,MAAM,YAAY,CAAC;AAEzH,eAAO,MAAM,iBAAiB,QAOlB,CAAC;AAEb,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,IAAI,CAAmB;IAC/B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,QAAQ,EAAE,OAAO,EAAE,CAAM;gBAEb,IAAI,EAAE,gBAAgB;IAwBlC,2CAA2C;YAC7B,QAAQ;IAStB,gDAAgD;YAClC,WAAW;IAiBzB,iCAAiC;IACjC,SAAS,IAAI,MAAM,EAAE;IAIrB,0CAA0C;IACpC,cAAc,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAoBtF,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,IAAI;IAMZ,mDAAmD;IAC7C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;YA4CvD,OAAO;CAStB"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * CodeAgent —— Claude-Code 式通用对话循环。
3
+ * 维护 messages[],每轮 llm.chat(messages, {tools}):模型要调工具就执行(写/bash 过审批)
4
+ * 把回执喂回再循环;只说话就流式输出、结束回合。会话历史可落盘,"继续"即恢复。
5
+ *
6
+ * core 不认识核查:领域能力(build/run/eval…)由 CLI 作为 extraTools 注入,与通用工具同槽。
7
+ */
8
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
9
+ import { dirname } from "node:path";
10
+ import { BudgetExceeded, LLMError } from "@mingyuxu/ai";
11
+ import { Sandbox } from "../sandbox.js";
12
+ import { codingTools, defaultExec } from "./tools.js";
13
+ export const CODE_AGENT_SYSTEM = [
14
+ "你是运行在用户仓库里的通用编码 agent(类似 Claude Code),用中文对话。",
15
+ "你能自由对话,也能用工具动手:读写/编辑文件、搜索、跑 shell 命令。需要时就调工具,不要凭空猜文件内容。",
16
+ "写文件、编辑、跑命令是危险动作,会经过人工审批门——被拒就换方案或问用户,别硬来。",
17
+ "动手前先简短说明你要做什么;完成后向用户总结结果。多步任务自己规划、连续调工具推进,直到目标达成再收尾。",
18
+ "需要额外 LLM 算力时按难度选:简单纯文本活(总结/分类/起草/抽取)用 ask_model 一次搞定;较复杂、要多步动手或想隔离的活用 spawn_agent 派子 agent。琐碎的活自己做,别为了用而用。",
19
+ "不清楚需求、或关键决策该用户拍板时,直接发问,不要自作主张做不可逆的事。",
20
+ ].join("\n");
21
+ export class CodeAgent {
22
+ llm;
23
+ root;
24
+ ctx;
25
+ toolMap;
26
+ schemas;
27
+ instructions;
28
+ maxSteps;
29
+ historyPath;
30
+ onEvent;
31
+ stream;
32
+ opts; // 存构造参数,用于派生子 agent
33
+ depth;
34
+ maxDepth;
35
+ messages = [];
36
+ constructor(opts) {
37
+ this.opts = opts;
38
+ this.llm = opts.llm;
39
+ this.root = opts.root;
40
+ this.instructions = opts.instructions ?? "";
41
+ this.maxSteps = opts.maxSteps ?? 40;
42
+ this.historyPath = opts.historyPath;
43
+ this.onEvent = opts.onEvent ?? (() => { });
44
+ this.stream = opts.stream ?? true;
45
+ this.depth = opts.depth ?? 0;
46
+ this.maxDepth = opts.maxDepth ?? 2;
47
+ const tools = [...(opts.tools ?? codingTools()), ...(opts.extraTools ?? [])];
48
+ this.toolMap = new Map(tools.map((t) => [t.schema.name, t]));
49
+ this.schemas = tools.map((t) => t.schema);
50
+ this.ctx = {
51
+ root: opts.root, sandbox: new Sandbox(opts.root), approve: opts.approve, exec: opts.exec ?? defaultExec,
52
+ askModel: (prompt, system) => this.askModel(prompt, system),
53
+ spawnAgent: this.depth < this.maxDepth ? (task) => this.runSubAgent(task) : undefined,
54
+ };
55
+ if (this.historyPath && existsSync(this.historyPath)) {
56
+ try {
57
+ this.messages = JSON.parse(readFileSync(this.historyPath, "utf-8"));
58
+ }
59
+ catch {
60
+ this.messages = [];
61
+ }
62
+ }
63
+ }
64
+ /** 一次性 LLM 文本调用(无工具、单轮);优先 worker 模型省钱。 */
65
+ async askModel(prompt, system) {
66
+ const client = this.opts.workerLlm ?? this.llm;
67
+ const msgs = [];
68
+ if (system)
69
+ msgs.push({ role: "system", content: system });
70
+ msgs.push({ role: "user", content: prompt });
71
+ const res = await client.chat(msgs); // 不传 tools / onDelta → 纯文本
72
+ return res.text;
73
+ }
74
+ /** 派一个子 agent(完整工具集、独立历史、共享审批门)跑完子任务,回其结论文本。 */
75
+ async runSubAgent(task) {
76
+ const sub = new CodeAgent({
77
+ ...this.opts,
78
+ historyPath: undefined, // 子 agent 独立上下文,不污染主会话
79
+ stream: false,
80
+ depth: this.depth + 1,
81
+ onEvent: (e) => {
82
+ if (e.kind === "tool_start")
83
+ this.onEvent({ ...e, tool: `▸${e.tool}` });
84
+ else if (e.kind === "tool_end")
85
+ this.onEvent(e);
86
+ else if (e.kind === "notice")
87
+ this.onEvent(e);
88
+ },
89
+ });
90
+ const r = await sub.turn(task);
91
+ const last = [...sub.messages].reverse().find((m) => m.role === "assistant" && m.content);
92
+ return last?.content || `(子 agent 结束于 ${r.stop},未产出文本结论)`;
93
+ }
94
+ /** 当前挂载的工具名(通用 + 领域),供 UI 展示。 */
95
+ toolNames() {
96
+ return [...this.toolMap.keys()];
97
+ }
98
+ /** 把长历史压成一个可恢复摘要;工作产物和工具轨迹仍保留在工作空间磁盘中。 */
99
+ async compactHistory() {
100
+ const before = this.messages.length;
101
+ if (before < 6)
102
+ return { before, after: before, compacted: false };
103
+ const source = this.messages.map((m) => {
104
+ const calls = m.role === "assistant" ? m.toolCalls : undefined;
105
+ const tools = calls?.length ? ` [工具: ${calls.map((t) => t.name).join(", ")}]` : "";
106
+ return `${m.role}${tools}: ${m.content}`;
107
+ }).join("\n");
108
+ const summary = await this.askModel(`请压缩下面的 Agent 会话。必须保留:用户最终目标、验收标准、已确认决策、已改文件、工具结果、失败原因、未完成事项、审批状态。不要补造事实。\n\n${source}`, "你是会话压缩器,只输出供另一个 Agent 无损续接工作的中文结构化摘要。");
109
+ this.messages = [
110
+ { role: "user", content: `【此前会话已压缩,以下是可信续接上下文】\n${summary}` },
111
+ { role: "assistant", content: "已读取续接上下文,将基于该目标、决策、磁盘产物和未完成事项继续工作。" },
112
+ ];
113
+ this.save();
114
+ return { before, after: this.messages.length, compacted: true };
115
+ }
116
+ system() {
117
+ return this.instructions ? `${this.instructions}\n\n———\n${CODE_AGENT_SYSTEM}` : CODE_AGENT_SYSTEM;
118
+ }
119
+ save() {
120
+ if (!this.historyPath)
121
+ return;
122
+ mkdirSync(dirname(this.historyPath), { recursive: true });
123
+ writeFileSync(this.historyPath, JSON.stringify(this.messages, null, 2), "utf-8");
124
+ }
125
+ /** 跑一整个用户回合:接一句话 → 自主调工具推进 → 直到模型只说话(收尾)或到步数上限。 */
126
+ async turn(userMsg) {
127
+ this.messages.push({ role: "user", content: userMsg });
128
+ const convo = () => [{ role: "system", content: this.system() }, ...this.messages];
129
+ for (let step = 0; step < this.maxSteps; step++) {
130
+ let res;
131
+ try {
132
+ res = await this.llm.chat(convo(), {
133
+ tools: this.schemas,
134
+ onDelta: this.stream ? (t) => this.onEvent({ kind: "text", text: t }) : undefined,
135
+ onNotice: (m) => this.onEvent({ kind: "notice", text: m }),
136
+ });
137
+ }
138
+ catch (e) {
139
+ const reason = e instanceof BudgetExceeded ? "budget_exceeded" : e instanceof LLMError ? "llm_error" : "chat_error";
140
+ this.onEvent({ kind: "stop", text: `${reason}: ${e.message}` });
141
+ this.save();
142
+ return { stop: reason, steps: step };
143
+ }
144
+ if (!this.stream && res.text)
145
+ this.onEvent({ kind: "text", text: res.text });
146
+ // 无工具调用 → 本回合收尾
147
+ if (!res.toolCalls.length) {
148
+ this.messages.push({ role: "assistant", content: res.text });
149
+ this.save();
150
+ this.onEvent({ kind: "stop", text: "reply" });
151
+ return { stop: "reply", steps: step + 1 };
152
+ }
153
+ // 有工具调用 → 记助手消息(含 tool_calls),逐个执行并把回执喂回
154
+ this.messages.push({ role: "assistant", content: res.text, toolCalls: res.toolCalls });
155
+ for (const call of res.toolCalls) {
156
+ this.onEvent({ kind: "tool_start", tool: call.name, args: call.arguments });
157
+ const result = await this.runTool(call.name, call.arguments);
158
+ this.onEvent({ kind: "tool_end", tool: call.name, args: call.arguments, result });
159
+ this.messages.push({ role: "tool", toolCallId: call.id, content: result });
160
+ }
161
+ this.save();
162
+ }
163
+ this.onEvent({ kind: "stop", text: "max_steps" });
164
+ this.save();
165
+ return { stop: "max_steps", steps: this.maxSteps };
166
+ }
167
+ async runTool(name, args) {
168
+ const tool = this.toolMap.get(name);
169
+ if (!tool)
170
+ return `未知工具:${name}(可用:${[...this.toolMap.keys()].join(", ")})`;
171
+ try {
172
+ return await tool.run(args, this.ctx);
173
+ }
174
+ catch (e) {
175
+ return `工具 ${name} 失败:${e.name}: ${e.message}`;
176
+ }
177
+ }
178
+ }
179
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/coding/agent.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAqE,MAAM,YAAY,CAAC;AAEzH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,8CAA8C;IAC9C,yDAAyD;IACzD,2CAA2C;IAC3C,sDAAsD;IACtD,4GAA4G;IAC5G,sCAAsC;CACvC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AA2Bb,MAAM,OAAO,SAAS;IACZ,GAAG,CAAY;IACf,IAAI,CAAS;IACb,GAAG,CAAc;IACjB,OAAO,CAA0B;IACjC,OAAO,CAAe;IACtB,YAAY,CAAS;IACrB,QAAQ,CAAS;IACjB,WAAW,CAAU;IACrB,OAAO,CAA8B;IACrC,MAAM,CAAU;IAChB,IAAI,CAAmB,CAAM,oBAAoB;IACjD,KAAK,CAAS;IACd,QAAQ,CAAS;IACzB,QAAQ,GAAc,EAAE,CAAC;IAEzB,YAAY,IAAsB;QAChC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;QACpC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnC,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,WAAW,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7D,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG,GAAG;YACT,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,WAAW;YACvG,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;YAC3D,UAAU,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SACtF,CAAC;QACF,IAAI,IAAI,CAAC,WAAW,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACrD,IAAI,CAAC;gBAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;YAAC,CAAC;QAC5G,CAAC;IACH,CAAC;IAED,2CAA2C;IACnC,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,MAAe;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC;QAC/C,MAAM,IAAI,GAAc,EAAE,CAAC;QAC3B,IAAI,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAU,2BAA2B;QACzE,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IAED,gDAAgD;IACxC,KAAK,CAAC,WAAW,CAAC,IAAY;QACpC,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC;YACxB,GAAG,IAAI,CAAC,IAAI;YACZ,WAAW,EAAE,SAAS,EAAsB,uBAAuB;YACnE,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC;YACrB,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;gBACb,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY;oBAAE,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;qBACnE,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU;oBAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;qBAC3C,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;oBAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAChD,CAAC;SACF,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;QAC1F,OAAO,IAAI,EAAE,OAAO,IAAI,gBAAgB,CAAC,CAAC,IAAI,WAAW,CAAC;IAC5D,CAAC;IAED,iCAAiC;IACjC,SAAS;QACP,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;IAED,0CAA0C;IAC1C,KAAK,CAAC,cAAc;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QACpC,IAAI,MAAM,GAAG,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACnE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACrC,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;YAC/D,MAAM,KAAK,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACnF,OAAO,GAAG,CAAC,CAAC,IAAI,GAAG,KAAK,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;QAC3C,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CACjC,+EAA+E,MAAM,EAAE,EACvF,uCAAuC,CACxC,CAAC;QACF,IAAI,CAAC,QAAQ,GAAG;YACd,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,OAAO,EAAE,EAAE;YAC7D,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,oCAAoC,EAAE;SACrE,CAAC;QACF,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAClE,CAAC;IAEO,MAAM;QACZ,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,YAAY,iBAAiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC;IACrG,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACnF,CAAC;IAED,mDAAmD;IACnD,KAAK,CAAC,IAAI,CAAC,OAAe;QACxB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,GAAc,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE9F,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;YAChD,IAAI,GAAG,CAAC;YACR,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;oBACjC,KAAK,EAAE,IAAI,CAAC,OAAO;oBACnB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;oBACjF,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;iBAC3D,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,MAAM,GAAG,CAAC,YAAY,cAAc,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,YAAY,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC;gBACpH,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,KAAM,CAAW,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAC3E,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YACvC,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI;gBAAE,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;YAE7E,gBAAgB;YAChB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;gBAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC7D,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;gBAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;YAC5C,CAAC;YAED,yCAAyC;YACzC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;YACvF,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;gBACjC,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC5E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC7D,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;gBAClF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YAC7E,CAAC;YACD,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,IAA6B;QAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI;YAAE,OAAO,QAAQ,IAAI,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAC5E,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,MAAM,IAAI,OAAQ,CAAW,CAAC,IAAI,KAAM,CAAW,CAAC,OAAO,EAAE,CAAC;QACvE,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,45 @@
1
+ import type { ToolSchema } from "@mingyuxu/ai";
2
+ import { Sandbox } from "../sandbox.js";
3
+ /** 审批门:mutating 工具执行前询问;返回 false 则拒绝执行。TUI 弹 y/n,headless 按策略。 */
4
+ export type ApprovalGate = (req: {
5
+ tool: string;
6
+ summary: string;
7
+ args: Record<string, unknown>;
8
+ }) => boolean | Promise<boolean>;
9
+ /** shell 执行器(可注入,测试用桩)。返回合并输出与退出码。 */
10
+ export type ExecFn = (command: string, opts: {
11
+ cwd: string;
12
+ timeoutMs: number;
13
+ }) => Promise<{
14
+ output: string;
15
+ code: number;
16
+ }>;
17
+ export interface ToolContext {
18
+ root: string;
19
+ sandbox: Sandbox;
20
+ approve: ApprovalGate;
21
+ exec: ExecFn;
22
+ /** 一次性 LLM 文本子调用(ask_model 用)。由 CodeAgent 注入;未注入则工具报"未启用"。 */
23
+ askModel?: (prompt: string, system?: string) => Promise<string>;
24
+ /** 派子 agent 跑一个多步子任务,返回结论(spawn_agent 用)。由 CodeAgent 注入。 */
25
+ spawnAgent?: (task: string) => Promise<string>;
26
+ }
27
+ export interface CodingTool {
28
+ schema: ToolSchema;
29
+ mutating: boolean;
30
+ run(args: Record<string, unknown>, ctx: ToolContext): Promise<string>;
31
+ }
32
+ export declare const READ_FILE: CodingTool;
33
+ export declare const LIST_DIR: CodingTool;
34
+ export declare const GLOB: CodingTool;
35
+ export declare const GREP: CodingTool;
36
+ export declare const WRITE_FILE: CodingTool;
37
+ export declare const EDIT_FILE: CodingTool;
38
+ export declare const BASH: CodingTool;
39
+ export declare const ASK_MODEL: CodingTool;
40
+ export declare const SPAWN_AGENT: CodingTool;
41
+ /** 全部通用编码工具(含 LLM 子调用 ask_model / spawn_agent)。 */
42
+ export declare function codingTools(): CodingTool[];
43
+ /** 默认 shell 执行器:/bin/sh -c,合并 stdout/stderr。 */
44
+ export declare const defaultExec: ExecFn;
45
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/coding/tools.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAoB,MAAM,eAAe,CAAC;AAE1D,kEAAkE;AAClE,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjI,sCAAsC;AACtC,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE9H,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,YAAY,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChE,4DAA4D;IAC5D,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACvE;AA8CD,eAAO,MAAM,SAAS,EAAE,UAyBvB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,UAiBtB,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,UAclB,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,UAiClB,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,UAsBxB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,UAiCvB,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,UAqBlB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,UAmBvB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,UAkBzB,CAAC;AAEF,mDAAmD;AACnD,wBAAgB,WAAW,IAAI,UAAU,EAAE,CAE1C;AAED,gDAAgD;AAChD,eAAO,MAAM,WAAW,EAAE,MAOtB,CAAC"}
@@ -0,0 +1,327 @@
1
+ /**
2
+ * 通用编码工具集(Claude-Code 式)—— 让通用 agent 能读/写/编辑文件、搜索、跑命令。
3
+ * 读类自动放行;写/编辑/bash 是 mutating,执行前过 ApprovalGate(人工审批门)。
4
+ * 所有文件路径经 Sandbox 约束在工作根内,越界 fail-closed。bash 是唯一逃逸口(仍需审批)。
5
+ */
6
+ import { readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, existsSync } from "node:fs";
7
+ import { resolve, relative, join, dirname, isAbsolute } from "node:path";
8
+ import { execFile } from "node:child_process";
9
+ import { SandboxViolation } from "../sandbox.js";
10
+ const MAX_OUTPUT = 30_000; // 单次工具结果上限,护住 agent 上下文
11
+ const SKIP_DIRS = new Set([".git", "node_modules", "dist", ".vet", ".session"]);
12
+ function clip(s) {
13
+ return s.length > MAX_OUTPUT ? s.slice(0, MAX_OUTPUT) + `\n…(已截断,共 ${s.length} 字)` : s;
14
+ }
15
+ /** 解析并校验路径在工作根内,返回绝对路径;越界抛 SandboxViolation。 */
16
+ function within(ctx, path) {
17
+ const abs = isAbsolute(path) ? resolve(path) : resolve(ctx.root, path);
18
+ if (!ctx.sandbox.contains(abs))
19
+ throw new SandboxViolation(`路径越界:${abs} 不在工作根 ${ctx.root} 内`);
20
+ return abs;
21
+ }
22
+ function walk(dir, root, out, cap) {
23
+ if (out.length >= cap)
24
+ return;
25
+ let entries;
26
+ try {
27
+ entries = readdirSync(dir);
28
+ }
29
+ catch {
30
+ return;
31
+ }
32
+ for (const name of entries) {
33
+ if (SKIP_DIRS.has(name))
34
+ continue;
35
+ const full = join(dir, name);
36
+ let st;
37
+ try {
38
+ st = statSync(full);
39
+ }
40
+ catch {
41
+ continue;
42
+ }
43
+ if (st.isDirectory())
44
+ walk(full, root, out, cap);
45
+ else
46
+ out.push(relative(root, full));
47
+ if (out.length >= cap)
48
+ return;
49
+ }
50
+ }
51
+ /** glob 通配 → 正则(支持 ** / * / ?)。 */
52
+ function globToRe(glob) {
53
+ let re = "";
54
+ for (let i = 0; i < glob.length; i++) {
55
+ const c = glob[i];
56
+ if (c === "*") {
57
+ if (glob[i + 1] === "*") {
58
+ re += ".*";
59
+ i++;
60
+ if (glob[i + 1] === "/")
61
+ i++;
62
+ }
63
+ else
64
+ re += "[^/]*";
65
+ }
66
+ else if (c === "?")
67
+ re += "[^/]";
68
+ else if (".+^${}()|[]\\".includes(c))
69
+ re += "\\" + c;
70
+ else
71
+ re += c;
72
+ }
73
+ return new RegExp("^" + re + "$");
74
+ }
75
+ export const READ_FILE = {
76
+ mutating: false,
77
+ schema: {
78
+ name: "read_file",
79
+ description: "读取工作根内某个文件的内容。可选 offset/limit 按行截取。",
80
+ parameters: {
81
+ type: "object",
82
+ properties: {
83
+ path: { type: "string", description: "相对工作根或绝对路径(须在工作根内)" },
84
+ offset: { type: "integer", description: "起始行(1 基),可选" },
85
+ limit: { type: "integer", description: "读取行数,可选" },
86
+ },
87
+ required: ["path"],
88
+ },
89
+ },
90
+ async run(args, ctx) {
91
+ const abs = within(ctx, String(args.path));
92
+ if (!existsSync(abs))
93
+ return `文件不存在:${args.path}`;
94
+ const raw = readFileSync(abs, "utf-8");
95
+ const lines = raw.split("\n");
96
+ const off = args.offset ? Math.max(1, Number(args.offset)) : 1;
97
+ const lim = args.limit ? Number(args.limit) : lines.length;
98
+ const slice = lines.slice(off - 1, off - 1 + lim);
99
+ return clip(slice.map((l, i) => `${off + i}\t${l}`).join("\n"));
100
+ },
101
+ };
102
+ export const LIST_DIR = {
103
+ mutating: false,
104
+ schema: {
105
+ name: "list_dir",
106
+ description: "列出目录下的条目(不递归)。",
107
+ parameters: { type: "object", properties: { path: { type: "string", description: "目录路径,默认工作根" } } },
108
+ },
109
+ async run(args, ctx) {
110
+ const abs = within(ctx, String(args.path ?? "."));
111
+ if (!existsSync(abs))
112
+ return `目录不存在:${args.path ?? "."}`;
113
+ const entries = readdirSync(abs).map((n) => {
114
+ let dir = false;
115
+ try {
116
+ dir = statSync(join(abs, n)).isDirectory();
117
+ }
118
+ catch { /* ignore */ }
119
+ return dir ? n + "/" : n;
120
+ });
121
+ return clip(entries.sort().join("\n") || "(空目录)");
122
+ },
123
+ };
124
+ export const GLOB = {
125
+ mutating: false,
126
+ schema: {
127
+ name: "glob",
128
+ description: "按通配模式查找文件(支持 ** / * / ?),返回相对路径列表。",
129
+ parameters: { type: "object", properties: { pattern: { type: "string", description: "如 src/**/*.ts" } }, required: ["pattern"] },
130
+ },
131
+ async run(args, ctx) {
132
+ const re = globToRe(String(args.pattern));
133
+ const all = [];
134
+ walk(ctx.root, ctx.root, all, 5000);
135
+ const hits = all.filter((p) => re.test(p)).slice(0, 500);
136
+ return clip(hits.join("\n") || "(无匹配)");
137
+ },
138
+ };
139
+ export const GREP = {
140
+ mutating: false,
141
+ schema: {
142
+ name: "grep",
143
+ description: "在文件内容里正则搜索,返回 文件:行号:内容。可选 glob 限定文件范围。",
144
+ parameters: {
145
+ type: "object",
146
+ properties: {
147
+ pattern: { type: "string", description: "正则表达式" },
148
+ glob: { type: "string", description: "限定文件的通配,如 *.ts,可选" },
149
+ },
150
+ required: ["pattern"],
151
+ },
152
+ },
153
+ async run(args, ctx) {
154
+ let re;
155
+ try {
156
+ re = new RegExp(String(args.pattern));
157
+ }
158
+ catch (e) {
159
+ return `非法正则:${e.message}`;
160
+ }
161
+ const fileRe = args.glob ? globToRe(String(args.glob).includes("/") ? String(args.glob) : "**/" + String(args.glob)) : null;
162
+ const files = [];
163
+ walk(ctx.root, ctx.root, files, 5000);
164
+ const out = [];
165
+ for (const rel of files) {
166
+ if (fileRe && !fileRe.test(rel))
167
+ continue;
168
+ let text;
169
+ try {
170
+ text = readFileSync(join(ctx.root, rel), "utf-8");
171
+ }
172
+ catch {
173
+ continue;
174
+ }
175
+ const lines = text.split("\n");
176
+ for (let i = 0; i < lines.length; i++) {
177
+ if (re.test(lines[i])) {
178
+ out.push(`${rel}:${i + 1}:${lines[i].slice(0, 200)}`);
179
+ if (out.length >= 200)
180
+ break;
181
+ }
182
+ }
183
+ if (out.length >= 200)
184
+ break;
185
+ }
186
+ return clip(out.join("\n") || "(无匹配)");
187
+ },
188
+ };
189
+ export const WRITE_FILE = {
190
+ mutating: true,
191
+ schema: {
192
+ name: "write_file",
193
+ description: "写入(覆盖)工作根内的文件,自动建目录。危险操作,执行前需人工审批。",
194
+ parameters: {
195
+ type: "object",
196
+ properties: { path: { type: "string" }, content: { type: "string" } },
197
+ required: ["path", "content"],
198
+ },
199
+ },
200
+ async run(args, ctx) {
201
+ const path = String(args.path);
202
+ const content = String(args.content ?? "");
203
+ const abs = within(ctx, path);
204
+ const existed = existsSync(abs);
205
+ const ok = await ctx.approve({ tool: "write_file", summary: `${existed ? "覆盖" : "新建"} ${path}(${content.length} 字)`, args });
206
+ if (!ok)
207
+ return `用户拒绝了写入 ${path}`;
208
+ mkdirSync(dirname(abs), { recursive: true });
209
+ writeFileSync(abs, content, "utf-8");
210
+ return `✓ 已${existed ? "覆盖" : "写入"} ${path}(${content.length} 字)`;
211
+ },
212
+ };
213
+ export const EDIT_FILE = {
214
+ mutating: true,
215
+ schema: {
216
+ name: "edit_file",
217
+ description: "把文件里的 old 文本替换为 new。默认要求 old 唯一匹配;replace_all=true 则全替。执行前需人工审批。",
218
+ parameters: {
219
+ type: "object",
220
+ properties: {
221
+ path: { type: "string" },
222
+ old: { type: "string", description: "要替换的原文(含足够上下文以唯一定位)" },
223
+ new: { type: "string", description: "替换后的新文本" },
224
+ replace_all: { type: "boolean", description: "是否全部替换,默认 false" },
225
+ },
226
+ required: ["path", "old", "new"],
227
+ },
228
+ },
229
+ async run(args, ctx) {
230
+ const path = String(args.path);
231
+ const abs = within(ctx, path);
232
+ if (!existsSync(abs))
233
+ return `文件不存在:${path}`;
234
+ const oldStr = String(args.old);
235
+ const newStr = String(args.new ?? "");
236
+ const all = Boolean(args.replace_all);
237
+ const raw = readFileSync(abs, "utf-8");
238
+ const count = oldStr ? raw.split(oldStr).length - 1 : 0;
239
+ if (count === 0)
240
+ return `未找到要替换的文本,未改动 ${path}`;
241
+ if (count > 1 && !all)
242
+ return `old 文本在 ${path} 命中 ${count} 处,非唯一。补足上下文或设 replace_all=true。`;
243
+ const ok = await ctx.approve({ tool: "edit_file", summary: `编辑 ${path}(替换 ${all ? count : 1} 处)`, args });
244
+ if (!ok)
245
+ return `用户拒绝了编辑 ${path}`;
246
+ const next = all ? raw.split(oldStr).join(newStr) : raw.replace(oldStr, newStr);
247
+ writeFileSync(abs, next, "utf-8");
248
+ return `✓ 已编辑 ${path}(替换 ${all ? count : 1} 处)`;
249
+ },
250
+ };
251
+ export const BASH = {
252
+ mutating: true,
253
+ schema: {
254
+ name: "bash",
255
+ description: "在工作根内执行一条 shell 命令并返回输出。执行前需人工审批。",
256
+ parameters: {
257
+ type: "object",
258
+ properties: {
259
+ command: { type: "string" },
260
+ timeout_ms: { type: "integer", description: "超时毫秒,默认 120000" },
261
+ },
262
+ required: ["command"],
263
+ },
264
+ },
265
+ async run(args, ctx) {
266
+ const command = String(args.command);
267
+ const ok = await ctx.approve({ tool: "bash", summary: command.slice(0, 200), args });
268
+ if (!ok)
269
+ return `用户拒绝了执行:${command}`;
270
+ const { output, code } = await ctx.exec(command, { cwd: ctx.root, timeoutMs: args.timeout_ms ? Number(args.timeout_ms) : 120_000 });
271
+ return clip(`[退出码 ${code}]\n${output}`);
272
+ },
273
+ };
274
+ export const ASK_MODEL = {
275
+ mutating: false,
276
+ schema: {
277
+ name: "ask_model",
278
+ description: "一次性调用 LLM 完成一个纯文本子任务(总结/分类/起草/翻译/抽取等),无工具、不读写文件、不多轮。适合简单、独立、给定输入就能出答案的思考活;默认走更省的 worker 模型。复杂或需要动手的活用 spawn_agent。",
279
+ parameters: {
280
+ type: "object",
281
+ properties: {
282
+ prompt: { type: "string", description: "交给模型的完整输入(把要处理的内容也带上)" },
283
+ system: { type: "string", description: "可选的系统指令(角色/输出格式约束)" },
284
+ },
285
+ required: ["prompt"],
286
+ },
287
+ },
288
+ async run(args, ctx) {
289
+ if (!ctx.askModel)
290
+ return "未启用 LLM 子调用(ask_model 需由 CodeAgent 注入)。";
291
+ const out = await ctx.askModel(String(args.prompt), args.system ? String(args.system) : undefined);
292
+ return clip(out || "(模型无输出)");
293
+ },
294
+ };
295
+ export const SPAWN_AGENT = {
296
+ mutating: false, // 委派本身不改文件;子 agent 的写/命令仍逐个走同一审批门
297
+ schema: {
298
+ name: "spawn_agent",
299
+ description: "派一个子 agent 独立完成一个多步子任务(它有完整工具集:读写/编辑/搜索/跑命令/再委派),跑完只把结论回给你。适合较复杂、需要动手、可隔离的活,能保持你自己的上下文干净。子 agent 的危险操作仍会向用户请求审批。简单纯文本活请用 ask_model。",
300
+ parameters: {
301
+ type: "object",
302
+ properties: {
303
+ task: { type: "string", description: "子任务的完整描述:要做什么、约束、期望产出。子 agent 看不到你当前对话,务必自足。" },
304
+ },
305
+ required: ["task"],
306
+ },
307
+ },
308
+ async run(args, ctx) {
309
+ if (!ctx.spawnAgent)
310
+ return "未启用子 agent(spawn_agent 需由 CodeAgent 注入,或已达嵌套深度上限)。";
311
+ const out = await ctx.spawnAgent(String(args.task));
312
+ return clip(out || "(子 agent 未产出结论)");
313
+ },
314
+ };
315
+ /** 全部通用编码工具(含 LLM 子调用 ask_model / spawn_agent)。 */
316
+ export function codingTools() {
317
+ return [READ_FILE, LIST_DIR, GLOB, GREP, WRITE_FILE, EDIT_FILE, BASH, ASK_MODEL, SPAWN_AGENT];
318
+ }
319
+ /** 默认 shell 执行器:/bin/sh -c,合并 stdout/stderr。 */
320
+ export const defaultExec = (command, opts) => new Promise((res) => {
321
+ execFile("/bin/sh", ["-c", command], { cwd: opts.cwd, timeout: opts.timeoutMs, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
322
+ const output = (stdout ?? "") + (stderr ? (stdout ? "\n" : "") + stderr : "");
323
+ const code = err && typeof err.code === "number" ? err.code : err ? 1 : 0;
324
+ res({ output: output || "(无输出)", code });
325
+ });
326
+ });
327
+ //# sourceMappingURL=tools.js.map