@ayoxx/kundex 2.0.4 → 2.0.6
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/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/repl.ts +21 -7
- package/src/sdk.ts +14 -14
- package/src/tools.ts +224 -2
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/src/repl.ts
CHANGED
|
@@ -161,11 +161,19 @@ ${c.bold}Commands:${c.reset}
|
|
|
161
161
|
/clear Start a fresh agent session
|
|
162
162
|
/exit Quit kundex
|
|
163
163
|
|
|
164
|
+
${c.bold}Capabilities:${c.reset}
|
|
165
|
+
• Read, write, edit, and create files
|
|
166
|
+
• Run shell commands (npm, git, python, make, etc.)
|
|
167
|
+
• Search through codebases
|
|
168
|
+
• Search the web for documentation and solutions
|
|
169
|
+
• Fetch and read web pages
|
|
170
|
+
• Initialize git repos and make commits
|
|
171
|
+
• Install packages and dependencies
|
|
172
|
+
|
|
164
173
|
${c.bold}Tips:${c.reset}
|
|
165
|
-
•
|
|
166
|
-
•
|
|
167
|
-
• Responses stream in real-time
|
|
168
|
-
• Code blocks are syntax-highlighted in the terminal.
|
|
174
|
+
• Code blocks are syntax-highlighted automatically.
|
|
175
|
+
• The agent will ask for confirmation before writes and shell commands.
|
|
176
|
+
• Responses stream in real-time.
|
|
169
177
|
• Press Ctrl+C to abort a running request.
|
|
170
178
|
`;
|
|
171
179
|
|
|
@@ -245,10 +253,13 @@ async function handleTurn(
|
|
|
245
253
|
}
|
|
246
254
|
}
|
|
247
255
|
|
|
248
|
-
if (current.reply && !turn.toolCalls?.length) {
|
|
256
|
+
if (current.reply && current.reply.trim() && !turn.toolCalls?.length) {
|
|
249
257
|
process.stdout.write(`\n${c.dim}${"─".repeat(60)}${c.reset}\n\n`);
|
|
250
|
-
} else if (current.reply) {
|
|
258
|
+
} else if (current.reply && current.reply.trim()) {
|
|
251
259
|
printReply(current.reply);
|
|
260
|
+
} else if (turn.toolCalls?.length) {
|
|
261
|
+
// After tool calls, if no text was returned, just print the separator
|
|
262
|
+
process.stdout.write(`\n${c.dim}${"─".repeat(60)}${c.reset}\n\n`);
|
|
252
263
|
}
|
|
253
264
|
}
|
|
254
265
|
|
|
@@ -351,8 +362,11 @@ export async function runRepl(config: KundexConfig, apiKey: string, baseUrl: str
|
|
|
351
362
|
await handleTurn(kundex, cwd, session.id, turn, signal);
|
|
352
363
|
} else if (streamedAnyText) {
|
|
353
364
|
process.stdout.write(`\n${c.dim}${"─".repeat(60)}${c.reset}\n\n`);
|
|
354
|
-
} else if (turn.reply) {
|
|
365
|
+
} else if (turn.reply && turn.reply.trim()) {
|
|
355
366
|
printReply(turn.reply);
|
|
367
|
+
} else {
|
|
368
|
+
// Empty response — show a message so user knows something happened
|
|
369
|
+
process.stdout.write(`\n${c.dim} (no response — try rephrasing your message)${c.reset}\n\n`);
|
|
356
370
|
}
|
|
357
371
|
} catch (err) {
|
|
358
372
|
spinner.clear();
|
package/src/sdk.ts
CHANGED
|
@@ -133,7 +133,7 @@ export class Kundex {
|
|
|
133
133
|
|
|
134
134
|
models = {
|
|
135
135
|
list: async (): Promise<ModelInfo[]> => {
|
|
136
|
-
const res = await fetch(`${this.baseUrl}/v1/models`, {
|
|
136
|
+
const res = await fetch(`${this.baseUrl}/api/v1/models`, {
|
|
137
137
|
headers: { authorization: `Bearer ${this.apiKey}` },
|
|
138
138
|
});
|
|
139
139
|
if (!res.ok) throw new Error(`Kundex API error (${res.status})`);
|
|
@@ -155,7 +155,7 @@ export class Kundex {
|
|
|
155
155
|
message: KundexChatMessage;
|
|
156
156
|
finishReason: string;
|
|
157
157
|
usage: ChatUsage;
|
|
158
|
-
}>("/v1/chat/completions", { ...params, stream: false }),
|
|
158
|
+
}>("/api/v1/chat/completions", { ...params, stream: false }),
|
|
159
159
|
|
|
160
160
|
/** Streaming chat completion; invokes onDelta as tokens arrive. */
|
|
161
161
|
stream: async (
|
|
@@ -168,7 +168,7 @@ export class Kundex {
|
|
|
168
168
|
onDelta: (text: string) => void,
|
|
169
169
|
signal?: AbortSignal,
|
|
170
170
|
): Promise<{ finishReason: string; usage: ChatUsage | null }> => {
|
|
171
|
-
const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
171
|
+
const res = await fetch(`${this.baseUrl}/api/v1/chat/completions`, {
|
|
172
172
|
method: "POST",
|
|
173
173
|
headers: {
|
|
174
174
|
"content-type": "application/json",
|
|
@@ -229,19 +229,19 @@ export class Kundex {
|
|
|
229
229
|
description?: string;
|
|
230
230
|
language?: string;
|
|
231
231
|
}) =>
|
|
232
|
-
this.request<AgentSession>("/v1/agent/session", params),
|
|
232
|
+
this.request<AgentSession>("/api/v1/agent/session", params),
|
|
233
233
|
|
|
234
234
|
listSessions: () =>
|
|
235
|
-
this.requestGet<AgentSession[]>("/v1/agent/sessions"),
|
|
235
|
+
this.requestGet<AgentSession[]>("/api/v1/agent/sessions"),
|
|
236
236
|
|
|
237
237
|
getSession: (id: number) =>
|
|
238
|
-
this.requestGet<AgentSession>(`/v1/agent/sessions/${id}`),
|
|
238
|
+
this.requestGet<AgentSession>(`/api/v1/agent/sessions/${id}`),
|
|
239
239
|
|
|
240
240
|
deleteSession: (id: number) =>
|
|
241
|
-
this.requestDelete(`/v1/agent/sessions/${id}`),
|
|
241
|
+
this.requestDelete(`/api/v1/agent/sessions/${id}`),
|
|
242
242
|
|
|
243
243
|
getMessages: (id: number) =>
|
|
244
|
-
this.requestGet<{ sessionId: number; messages: unknown[] }>(`/v1/agent/sessions/${id}/messages`),
|
|
244
|
+
this.requestGet<{ sessionId: number; messages: unknown[] }>(`/api/v1/agent/sessions/${id}/messages`),
|
|
245
245
|
|
|
246
246
|
/**
|
|
247
247
|
* Send a message to the agent and stream the response.
|
|
@@ -266,7 +266,7 @@ export class Kundex {
|
|
|
266
266
|
onDelta: (text: string) => void,
|
|
267
267
|
signal?: AbortSignal,
|
|
268
268
|
): Promise<AgentTurn> => {
|
|
269
|
-
const endpoint = "/v1/agent/message";
|
|
269
|
+
const endpoint = "/api/v1/agent/message";
|
|
270
270
|
const res = await fetch(`${this.baseUrl}${endpoint}`, {
|
|
271
271
|
method: "POST",
|
|
272
272
|
headers: {
|
|
@@ -365,7 +365,7 @@ export class Kundex {
|
|
|
365
365
|
onDelta: (text: string) => void,
|
|
366
366
|
signal?: AbortSignal,
|
|
367
367
|
): Promise<AgentTurn> => {
|
|
368
|
-
const res = await fetch(`${this.baseUrl}/v1/agent/tool-result`, {
|
|
368
|
+
const res = await fetch(`${this.baseUrl}/api/v1/agent/tool-result`, {
|
|
369
369
|
method: "POST",
|
|
370
370
|
headers: {
|
|
371
371
|
"content-type": "application/json",
|
|
@@ -447,17 +447,17 @@ export class Kundex {
|
|
|
447
447
|
|
|
448
448
|
files = {
|
|
449
449
|
list: (wsId: number) =>
|
|
450
|
-
this.requestGet<{ files: WorkspaceFile[] }>(`/v1/workspace/${wsId}/files`),
|
|
450
|
+
this.requestGet<{ files: WorkspaceFile[] }>(`/api/v1/workspace/${wsId}/files`),
|
|
451
451
|
|
|
452
452
|
get: (wsId: number, path: string) =>
|
|
453
453
|
this.requestGet<WorkspaceFile>(
|
|
454
|
-
`/v1/workspace/${wsId}/files/content?path=${encodeURIComponent(path)}`,
|
|
454
|
+
`/api/v1/workspace/${wsId}/files/content?path=${encodeURIComponent(path)}`,
|
|
455
455
|
),
|
|
456
456
|
|
|
457
457
|
upsert: (wsId: number, params: { path: string; name: string; content: string; mimeType?: string }) =>
|
|
458
|
-
this.request<{ file: WorkspaceFile }>(`/v1/workspace/${wsId}/files`, params),
|
|
458
|
+
this.request<{ file: WorkspaceFile }>(`/api/v1/workspace/${wsId}/files`, params),
|
|
459
459
|
|
|
460
460
|
delete: (wsId: number, path: string) =>
|
|
461
|
-
this.requestDelete(`/v1/workspace/${wsId}/files?path=${encodeURIComponent(path)}`),
|
|
461
|
+
this.requestDelete(`/api/v1/workspace/${wsId}/files?path=${encodeURIComponent(path)}`),
|
|
462
462
|
};
|
|
463
463
|
}
|
package/src/tools.ts
CHANGED
|
@@ -33,6 +33,34 @@ function header(label: string, value: string) {
|
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function printDiff(oldContent: string | null, newContent: string, filePath: string) {
|
|
37
|
+
const oldLines = oldContent ? oldContent.split("\n") : [];
|
|
38
|
+
const newLines = newContent.split("\n");
|
|
39
|
+
const maxLines = Math.max(oldLines.length, newLines.length);
|
|
40
|
+
const LIMIT = 40;
|
|
41
|
+
const shown = Math.min(maxLines, LIMIT);
|
|
42
|
+
|
|
43
|
+
console.log(`${c.bold}${c.white} ┌─ ${filePath} ─────────────────────────${c.reset}`);
|
|
44
|
+
for (let i = 0; i < shown; i++) {
|
|
45
|
+
const o = oldLines[i];
|
|
46
|
+
const n = newLines[i];
|
|
47
|
+
if (o === undefined) {
|
|
48
|
+
process.stdout.write(`${c.green} + ${(i + 1).toString().padStart(4)} ${n ?? ""}${c.reset}\n`);
|
|
49
|
+
} else if (n === undefined) {
|
|
50
|
+
process.stdout.write(`${c.red} - ${(i + 1).toString().padStart(4)} ${o ?? ""}${c.reset}\n`);
|
|
51
|
+
} else if (o !== n) {
|
|
52
|
+
process.stdout.write(`${c.red} - ${(i + 1).toString().padStart(4)} ${o}${c.reset}\n`);
|
|
53
|
+
process.stdout.write(`${c.green} + ${(i + 1).toString().padStart(4)} ${n}${c.reset}\n`);
|
|
54
|
+
} else {
|
|
55
|
+
process.stdout.write(`${c.dim} ${(i + 1).toString().padStart(4)} ${o}${c.reset}\n`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (maxLines > LIMIT) {
|
|
59
|
+
console.log(`${c.dim} ... ${maxLines - LIMIT} more lines omitted${c.reset}`);
|
|
60
|
+
}
|
|
61
|
+
console.log(`${c.bold}${c.white} └──────────────────────────────────────${c.reset}\n`);
|
|
62
|
+
}
|
|
63
|
+
|
|
36
64
|
async function confirm(prompt: string): Promise<boolean> {
|
|
37
65
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
38
66
|
try {
|
|
@@ -51,19 +79,108 @@ function resolveInCwd(cwd: string, relPath: string): string {
|
|
|
51
79
|
return resolved;
|
|
52
80
|
}
|
|
53
81
|
|
|
82
|
+
// ── File tools ────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
54
84
|
async function readFileTool(cwd: string, args: { path: string }): Promise<string> {
|
|
55
85
|
const full = resolveInCwd(cwd, args.path);
|
|
56
86
|
header("read_file", args.path);
|
|
57
87
|
return fs.readFile(full, "utf-8");
|
|
58
88
|
}
|
|
59
89
|
|
|
90
|
+
async function writeFileTool(cwd: string, args: { path: string; content: string }): Promise<string> {
|
|
91
|
+
const full = resolveInCwd(cwd, args.path);
|
|
92
|
+
const lines = args.content.split("\n").length;
|
|
93
|
+
const bytes = Buffer.byteLength(args.content, "utf-8");
|
|
94
|
+
|
|
95
|
+
console.log(`\n${c.bold}${c.magenta} ◆ write_file${c.reset} ${c.dim}${args.path}${c.reset}`);
|
|
96
|
+
console.log(`${c.dim} ${lines} lines · ${bytes} bytes${c.reset}`);
|
|
97
|
+
|
|
98
|
+
let oldContent: string | null = null;
|
|
99
|
+
try {
|
|
100
|
+
oldContent = await fs.readFile(full, "utf-8");
|
|
101
|
+
console.log(`${c.dim} (modifying existing file)${c.reset}`);
|
|
102
|
+
} catch {
|
|
103
|
+
console.log(`${c.dim} (creating new file)${c.reset}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
printDiff(oldContent, args.content, args.path);
|
|
107
|
+
|
|
108
|
+
const approved = await confirm(`Write ${bytes} bytes to "${args.path}"?`);
|
|
109
|
+
if (!approved) return "User declined to write this file.";
|
|
110
|
+
|
|
111
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
112
|
+
await fs.writeFile(full, args.content, "utf-8");
|
|
113
|
+
|
|
114
|
+
console.log(`${c.green}${c.bold} ✓ Wrote ${args.path}${c.reset} ${c.dim}(${bytes} bytes)${c.reset}\n`);
|
|
115
|
+
return `Wrote ${bytes} bytes to ${args.path}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function appendToFileTool(cwd: string, args: { path: string; content: string }): Promise<string> {
|
|
119
|
+
const full = resolveInCwd(cwd, args.path);
|
|
120
|
+
const bytes = Buffer.byteLength(args.content, "utf-8");
|
|
121
|
+
|
|
122
|
+
header("append_to_file", args.path);
|
|
123
|
+
|
|
124
|
+
const approved = await confirm(`Append ${bytes} bytes to "${args.path}"?`);
|
|
125
|
+
if (!approved) return "User declined to append to this file.";
|
|
126
|
+
|
|
127
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
128
|
+
await fs.appendFile(full, args.content, "utf-8");
|
|
129
|
+
|
|
130
|
+
console.log(`${c.green}${c.bold} ✓ Appended to ${args.path}${c.reset}\n`);
|
|
131
|
+
return `Appended ${bytes} bytes to ${args.path}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function renameFileTool(cwd: string, args: { old_path: string; new_path: string }): Promise<string> {
|
|
135
|
+
const oldFull = resolveInCwd(cwd, args.old_path);
|
|
136
|
+
const newFull = resolveInCwd(cwd, args.new_path);
|
|
137
|
+
|
|
138
|
+
header("rename_file", `${args.old_path} → ${args.new_path}`);
|
|
139
|
+
|
|
140
|
+
const approved = await confirm(`Rename "${args.old_path}" to "${args.new_path}"?`);
|
|
141
|
+
if (!approved) return "User declined to rename.";
|
|
142
|
+
|
|
143
|
+
await fs.mkdir(path.dirname(newFull), { recursive: true });
|
|
144
|
+
await fs.rename(oldFull, newFull);
|
|
145
|
+
|
|
146
|
+
console.log(`${c.green}${c.bold} ✓ Renamed${c.reset}\n`);
|
|
147
|
+
return `Renamed ${args.old_path} to ${args.new_path}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function deleteFileTool(cwd: string, args: { path: string }): Promise<string> {
|
|
151
|
+
const full = resolveInCwd(cwd, args.path);
|
|
152
|
+
|
|
153
|
+
header("delete_file", args.path);
|
|
154
|
+
|
|
155
|
+
const approved = await confirm(`Permanently delete "${args.path}"?`);
|
|
156
|
+
if (!approved) return "User declined to delete.";
|
|
157
|
+
|
|
158
|
+
await fs.unlink(full);
|
|
159
|
+
|
|
160
|
+
console.log(`${c.green}${c.bold} ✓ Deleted ${args.path}${c.reset}\n`);
|
|
161
|
+
return `Deleted ${args.path}`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function createDirectoryTool(cwd: string, args: { path: string }): Promise<string> {
|
|
165
|
+
const full = resolveInCwd(cwd, args.path);
|
|
166
|
+
|
|
167
|
+
header("create_directory", args.path);
|
|
168
|
+
|
|
169
|
+
await fs.mkdir(full, { recursive: true });
|
|
170
|
+
|
|
171
|
+
console.log(`${c.green}${c.bold} ✓ Created directory ${args.path}${c.reset}\n`);
|
|
172
|
+
return `Created directory ${args.path}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── Shell tools ───────────────────────────────────────────────────────────────
|
|
176
|
+
|
|
60
177
|
async function runCommandTool(cwd: string, args: { command: string }): Promise<string> {
|
|
61
178
|
console.log(`\n${c.bold}${c.yellow} ◆ run_command${c.reset} ${c.dim}${args.command}${c.reset}`);
|
|
62
179
|
const approved = await confirm(`Run: ${c.bold}${args.command}${c.reset}?`);
|
|
63
180
|
if (!approved) return "User declined to run this command.";
|
|
64
181
|
|
|
65
182
|
return new Promise((resolve) => {
|
|
66
|
-
const child = exec(args.command, { cwd, timeout:
|
|
183
|
+
const child = exec(args.command, { cwd, timeout: 120_000, maxBuffer: 4 * 1024 * 1024 });
|
|
67
184
|
|
|
68
185
|
let stdout = "";
|
|
69
186
|
let stderr = "";
|
|
@@ -92,7 +209,13 @@ async function runCommandTool(cwd: string, args: { command: string }): Promise<s
|
|
|
92
209
|
|
|
93
210
|
async function gitTool(cwd: string, args: { args: string[] }): Promise<string> {
|
|
94
211
|
const cmd = `git ${args.args.join(" ")}`;
|
|
212
|
+
const isMutating = !["status", "diff", "log", "show", "branch"].includes(args.args[0]);
|
|
213
|
+
|
|
95
214
|
header("git", cmd);
|
|
215
|
+
if (isMutating) {
|
|
216
|
+
const approved = await confirm(`Run: ${c.bold}${cmd}${c.reset}?`);
|
|
217
|
+
if (!approved) return "User declined to run this git command.";
|
|
218
|
+
}
|
|
96
219
|
|
|
97
220
|
return new Promise((resolve) => {
|
|
98
221
|
exec(cmd, { cwd, timeout: 30_000, maxBuffer: 1024 * 1024 }, (err, stdout, stderr) => {
|
|
@@ -138,7 +261,7 @@ async function searchFilesTool(
|
|
|
138
261
|
const grepArgs: string[] = [
|
|
139
262
|
"-rn",
|
|
140
263
|
"--color=never",
|
|
141
|
-
"-m", "
|
|
264
|
+
"-m", "10",
|
|
142
265
|
];
|
|
143
266
|
if (args.glob) {
|
|
144
267
|
grepArgs.push(`--include=${args.glob}`);
|
|
@@ -162,6 +285,91 @@ async function searchFilesTool(
|
|
|
162
285
|
});
|
|
163
286
|
}
|
|
164
287
|
|
|
288
|
+
// ── Web tools ─────────────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
async function webSearchTool(_cwd: string, args: { query: string }): Promise<string> {
|
|
291
|
+
header("web_search", args.query);
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
// Use DuckDuckGo instant answer API
|
|
295
|
+
const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(args.query)}&format=json&no_html=1&skip_disambig=1`;
|
|
296
|
+
const res = await fetch(url, {
|
|
297
|
+
headers: { "User-Agent": "Kundex/2.0.6" },
|
|
298
|
+
signal: AbortSignal.timeout(10_000),
|
|
299
|
+
});
|
|
300
|
+
const data = await res.json() as any;
|
|
301
|
+
|
|
302
|
+
const results: string[] = [];
|
|
303
|
+
|
|
304
|
+
if (data.AbstractText) {
|
|
305
|
+
results.push(`**${data.Heading || args.query}**\n${data.AbstractText}`);
|
|
306
|
+
if (data.AbstractURL) results.push(`Source: ${data.AbstractURL}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (data.Answer) {
|
|
310
|
+
results.push(`Answer: ${data.Answer}`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (data.RelatedTopics?.length) {
|
|
314
|
+
const topics = data.RelatedTopics
|
|
315
|
+
.filter((t: any) => t.Text)
|
|
316
|
+
.slice(0, 5)
|
|
317
|
+
.map((t: any) => `- ${t.Text}${t.FirstURL ? ` (${t.FirstURL})` : ""}`);
|
|
318
|
+
if (topics.length) {
|
|
319
|
+
results.push("Related:\n" + topics.join("\n"));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (results.length === 0) {
|
|
324
|
+
// Fallback: return a helpful message
|
|
325
|
+
return `No instant answer found for "${args.query}". Try using fetch_url to read a specific documentation page, or rephrase your search.`;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return results.join("\n\n");
|
|
329
|
+
} catch (err) {
|
|
330
|
+
return `Web search failed: ${(err as Error).message}`;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function fetchUrlTool(_cwd: string, args: { url: string }): Promise<string> {
|
|
335
|
+
header("fetch_url", args.url);
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const res = await fetch(args.url, {
|
|
339
|
+
headers: {
|
|
340
|
+
"User-Agent": "Kundex/2.0.6",
|
|
341
|
+
"Accept": "text/html,application/xhtml+xml,text/plain",
|
|
342
|
+
},
|
|
343
|
+
signal: AbortSignal.timeout(15_000),
|
|
344
|
+
redirect: "follow",
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
if (!res.ok) {
|
|
348
|
+
return `HTTP ${res.status} ${res.statusText}`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
352
|
+
const text = await res.text();
|
|
353
|
+
|
|
354
|
+
if (contentType.includes("text/html")) {
|
|
355
|
+
// Strip HTML tags for readability
|
|
356
|
+
const stripped = text
|
|
357
|
+
.replace(/<script[\s\S]*?<\/script>/gi, "")
|
|
358
|
+
.replace(/<style[\s\S]*?<\/style>/gi, "")
|
|
359
|
+
.replace(/<[^>]+>/g, " ")
|
|
360
|
+
.replace(/\s+/g, " ")
|
|
361
|
+
.trim();
|
|
362
|
+
return stripped.slice(0, 8000);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return text.slice(0, 8000);
|
|
366
|
+
} catch (err) {
|
|
367
|
+
return `Fetch failed: ${(err as Error).message}`;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ── Main dispatcher ───────────────────────────────────────────────────────────
|
|
372
|
+
|
|
165
373
|
export async function executeToolCall(cwd: string, call: ToolCall): Promise<ToolResult> {
|
|
166
374
|
let args: any;
|
|
167
375
|
try {
|
|
@@ -174,6 +382,16 @@ export async function executeToolCall(cwd: string, call: ToolCall): Promise<Tool
|
|
|
174
382
|
switch (call.name) {
|
|
175
383
|
case "read_file":
|
|
176
384
|
return { result: await readFileTool(cwd, args), isError: false };
|
|
385
|
+
case "write_file":
|
|
386
|
+
return { result: await writeFileTool(cwd, args), isError: false };
|
|
387
|
+
case "append_to_file":
|
|
388
|
+
return { result: await appendToFileTool(cwd, args), isError: false };
|
|
389
|
+
case "rename_file":
|
|
390
|
+
return { result: await renameFileTool(cwd, args), isError: false };
|
|
391
|
+
case "delete_file":
|
|
392
|
+
return { result: await deleteFileTool(cwd, args), isError: false };
|
|
393
|
+
case "create_directory":
|
|
394
|
+
return { result: await createDirectoryTool(cwd, args), isError: false };
|
|
177
395
|
case "run_command":
|
|
178
396
|
return { result: await runCommandTool(cwd, args), isError: false };
|
|
179
397
|
case "git":
|
|
@@ -182,6 +400,10 @@ export async function executeToolCall(cwd: string, call: ToolCall): Promise<Tool
|
|
|
182
400
|
return { result: await listDirectoryTool(cwd, args), isError: false };
|
|
183
401
|
case "search_files":
|
|
184
402
|
return { result: await searchFilesTool(cwd, args), isError: false };
|
|
403
|
+
case "web_search":
|
|
404
|
+
return { result: await webSearchTool(cwd, args), isError: false };
|
|
405
|
+
case "fetch_url":
|
|
406
|
+
return { result: await fetchUrlTool(cwd, args), isError: false };
|
|
185
407
|
default:
|
|
186
408
|
return { result: `Unknown tool: ${call.name}`, isError: true };
|
|
187
409
|
}
|