jazz-ai 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.
- package/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/cli/commands/auth.d.ts +18 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +128 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/chat-agent.d.ts +18 -0
- package/dist/cli/commands/chat-agent.d.ts.map +1 -0
- package/dist/cli/commands/chat-agent.js +421 -0
- package/dist/cli/commands/chat-agent.js.map +1 -0
- package/dist/cli/commands/edit-agent.d.ts +10 -0
- package/dist/cli/commands/edit-agent.d.ts.map +1 -0
- package/dist/cli/commands/edit-agent.js +310 -0
- package/dist/cli/commands/edit-agent.js.map +1 -0
- package/dist/cli/commands/task-agent.d.ts +126 -0
- package/dist/cli/commands/task-agent.d.ts.map +1 -0
- package/dist/cli/commands/task-agent.js +345 -0
- package/dist/cli/commands/task-agent.js.map +1 -0
- package/dist/core/agent/agent-prompt.d.ts +47 -0
- package/dist/core/agent/agent-prompt.d.ts.map +1 -0
- package/dist/core/agent/agent-prompt.js +146 -0
- package/dist/core/agent/agent-prompt.js.map +1 -0
- package/dist/core/agent/agent-runner.d.ts +63 -0
- package/dist/core/agent/agent-runner.d.ts.map +1 -0
- package/dist/core/agent/agent-runner.js +346 -0
- package/dist/core/agent/agent-runner.js.map +1 -0
- package/dist/core/agent/agent-service.d.ts +164 -0
- package/dist/core/agent/agent-service.d.ts.map +1 -0
- package/dist/core/agent/agent-service.js +463 -0
- package/dist/core/agent/agent-service.js.map +1 -0
- package/dist/core/agent/gmail-agent.d.ts +17 -0
- package/dist/core/agent/gmail-agent.d.ts.map +1 -0
- package/dist/core/agent/gmail-agent.js +191 -0
- package/dist/core/agent/gmail-agent.js.map +1 -0
- package/dist/core/agent/prompts/default/v1.d.ts +2 -0
- package/dist/core/agent/prompts/default/v1.d.ts.map +1 -0
- package/dist/core/agent/prompts/default/v1.js +202 -0
- package/dist/core/agent/prompts/default/v1.js.map +1 -0
- package/dist/core/agent/prompts/default/v2.d.ts +2 -0
- package/dist/core/agent/prompts/default/v2.d.ts.map +1 -0
- package/dist/core/agent/prompts/default/v2.js +72 -0
- package/dist/core/agent/prompts/default/v2.js.map +1 -0
- package/dist/core/agent/prompts/gmail/v1.d.ts +2 -0
- package/dist/core/agent/prompts/gmail/v1.d.ts.map +1 -0
- package/dist/core/agent/prompts/gmail/v1.js +206 -0
- package/dist/core/agent/prompts/gmail/v1.js.map +1 -0
- package/dist/core/agent/prompts/gmail/v2.d.ts +2 -0
- package/dist/core/agent/prompts/gmail/v2.d.ts.map +1 -0
- package/dist/core/agent/prompts/gmail/v2.js +59 -0
- package/dist/core/agent/prompts/gmail/v2.js.map +1 -0
- package/dist/core/agent/tools/base-tool.d.ts +161 -0
- package/dist/core/agent/tools/base-tool.d.ts.map +1 -0
- package/dist/core/agent/tools/base-tool.js +153 -0
- package/dist/core/agent/tools/base-tool.js.map +1 -0
- package/dist/core/agent/tools/fs-tools.d.ts +21 -0
- package/dist/core/agent/tools/fs-tools.d.ts.map +1 -0
- package/dist/core/agent/tools/fs-tools.js +1210 -0
- package/dist/core/agent/tools/fs-tools.js.map +1 -0
- package/dist/core/agent/tools/git-tools.d.ts +63 -0
- package/dist/core/agent/tools/git-tools.d.ts.map +1 -0
- package/dist/core/agent/tools/git-tools.js +600 -0
- package/dist/core/agent/tools/git-tools.js.map +1 -0
- package/dist/core/agent/tools/gmail-tools.d.ts +22 -0
- package/dist/core/agent/tools/gmail-tools.d.ts.map +1 -0
- package/dist/core/agent/tools/gmail-tools.js +779 -0
- package/dist/core/agent/tools/gmail-tools.js.map +1 -0
- package/dist/core/agent/tools/register-tools.d.ts +13 -0
- package/dist/core/agent/tools/register-tools.d.ts.map +1 -0
- package/dist/core/agent/tools/register-tools.js +169 -0
- package/dist/core/agent/tools/register-tools.js.map +1 -0
- package/dist/core/agent/tools/shell-tools.d.ts +19 -0
- package/dist/core/agent/tools/shell-tools.d.ts.map +1 -0
- package/dist/core/agent/tools/shell-tools.js +367 -0
- package/dist/core/agent/tools/shell-tools.js.map +1 -0
- package/dist/core/agent/tools/tool-registry.d.ts +62 -0
- package/dist/core/agent/tools/tool-registry.d.ts.map +1 -0
- package/dist/core/agent/tools/tool-registry.js +187 -0
- package/dist/core/agent/tools/tool-registry.js.map +1 -0
- package/dist/core/agent/tools/web-search-tools.d.ts +39 -0
- package/dist/core/agent/tools/web-search-tools.d.ts.map +1 -0
- package/dist/core/agent/tools/web-search-tools.js +220 -0
- package/dist/core/agent/tools/web-search-tools.js.map +1 -0
- package/dist/core/types/errors.d.ts +300 -0
- package/dist/core/types/errors.d.ts.map +1 -0
- package/dist/core/types/errors.js +115 -0
- package/dist/core/types/errors.js.map +1 -0
- package/dist/core/types/index.d.ts +208 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +30 -0
- package/dist/core/types/index.js.map +1 -0
- package/dist/core/utils/error-handler.d.ts +114 -0
- package/dist/core/utils/error-handler.d.ts.map +1 -0
- package/dist/core/utils/error-handler.js +551 -0
- package/dist/core/utils/error-handler.js.map +1 -0
- package/dist/core/utils/markdown-renderer.d.ts +52 -0
- package/dist/core/utils/markdown-renderer.d.ts.map +1 -0
- package/dist/core/utils/markdown-renderer.js +134 -0
- package/dist/core/utils/markdown-renderer.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +255 -0
- package/dist/main.js.map +1 -0
- package/dist/services/config.d.ts +29 -0
- package/dist/services/config.d.ts.map +1 -0
- package/dist/services/config.js +204 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/gmail.d.ts +197 -0
- package/dist/services/gmail.d.ts.map +1 -0
- package/dist/services/gmail.js +592 -0
- package/dist/services/gmail.js.map +1 -0
- package/dist/services/llm/ai-sdk-service.d.ts +5 -0
- package/dist/services/llm/ai-sdk-service.d.ts.map +1 -0
- package/dist/services/llm/ai-sdk-service.js +326 -0
- package/dist/services/llm/ai-sdk-service.js.map +1 -0
- package/dist/services/llm/context-manager.d.ts +51 -0
- package/dist/services/llm/context-manager.d.ts.map +1 -0
- package/dist/services/llm/context-manager.js +269 -0
- package/dist/services/llm/context-manager.js.map +1 -0
- package/dist/services/llm/types.d.ts +114 -0
- package/dist/services/llm/types.d.ts.map +1 -0
- package/dist/services/llm/types.js +51 -0
- package/dist/services/llm/types.js.map +1 -0
- package/dist/services/logger.d.ts +28 -0
- package/dist/services/logger.d.ts.map +1 -0
- package/dist/services/logger.js +267 -0
- package/dist/services/logger.js.map +1 -0
- package/dist/services/shell.d.ts +37 -0
- package/dist/services/shell.d.ts.map +1 -0
- package/dist/services/shell.js +197 -0
- package/dist/services/shell.js.map +1 -0
- package/dist/services/storage/file.d.ts +37 -0
- package/dist/services/storage/file.d.ts.map +1 -0
- package/dist/services/storage/file.js +221 -0
- package/dist/services/storage/file.js.map +1 -0
- package/dist/services/storage/inMemory.d.ts +25 -0
- package/dist/services/storage/inMemory.d.ts.map +1 -0
- package/dist/services/storage/inMemory.js +106 -0
- package/dist/services/storage/inMemory.js.map +1 -0
- package/dist/services/storage/service.d.ts +26 -0
- package/dist/services/storage/service.d.ts.map +1 -0
- package/dist/services/storage/service.js +48 -0
- package/dist/services/storage/service.js.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,1210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFindPathTool = createFindPathTool;
|
|
4
|
+
exports.createPwdTool = createPwdTool;
|
|
5
|
+
exports.createLsTool = createLsTool;
|
|
6
|
+
exports.createCdTool = createCdTool;
|
|
7
|
+
exports.createReadFileTool = createReadFileTool;
|
|
8
|
+
exports.createWriteFileTool = createWriteFileTool;
|
|
9
|
+
exports.createExecuteWriteFileTool = createExecuteWriteFileTool;
|
|
10
|
+
exports.createGrepTool = createGrepTool;
|
|
11
|
+
exports.createFindTool = createFindTool;
|
|
12
|
+
exports.createMkdirTool = createMkdirTool;
|
|
13
|
+
exports.createExecuteMkdirTool = createExecuteMkdirTool;
|
|
14
|
+
exports.createStatTool = createStatTool;
|
|
15
|
+
exports.createRmTool = createRmTool;
|
|
16
|
+
exports.createExecuteRmTool = createExecuteRmTool;
|
|
17
|
+
exports.createFindDirTool = createFindDirTool;
|
|
18
|
+
exports.registerFileTools = registerFileTools;
|
|
19
|
+
const platform_1 = require("@effect/platform");
|
|
20
|
+
const child_process_1 = require("child_process");
|
|
21
|
+
const effect_1 = require("effect");
|
|
22
|
+
const zod_1 = require("zod");
|
|
23
|
+
const shell_1 = require("../../../services/shell");
|
|
24
|
+
const base_tool_1 = require("./base-tool");
|
|
25
|
+
/**
|
|
26
|
+
* Filesystem and shell tools: pwd, ls, cd, grep, find, mkdir, rm
|
|
27
|
+
* mkdir and rm require explicit approval and are executed via hidden execute* tools.
|
|
28
|
+
*/
|
|
29
|
+
// Utility helpers
|
|
30
|
+
function buildKeyFromContext(context) {
|
|
31
|
+
return context.conversationId
|
|
32
|
+
? { agentId: context.agentId, conversationId: context.conversationId }
|
|
33
|
+
: { agentId: context.agentId };
|
|
34
|
+
}
|
|
35
|
+
function normalizeFilterPattern(pattern) {
|
|
36
|
+
if (!pattern || pattern.trim() === "")
|
|
37
|
+
return { type: "substring" };
|
|
38
|
+
const trimmed = pattern.trim();
|
|
39
|
+
if (trimmed.startsWith("re:")) {
|
|
40
|
+
const body = trimmed.slice(3);
|
|
41
|
+
try {
|
|
42
|
+
return { type: "regex", regex: new RegExp(body) };
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { type: "substring", value: body };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { type: "substring", value: trimmed };
|
|
49
|
+
}
|
|
50
|
+
// findPath - helps agent discover paths when unsure
|
|
51
|
+
function createFindPathTool() {
|
|
52
|
+
const parameters = zod_1.z
|
|
53
|
+
.object({
|
|
54
|
+
name: zod_1.z.string().min(1).describe("Name or partial name of the directory/file to find"),
|
|
55
|
+
maxDepth: zod_1.z
|
|
56
|
+
.number()
|
|
57
|
+
.int()
|
|
58
|
+
.positive()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe("Maximum search depth (default: 3)"),
|
|
61
|
+
type: zod_1.z.enum(["directory", "file", "both"]).optional().describe("Type of item to search for"),
|
|
62
|
+
searchPath: zod_1.z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Directory to start search from (defaults to current directory)"),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
68
|
+
return (0, base_tool_1.defineTool)({
|
|
69
|
+
name: "findPath",
|
|
70
|
+
description: "Find directories or files by name using the system find command",
|
|
71
|
+
parameters,
|
|
72
|
+
validate: (args) => {
|
|
73
|
+
const result = parameters.safeParse(args);
|
|
74
|
+
return result.success
|
|
75
|
+
? {
|
|
76
|
+
valid: true,
|
|
77
|
+
value: result.data,
|
|
78
|
+
}
|
|
79
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
80
|
+
},
|
|
81
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
82
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
83
|
+
const currentDir = yield* shell.getCwd(buildKeyFromContext(context));
|
|
84
|
+
const searchDir = args.searchPath
|
|
85
|
+
? yield* shell.resolvePath(buildKeyFromContext(context), args.searchPath)
|
|
86
|
+
: currentDir;
|
|
87
|
+
const maxDepth = args.maxDepth ?? 3;
|
|
88
|
+
const searchType = args.type ?? "both";
|
|
89
|
+
// Build find command arguments
|
|
90
|
+
const findArgs = [searchDir];
|
|
91
|
+
// Add max depth
|
|
92
|
+
findArgs.push("-maxdepth", maxDepth.toString());
|
|
93
|
+
// Add type filter
|
|
94
|
+
if (searchType === "directory") {
|
|
95
|
+
findArgs.push("-type", "d");
|
|
96
|
+
}
|
|
97
|
+
else if (searchType === "file") {
|
|
98
|
+
findArgs.push("-type", "f");
|
|
99
|
+
}
|
|
100
|
+
// Add name pattern (case-insensitive)
|
|
101
|
+
findArgs.push("-iname", `*${args.name}*`);
|
|
102
|
+
const command = `find ${findArgs.map((arg) => shell.escapePath(arg)).join(" ")}`;
|
|
103
|
+
// Execute the find command
|
|
104
|
+
const result = yield* effect_1.Effect.promise(() => new Promise((resolve, reject) => {
|
|
105
|
+
const child = (0, child_process_1.spawn)("sh", ["-c", command], {
|
|
106
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
107
|
+
timeout: 30000,
|
|
108
|
+
});
|
|
109
|
+
let stdout = "";
|
|
110
|
+
let stderr = "";
|
|
111
|
+
if (child.stdout) {
|
|
112
|
+
child.stdout.on("data", (data) => {
|
|
113
|
+
stdout += data.toString();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (child.stderr) {
|
|
117
|
+
child.stderr.on("data", (data) => {
|
|
118
|
+
stderr += data.toString();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
child.on("close", (code) => {
|
|
122
|
+
resolve({
|
|
123
|
+
stdout: stdout.trim(),
|
|
124
|
+
stderr: stderr.trim(),
|
|
125
|
+
exitCode: code || 0,
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
child.on("error", (error) => {
|
|
129
|
+
reject(error);
|
|
130
|
+
});
|
|
131
|
+
})).pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.succeed({
|
|
132
|
+
stdout: "",
|
|
133
|
+
stderr: error.message,
|
|
134
|
+
exitCode: 1,
|
|
135
|
+
})));
|
|
136
|
+
if (result.exitCode !== 0) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
result: null,
|
|
140
|
+
error: `find command failed: ${result.stderr}`,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Parse results
|
|
144
|
+
const paths = result.stdout
|
|
145
|
+
.split("\n")
|
|
146
|
+
.filter((line) => line.trim())
|
|
147
|
+
.map((path) => {
|
|
148
|
+
const name = path.split("/").pop() || "";
|
|
149
|
+
// Determine type by checking if it's a directory
|
|
150
|
+
// We'll use a simple heuristic: if it doesn't have an extension and is likely a dir
|
|
151
|
+
const isDir = !name.includes(".") || name.endsWith("/");
|
|
152
|
+
return {
|
|
153
|
+
path: path.trim(),
|
|
154
|
+
name,
|
|
155
|
+
type: isDir ? "dir" : "file",
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
result: {
|
|
161
|
+
searchTerm: args.name,
|
|
162
|
+
currentDirectory: currentDir,
|
|
163
|
+
searchDirectory: searchDir,
|
|
164
|
+
maxDepth,
|
|
165
|
+
type: searchType,
|
|
166
|
+
results: paths.slice(0, 50), // Limit results to avoid overwhelming output
|
|
167
|
+
totalFound: paths.length,
|
|
168
|
+
message: paths.length === 0
|
|
169
|
+
? `No ${searchType === "both" ? "items" : searchType + "s"} found matching "${args.name}"`
|
|
170
|
+
: `Found ${paths.length} ${searchType === "both" ? "items" : searchType + "s"} matching "${args.name}"`,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// pwd
|
|
177
|
+
function createPwdTool() {
|
|
178
|
+
const parameters = zod_1.z.object({}).strict();
|
|
179
|
+
return (0, base_tool_1.defineTool)({
|
|
180
|
+
name: "pwd",
|
|
181
|
+
description: "Print the current working directory for this agent session",
|
|
182
|
+
parameters,
|
|
183
|
+
validate: (args) => {
|
|
184
|
+
const result = parameters.safeParse(args);
|
|
185
|
+
return result.success
|
|
186
|
+
? { valid: true, value: result.data }
|
|
187
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
188
|
+
},
|
|
189
|
+
handler: (_args, context) => effect_1.Effect.gen(function* () {
|
|
190
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
191
|
+
const cwd = yield* shell.getCwd(buildKeyFromContext(context));
|
|
192
|
+
return { success: true, result: cwd };
|
|
193
|
+
}),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
// ls
|
|
197
|
+
function createLsTool() {
|
|
198
|
+
const parameters = zod_1.z
|
|
199
|
+
.object({
|
|
200
|
+
path: zod_1.z
|
|
201
|
+
.string()
|
|
202
|
+
.optional()
|
|
203
|
+
.describe("Directory path to list (defaults to current directory)"),
|
|
204
|
+
showHidden: zod_1.z.boolean().optional().describe("Include hidden files (dotfiles)"),
|
|
205
|
+
recursive: zod_1.z.boolean().optional().describe("Recurse into sub-directories"),
|
|
206
|
+
pattern: zod_1.z.string().optional().describe("Filter by substring or use 're:<regex>'"),
|
|
207
|
+
maxResults: zod_1.z
|
|
208
|
+
.number()
|
|
209
|
+
.int()
|
|
210
|
+
.positive()
|
|
211
|
+
.optional()
|
|
212
|
+
.describe("Maximum number of results to return"),
|
|
213
|
+
})
|
|
214
|
+
.strict();
|
|
215
|
+
return (0, base_tool_1.defineTool)({
|
|
216
|
+
name: "ls",
|
|
217
|
+
description: "List directory contents with optional filtering and recursion",
|
|
218
|
+
parameters,
|
|
219
|
+
validate: (args) => {
|
|
220
|
+
const result = parameters.safeParse(args);
|
|
221
|
+
return result.success
|
|
222
|
+
? {
|
|
223
|
+
valid: true,
|
|
224
|
+
value: result.data,
|
|
225
|
+
}
|
|
226
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
227
|
+
},
|
|
228
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
229
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
230
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
231
|
+
const basePath = args.path
|
|
232
|
+
? yield* shell.resolvePath(buildKeyFromContext(context), args.path).pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.succeed({
|
|
233
|
+
success: false,
|
|
234
|
+
result: null,
|
|
235
|
+
error: error instanceof Error ? error.message : String(error),
|
|
236
|
+
})))
|
|
237
|
+
: yield* shell.getCwd(buildKeyFromContext(context));
|
|
238
|
+
// If path resolution failed, return the error with suggestions
|
|
239
|
+
if (typeof basePath === "object" && "success" in basePath && !basePath.success) {
|
|
240
|
+
return basePath;
|
|
241
|
+
}
|
|
242
|
+
const resolvedPath = basePath;
|
|
243
|
+
const includeHidden = args.showHidden === true;
|
|
244
|
+
const recursive = args.recursive === true;
|
|
245
|
+
const maxResults = typeof args.maxResults === "number" && args.maxResults > 0 ? args.maxResults : 2000;
|
|
246
|
+
const filter = normalizeFilterPattern(args.pattern);
|
|
247
|
+
function matches(name) {
|
|
248
|
+
if (!filter.value && !filter.regex)
|
|
249
|
+
return true;
|
|
250
|
+
if (filter.type === "regex" && filter.regex)
|
|
251
|
+
return filter.regex.test(name);
|
|
252
|
+
return filter.value ? name.includes(filter.value) : true;
|
|
253
|
+
}
|
|
254
|
+
const results = [];
|
|
255
|
+
function walk(dir) {
|
|
256
|
+
return effect_1.Effect.gen(function* () {
|
|
257
|
+
// Handle permission errors gracefully
|
|
258
|
+
const entries = yield* fs
|
|
259
|
+
.readDirectory(dir)
|
|
260
|
+
.pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed([])));
|
|
261
|
+
for (const name of entries) {
|
|
262
|
+
if (!includeHidden && name.startsWith("."))
|
|
263
|
+
continue;
|
|
264
|
+
const full = `${dir}/${name}`;
|
|
265
|
+
// Handle broken symbolic links gracefully
|
|
266
|
+
const stat = yield* fs.stat(full).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(null)));
|
|
267
|
+
if (!stat) {
|
|
268
|
+
// Skip broken symbolic links or inaccessible files
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
const type = stat.type === "Directory" ? "dir" : "file";
|
|
272
|
+
if (matches(name)) {
|
|
273
|
+
results.push({ path: full, name, type });
|
|
274
|
+
if (results.length >= maxResults)
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (recursive && stat.type === "Directory") {
|
|
278
|
+
yield* walk(full);
|
|
279
|
+
if (results.length >= maxResults)
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const stat = yield* fs.stat(resolvedPath);
|
|
287
|
+
if (stat.type !== "Directory") {
|
|
288
|
+
return { success: false, result: null, error: `Not a directory: ${resolvedPath}` };
|
|
289
|
+
}
|
|
290
|
+
yield* walk(resolvedPath);
|
|
291
|
+
return { success: true, result: results };
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
return {
|
|
295
|
+
success: false,
|
|
296
|
+
result: null,
|
|
297
|
+
error: `ls failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
// cd
|
|
304
|
+
function createCdTool() {
|
|
305
|
+
const parameters = zod_1.z
|
|
306
|
+
.object({
|
|
307
|
+
path: zod_1.z.string().min(1).describe("Path to change directory to"),
|
|
308
|
+
})
|
|
309
|
+
.strict();
|
|
310
|
+
return (0, base_tool_1.defineTool)({
|
|
311
|
+
name: "cd",
|
|
312
|
+
description: "Change the current working directory for this agent session",
|
|
313
|
+
parameters,
|
|
314
|
+
validate: (args) => {
|
|
315
|
+
const result = parameters.safeParse(args);
|
|
316
|
+
return result.success
|
|
317
|
+
? { valid: true, value: result.data }
|
|
318
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
319
|
+
},
|
|
320
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
321
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
322
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
323
|
+
// Try to resolve the path - this will provide helpful suggestions if the path doesn't exist
|
|
324
|
+
const targetResult = yield* shell.resolvePath(buildKeyFromContext(context), args.path).pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.succeed({
|
|
325
|
+
success: false,
|
|
326
|
+
result: null,
|
|
327
|
+
error: error instanceof Error ? error.message : String(error),
|
|
328
|
+
})));
|
|
329
|
+
// If path resolution failed, return the error with suggestions
|
|
330
|
+
if (typeof targetResult === "object" &&
|
|
331
|
+
"success" in targetResult &&
|
|
332
|
+
!targetResult.success) {
|
|
333
|
+
return targetResult;
|
|
334
|
+
}
|
|
335
|
+
const target = targetResult;
|
|
336
|
+
try {
|
|
337
|
+
const stat = yield* fs.stat(target);
|
|
338
|
+
if (stat.type !== "Directory") {
|
|
339
|
+
return { success: false, result: null, error: `Not a directory: ${target}` };
|
|
340
|
+
}
|
|
341
|
+
yield* shell.setCwd(buildKeyFromContext(context), target);
|
|
342
|
+
return { success: true, result: target };
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
return {
|
|
346
|
+
success: false,
|
|
347
|
+
result: null,
|
|
348
|
+
error: `cd failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}),
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
// readFile
|
|
355
|
+
function createReadFileTool() {
|
|
356
|
+
const parameters = zod_1.z
|
|
357
|
+
.object({
|
|
358
|
+
path: zod_1.z.string().min(1).describe("File path to read (relative to cwd allowed)"),
|
|
359
|
+
startLine: zod_1.z.number().int().positive().optional().describe("1-based start line (inclusive)"),
|
|
360
|
+
endLine: zod_1.z.number().int().positive().optional().describe("1-based end line (inclusive)"),
|
|
361
|
+
maxBytes: zod_1.z
|
|
362
|
+
.number()
|
|
363
|
+
.int()
|
|
364
|
+
.positive()
|
|
365
|
+
.optional()
|
|
366
|
+
.describe("Maximum number of bytes to return (content is truncated if exceeded)"),
|
|
367
|
+
encoding: zod_1.z.string().optional().describe("Text encoding (currently utf-8)"),
|
|
368
|
+
})
|
|
369
|
+
.strict();
|
|
370
|
+
return (0, base_tool_1.defineTool)({
|
|
371
|
+
name: "readFile",
|
|
372
|
+
description: "Read a text file with optional line range and size limit",
|
|
373
|
+
parameters,
|
|
374
|
+
validate: (args) => {
|
|
375
|
+
const result = parameters.safeParse(args);
|
|
376
|
+
return result.success
|
|
377
|
+
? {
|
|
378
|
+
valid: true,
|
|
379
|
+
value: result.data,
|
|
380
|
+
}
|
|
381
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
382
|
+
},
|
|
383
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
384
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
385
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
386
|
+
const filePathResult = yield* shell
|
|
387
|
+
.resolvePath(buildKeyFromContext(context), args.path)
|
|
388
|
+
.pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.succeed({
|
|
389
|
+
success: false,
|
|
390
|
+
result: null,
|
|
391
|
+
error: error instanceof Error ? error.message : String(error),
|
|
392
|
+
})));
|
|
393
|
+
// If path resolution failed, return the error with suggestions
|
|
394
|
+
if (typeof filePathResult === "object" &&
|
|
395
|
+
"success" in filePathResult &&
|
|
396
|
+
!filePathResult.success) {
|
|
397
|
+
return filePathResult;
|
|
398
|
+
}
|
|
399
|
+
const filePath = filePathResult;
|
|
400
|
+
try {
|
|
401
|
+
const stat = yield* fs.stat(filePath);
|
|
402
|
+
if (stat.type === "Directory") {
|
|
403
|
+
return { success: false, result: null, error: `Not a file: ${filePath}` };
|
|
404
|
+
}
|
|
405
|
+
let content = yield* fs.readFileString(filePath);
|
|
406
|
+
// Strip UTF-8 BOM if present
|
|
407
|
+
if (content.length > 0 && content.charCodeAt(0) === 0xfeff) {
|
|
408
|
+
content = content.slice(1);
|
|
409
|
+
}
|
|
410
|
+
let totalLines = 0;
|
|
411
|
+
let returnedLines = 0;
|
|
412
|
+
let rangeStart = undefined;
|
|
413
|
+
let rangeEnd = undefined;
|
|
414
|
+
// Apply line range if provided
|
|
415
|
+
if (args.startLine !== undefined || args.endLine !== undefined) {
|
|
416
|
+
const lines = content.split(/\r?\n/);
|
|
417
|
+
totalLines = lines.length;
|
|
418
|
+
const start = Math.max(1, args.startLine ?? 1);
|
|
419
|
+
const rawEnd = args.endLine ?? totalLines;
|
|
420
|
+
const end = Math.max(start, Math.min(rawEnd, totalLines));
|
|
421
|
+
content = lines.slice(start - 1, end).join("\n");
|
|
422
|
+
returnedLines = end - start + 1;
|
|
423
|
+
rangeStart = start;
|
|
424
|
+
rangeEnd = end;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
// If no range, we can still report total lines lazily without splitting twice
|
|
428
|
+
totalLines = content === "" ? 0 : content.split(/\r?\n/).length;
|
|
429
|
+
returnedLines = totalLines;
|
|
430
|
+
}
|
|
431
|
+
// Enforce maxBytes safeguard (approximate by string length)
|
|
432
|
+
const maxBytes = typeof args.maxBytes === "number" && args.maxBytes > 0 ? args.maxBytes : 131072;
|
|
433
|
+
let truncated = false;
|
|
434
|
+
if (content.length > maxBytes) {
|
|
435
|
+
content = content.slice(0, maxBytes);
|
|
436
|
+
truncated = true;
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
success: true,
|
|
440
|
+
result: {
|
|
441
|
+
path: filePath,
|
|
442
|
+
encoding: (args.encoding ?? "utf-8").toLowerCase(),
|
|
443
|
+
content,
|
|
444
|
+
truncated,
|
|
445
|
+
totalLines,
|
|
446
|
+
returnedLines,
|
|
447
|
+
range: rangeStart !== undefined ? { startLine: rangeStart, endLine: rangeEnd } : undefined,
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
return {
|
|
453
|
+
success: false,
|
|
454
|
+
result: null,
|
|
455
|
+
error: `readFile failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
}),
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
function createWriteFileTool() {
|
|
462
|
+
const parameters = zod_1.z
|
|
463
|
+
.object({
|
|
464
|
+
path: zod_1.z
|
|
465
|
+
.string()
|
|
466
|
+
.min(1)
|
|
467
|
+
.describe("File path to write to, will be created if it doesn't exist (relative to cwd allowed)"),
|
|
468
|
+
content: zod_1.z.string().describe("Content to write to the file"),
|
|
469
|
+
encoding: zod_1.z.string().optional().describe("Text encoding (currently utf-8)"),
|
|
470
|
+
createDirs: zod_1.z.boolean().optional().describe("Create parent directories if they don't exist"),
|
|
471
|
+
})
|
|
472
|
+
.strict();
|
|
473
|
+
return (0, base_tool_1.defineTool)({
|
|
474
|
+
name: "writeFile",
|
|
475
|
+
description: "Write content to a file, creating it if it doesn't exist (requires user approval)",
|
|
476
|
+
parameters,
|
|
477
|
+
validate: (args) => {
|
|
478
|
+
const result = parameters.safeParse(args);
|
|
479
|
+
return result.success
|
|
480
|
+
? { valid: true, value: result.data }
|
|
481
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
482
|
+
},
|
|
483
|
+
approval: {
|
|
484
|
+
message: (args, context) => effect_1.Effect.gen(function* () {
|
|
485
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
486
|
+
const target = yield* shell.resolvePath(buildKeyFromContext(context), args.path, {
|
|
487
|
+
skipExistenceCheck: true,
|
|
488
|
+
});
|
|
489
|
+
return `About to write to file: ${target}${args.createDirs === true ? " (will create parent directories)" : ""}.\n\nIMPORTANT: After getting user confirmation, you MUST call the executeWriteFile tool with these exact arguments: {"path": "${args.path}", "content": ${JSON.stringify(args.content)}, "encoding": "${args.encoding ?? "utf-8"}", "createDirs": ${args.createDirs === true}}`;
|
|
490
|
+
}),
|
|
491
|
+
errorMessage: "Approval required: File writing requires user confirmation.",
|
|
492
|
+
execute: {
|
|
493
|
+
toolName: "executeWriteFile",
|
|
494
|
+
buildArgs: (args) => ({
|
|
495
|
+
path: args.path,
|
|
496
|
+
content: args.content,
|
|
497
|
+
encoding: args.encoding,
|
|
498
|
+
createDirs: args.createDirs,
|
|
499
|
+
}),
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
handler: (_args) => effect_1.Effect.succeed({ success: false, result: null, error: "Approval required" }),
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
function createExecuteWriteFileTool() {
|
|
506
|
+
const parameters = zod_1.z
|
|
507
|
+
.object({
|
|
508
|
+
path: zod_1.z
|
|
509
|
+
.string()
|
|
510
|
+
.min(1)
|
|
511
|
+
.describe("File path to write to, will be created if it doesn't exist"),
|
|
512
|
+
content: zod_1.z.string().describe("Content to write to the file"),
|
|
513
|
+
encoding: zod_1.z.string().optional().describe("Text encoding (currently utf-8)"),
|
|
514
|
+
createDirs: zod_1.z.boolean().optional().describe("Create parent directories if they don't exist"),
|
|
515
|
+
})
|
|
516
|
+
.strict();
|
|
517
|
+
return (0, base_tool_1.defineTool)({
|
|
518
|
+
name: "executeWriteFile",
|
|
519
|
+
description: "Execute writeFile after user approval",
|
|
520
|
+
hidden: true,
|
|
521
|
+
parameters,
|
|
522
|
+
validate: (args) => {
|
|
523
|
+
const result = parameters.safeParse(args);
|
|
524
|
+
return result.success
|
|
525
|
+
? { valid: true, value: result.data }
|
|
526
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
527
|
+
},
|
|
528
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
529
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
530
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
531
|
+
const target = yield* shell.resolvePath(buildKeyFromContext(context), args.path, {
|
|
532
|
+
skipExistenceCheck: true,
|
|
533
|
+
});
|
|
534
|
+
try {
|
|
535
|
+
// Create parent directories if requested
|
|
536
|
+
if (args.createDirs === true) {
|
|
537
|
+
const parentDir = target.substring(0, target.lastIndexOf("/"));
|
|
538
|
+
if (parentDir && parentDir !== target) {
|
|
539
|
+
yield* fs.makeDirectory(parentDir, { recursive: true });
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// Write the file content
|
|
543
|
+
yield* fs.writeFileString(target, args.content);
|
|
544
|
+
return { success: true, result: `File written: ${target}` };
|
|
545
|
+
}
|
|
546
|
+
catch (error) {
|
|
547
|
+
return {
|
|
548
|
+
success: false,
|
|
549
|
+
result: null,
|
|
550
|
+
error: `writeFile failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
}),
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
// grep
|
|
557
|
+
function createGrepTool() {
|
|
558
|
+
const parameters = zod_1.z
|
|
559
|
+
.object({
|
|
560
|
+
pattern: zod_1.z.string().min(1).describe("Search pattern (literal or 're:<regex>')"),
|
|
561
|
+
path: zod_1.z.string().optional().describe("File or directory to search (defaults to cwd)"),
|
|
562
|
+
recursive: zod_1.z.boolean().optional().describe("Recurse into directories"),
|
|
563
|
+
regex: zod_1.z.boolean().optional().describe("Treat pattern as regex (overrides re:<...>)"),
|
|
564
|
+
ignoreCase: zod_1.z.boolean().optional().describe("Case-insensitive match"),
|
|
565
|
+
maxResults: zod_1.z.number().int().positive().optional().describe("Max matches to return"),
|
|
566
|
+
filePattern: zod_1.z
|
|
567
|
+
.string()
|
|
568
|
+
.optional()
|
|
569
|
+
.describe("File pattern to search in (e.g., '*.js', '*.ts')"),
|
|
570
|
+
})
|
|
571
|
+
.strict();
|
|
572
|
+
return (0, base_tool_1.defineTool)({
|
|
573
|
+
name: "grep",
|
|
574
|
+
description: "Search for a pattern in files using the system grep command",
|
|
575
|
+
parameters,
|
|
576
|
+
validate: (args) => {
|
|
577
|
+
const result = parameters.safeParse(args);
|
|
578
|
+
return result.success
|
|
579
|
+
? {
|
|
580
|
+
valid: true,
|
|
581
|
+
value: result.data,
|
|
582
|
+
}
|
|
583
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
584
|
+
},
|
|
585
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
586
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
587
|
+
const start = args.path
|
|
588
|
+
? yield* shell.resolvePath(buildKeyFromContext(context), args.path)
|
|
589
|
+
: yield* shell.getCwd(buildKeyFromContext(context));
|
|
590
|
+
const recursive = args.recursive !== false;
|
|
591
|
+
const maxResults = typeof args.maxResults === "number" && args.maxResults > 0 ? args.maxResults : 5000;
|
|
592
|
+
// Build grep command arguments
|
|
593
|
+
const grepArgs = [];
|
|
594
|
+
// Add recursive flag
|
|
595
|
+
if (recursive) {
|
|
596
|
+
grepArgs.push("-r");
|
|
597
|
+
}
|
|
598
|
+
// Add case sensitivity
|
|
599
|
+
if (args.ignoreCase) {
|
|
600
|
+
grepArgs.push("-i");
|
|
601
|
+
}
|
|
602
|
+
// Add line numbers
|
|
603
|
+
grepArgs.push("-n");
|
|
604
|
+
// Add file pattern if specified
|
|
605
|
+
if (args.filePattern) {
|
|
606
|
+
grepArgs.push("--include", args.filePattern);
|
|
607
|
+
}
|
|
608
|
+
// Add max count to limit results
|
|
609
|
+
grepArgs.push("-m", maxResults.toString());
|
|
610
|
+
// Determine if pattern is regex
|
|
611
|
+
let searchPattern;
|
|
612
|
+
if (args.regex === true || args.pattern.startsWith("re:")) {
|
|
613
|
+
const source = args.regex === true ? args.pattern : args.pattern.slice(3) || "";
|
|
614
|
+
searchPattern = source;
|
|
615
|
+
grepArgs.push("-E"); // Extended regex
|
|
616
|
+
}
|
|
617
|
+
else {
|
|
618
|
+
searchPattern = args.pattern;
|
|
619
|
+
grepArgs.push("-F"); // Fixed string
|
|
620
|
+
}
|
|
621
|
+
// Add the search pattern and path
|
|
622
|
+
grepArgs.push(searchPattern, start);
|
|
623
|
+
const command = `grep ${grepArgs.map((arg) => shell.escapePath(arg)).join(" ")}`;
|
|
624
|
+
// Execute the grep command
|
|
625
|
+
const result = yield* effect_1.Effect.promise(() => new Promise((resolve, reject) => {
|
|
626
|
+
const child = (0, child_process_1.spawn)("sh", ["-c", command], {
|
|
627
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
628
|
+
timeout: 30000,
|
|
629
|
+
});
|
|
630
|
+
let stdout = "";
|
|
631
|
+
let stderr = "";
|
|
632
|
+
if (child.stdout) {
|
|
633
|
+
child.stdout.on("data", (data) => {
|
|
634
|
+
stdout += data.toString();
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
if (child.stderr) {
|
|
638
|
+
child.stderr.on("data", (data) => {
|
|
639
|
+
stderr += data.toString();
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
child.on("close", (code) => {
|
|
643
|
+
resolve({
|
|
644
|
+
stdout: stdout.trim(),
|
|
645
|
+
stderr: stderr.trim(),
|
|
646
|
+
exitCode: code || 0,
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
child.on("error", (error) => {
|
|
650
|
+
reject(error);
|
|
651
|
+
});
|
|
652
|
+
})).pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.succeed({
|
|
653
|
+
stdout: "",
|
|
654
|
+
stderr: error.message,
|
|
655
|
+
exitCode: 1,
|
|
656
|
+
})));
|
|
657
|
+
// Grep returns exit code 1 when no matches are found, which is normal
|
|
658
|
+
if (result.exitCode !== 0 && result.exitCode !== 1) {
|
|
659
|
+
return {
|
|
660
|
+
success: false,
|
|
661
|
+
result: null,
|
|
662
|
+
error: `grep command failed: ${result.stderr}`,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
// Parse results
|
|
666
|
+
const matches = result.stdout
|
|
667
|
+
.split("\n")
|
|
668
|
+
.filter((line) => line.trim())
|
|
669
|
+
.map((line) => {
|
|
670
|
+
// Parse format: file:line:content
|
|
671
|
+
const parts = line.split(":");
|
|
672
|
+
if (parts.length >= 3 && parts[1]) {
|
|
673
|
+
const file = parts[0];
|
|
674
|
+
const lineNum = parseInt(parts[1], 10);
|
|
675
|
+
const text = parts.slice(2).join(":");
|
|
676
|
+
return {
|
|
677
|
+
file,
|
|
678
|
+
line: lineNum,
|
|
679
|
+
text,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
return null;
|
|
683
|
+
})
|
|
684
|
+
.filter((match) => match !== null);
|
|
685
|
+
return {
|
|
686
|
+
success: true,
|
|
687
|
+
result: {
|
|
688
|
+
pattern: args.pattern,
|
|
689
|
+
searchPath: start,
|
|
690
|
+
recursive,
|
|
691
|
+
regex: args.regex === true || args.pattern.startsWith("re:"),
|
|
692
|
+
ignoreCase: args.ignoreCase,
|
|
693
|
+
filePattern: args.filePattern,
|
|
694
|
+
matches: matches.slice(0, maxResults),
|
|
695
|
+
totalFound: matches.length,
|
|
696
|
+
message: matches.length === 0
|
|
697
|
+
? `No matches found for pattern "${args.pattern}"`
|
|
698
|
+
: `Found ${matches.length} matches for pattern "${args.pattern}"`,
|
|
699
|
+
},
|
|
700
|
+
};
|
|
701
|
+
}),
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
// find
|
|
705
|
+
function createFindTool() {
|
|
706
|
+
const parameters = zod_1.z
|
|
707
|
+
.object({
|
|
708
|
+
path: zod_1.z.string().optional().describe("Start directory (defaults to smart search)"),
|
|
709
|
+
name: zod_1.z.string().optional().describe("Filter by name (substring or 're:<regex>')"),
|
|
710
|
+
type: zod_1.z.enum(["file", "dir", "all"]).optional().describe("Type filter"),
|
|
711
|
+
maxDepth: zod_1.z
|
|
712
|
+
.number()
|
|
713
|
+
.int()
|
|
714
|
+
.nonnegative()
|
|
715
|
+
.optional()
|
|
716
|
+
.describe("Maximum depth to traverse (0=current dir)"),
|
|
717
|
+
maxResults: zod_1.z.number().int().positive().optional().describe("Maximum results to return"),
|
|
718
|
+
includeHidden: zod_1.z.boolean().optional().describe("Include dotfiles and dot-directories"),
|
|
719
|
+
smart: zod_1.z
|
|
720
|
+
.boolean()
|
|
721
|
+
.optional()
|
|
722
|
+
.describe("Use smart hierarchical search (HOME first, then expand)"),
|
|
723
|
+
})
|
|
724
|
+
.strict();
|
|
725
|
+
return (0, base_tool_1.defineTool)({
|
|
726
|
+
name: "find",
|
|
727
|
+
description: "Find files and directories using the system find command with smart hierarchical search",
|
|
728
|
+
parameters,
|
|
729
|
+
validate: (args) => {
|
|
730
|
+
const result = parameters.safeParse(args);
|
|
731
|
+
return result.success
|
|
732
|
+
? {
|
|
733
|
+
valid: true,
|
|
734
|
+
value: result.data,
|
|
735
|
+
}
|
|
736
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
737
|
+
},
|
|
738
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
739
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
740
|
+
const includeHidden = args.includeHidden === true;
|
|
741
|
+
const maxResults = typeof args.maxResults === "number" && args.maxResults > 0 ? args.maxResults : 5000;
|
|
742
|
+
const maxDepth = typeof args.maxDepth === "number" ? args.maxDepth : 25;
|
|
743
|
+
const typeFilter = args.type ?? "all";
|
|
744
|
+
const useSmart = args.smart !== false; // Default to true
|
|
745
|
+
// Smart search strategy: search in order of likelihood
|
|
746
|
+
const searchPaths = [];
|
|
747
|
+
if (args.path) {
|
|
748
|
+
// If path is specified, use it directly
|
|
749
|
+
const start = yield* shell.resolvePath(buildKeyFromContext(context), args.path);
|
|
750
|
+
searchPaths.push(start);
|
|
751
|
+
}
|
|
752
|
+
else if (useSmart) {
|
|
753
|
+
// Smart search: start with most likely locations
|
|
754
|
+
const home = process.env["HOME"] || "";
|
|
755
|
+
const cwd = yield* shell.getCwd(buildKeyFromContext(context));
|
|
756
|
+
// 1. Current working directory (most likely)
|
|
757
|
+
if (cwd && cwd !== home) {
|
|
758
|
+
searchPaths.push(cwd);
|
|
759
|
+
}
|
|
760
|
+
// 2. Home directory (very likely)
|
|
761
|
+
if (home) {
|
|
762
|
+
searchPaths.push(home);
|
|
763
|
+
}
|
|
764
|
+
// 3. Parent directories (up to 3 levels up from cwd)
|
|
765
|
+
let currentPath = cwd;
|
|
766
|
+
for (let i = 0; i < 3; i++) {
|
|
767
|
+
const parent = currentPath.split("/").slice(0, -1).join("/");
|
|
768
|
+
if (parent && parent !== currentPath && parent !== "/") {
|
|
769
|
+
searchPaths.push(parent);
|
|
770
|
+
currentPath = parent;
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
// Traditional search: start from current directory
|
|
779
|
+
const start = yield* shell.getCwd(buildKeyFromContext(context));
|
|
780
|
+
searchPaths.push(start);
|
|
781
|
+
}
|
|
782
|
+
const allResults = [];
|
|
783
|
+
// Search each path in order using system find command
|
|
784
|
+
for (const searchPath of searchPaths) {
|
|
785
|
+
if (allResults.length >= maxResults)
|
|
786
|
+
break;
|
|
787
|
+
// Build find command arguments
|
|
788
|
+
const findArgs = [searchPath];
|
|
789
|
+
// Add max depth
|
|
790
|
+
findArgs.push("-maxdepth", maxDepth.toString());
|
|
791
|
+
// Add type filter
|
|
792
|
+
if (typeFilter === "dir") {
|
|
793
|
+
findArgs.push("-type", "d");
|
|
794
|
+
}
|
|
795
|
+
else if (typeFilter === "file") {
|
|
796
|
+
findArgs.push("-type", "f");
|
|
797
|
+
}
|
|
798
|
+
// Add name pattern if specified
|
|
799
|
+
if (args.name) {
|
|
800
|
+
const filter = normalizeFilterPattern(args.name);
|
|
801
|
+
if (filter.type === "regex" && filter.regex) {
|
|
802
|
+
findArgs.push("-regex", filter.regex.source);
|
|
803
|
+
}
|
|
804
|
+
else if (filter.value) {
|
|
805
|
+
findArgs.push("-iname", `*${filter.value}*`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
// Handle hidden files
|
|
809
|
+
if (!includeHidden) {
|
|
810
|
+
findArgs.push("!", "-name", ".*");
|
|
811
|
+
}
|
|
812
|
+
const command = `find ${findArgs.map((arg) => shell.escapePath(arg)).join(" ")}`;
|
|
813
|
+
// Execute the find command
|
|
814
|
+
const result = yield* effect_1.Effect.promise(() => new Promise((resolve, reject) => {
|
|
815
|
+
const child = (0, child_process_1.spawn)("sh", ["-c", command], {
|
|
816
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
817
|
+
timeout: 30000,
|
|
818
|
+
});
|
|
819
|
+
let stdout = "";
|
|
820
|
+
let stderr = "";
|
|
821
|
+
if (child.stdout) {
|
|
822
|
+
child.stdout.on("data", (data) => {
|
|
823
|
+
stdout += data.toString();
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
if (child.stderr) {
|
|
827
|
+
child.stderr.on("data", (data) => {
|
|
828
|
+
stderr += data.toString();
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
child.on("close", (code) => {
|
|
832
|
+
resolve({
|
|
833
|
+
stdout: stdout.trim(),
|
|
834
|
+
stderr: stderr.trim(),
|
|
835
|
+
exitCode: code || 0,
|
|
836
|
+
});
|
|
837
|
+
});
|
|
838
|
+
child.on("error", (error) => {
|
|
839
|
+
reject(error);
|
|
840
|
+
});
|
|
841
|
+
})).pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.succeed({
|
|
842
|
+
stdout: "",
|
|
843
|
+
stderr: error.message,
|
|
844
|
+
exitCode: 1,
|
|
845
|
+
})));
|
|
846
|
+
if (result.exitCode !== 0) {
|
|
847
|
+
// Continue to next search path if this one fails
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
// Parse results
|
|
851
|
+
const paths = result.stdout
|
|
852
|
+
.split("\n")
|
|
853
|
+
.filter((line) => line.trim())
|
|
854
|
+
.map((path) => {
|
|
855
|
+
const name = path.split("/").pop() || "";
|
|
856
|
+
// Determine type by checking if it's a directory
|
|
857
|
+
// We'll use a simple heuristic: if it doesn't have an extension and is likely a dir
|
|
858
|
+
const isDir = !name.includes(".") || name.endsWith("/");
|
|
859
|
+
return {
|
|
860
|
+
path: path.trim(),
|
|
861
|
+
name,
|
|
862
|
+
type: isDir ? "dir" : "file",
|
|
863
|
+
};
|
|
864
|
+
});
|
|
865
|
+
allResults.push(...paths);
|
|
866
|
+
// If we found results and using smart search, we can stop early
|
|
867
|
+
// This prevents searching too many locations when we already have good results
|
|
868
|
+
if (useSmart && allResults.length >= Math.min(maxResults / 2, 10)) {
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return {
|
|
873
|
+
success: true,
|
|
874
|
+
result: allResults.slice(0, maxResults),
|
|
875
|
+
};
|
|
876
|
+
}),
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
// mkdir (approval required)
|
|
880
|
+
function createMkdirTool() {
|
|
881
|
+
const parameters = zod_1.z
|
|
882
|
+
.object({
|
|
883
|
+
path: zod_1.z.string().min(1).describe("Directory path to create"),
|
|
884
|
+
recursive: zod_1.z.boolean().optional().describe("Create parent directories as needed"),
|
|
885
|
+
})
|
|
886
|
+
.strict();
|
|
887
|
+
return (0, base_tool_1.defineTool)({
|
|
888
|
+
name: "mkdir",
|
|
889
|
+
description: "Create a directory (requires user approval)",
|
|
890
|
+
parameters,
|
|
891
|
+
validate: (args) => {
|
|
892
|
+
const result = parameters.safeParse(args);
|
|
893
|
+
return result.success
|
|
894
|
+
? {
|
|
895
|
+
valid: true,
|
|
896
|
+
value: result.data,
|
|
897
|
+
}
|
|
898
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
899
|
+
},
|
|
900
|
+
approval: {
|
|
901
|
+
message: (args, context) => effect_1.Effect.gen(function* () {
|
|
902
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
903
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
904
|
+
const target = yield* shell.resolvePathForMkdir(buildKeyFromContext(context), args.path);
|
|
905
|
+
// Check if directory already exists
|
|
906
|
+
const statResult = yield* fs
|
|
907
|
+
.stat(target)
|
|
908
|
+
.pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(null)));
|
|
909
|
+
if (statResult) {
|
|
910
|
+
if (statResult.type === "Directory") {
|
|
911
|
+
return `Directory already exists: ${target}\n\nNo action needed - the directory is already present.`;
|
|
912
|
+
}
|
|
913
|
+
else {
|
|
914
|
+
return `Path exists but is not a directory: ${target}\n\nCannot create directory at this location because a file already exists.`;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return `About to create directory: ${target}${args.recursive === false ? "" : " (with parents)"}.\n\nIMPORTANT: After getting user confirmation, you MUST call the executeMkdir tool with these exact arguments: {"path": "${args.path}", "recursive": ${args.recursive !== false}}`;
|
|
918
|
+
}),
|
|
919
|
+
errorMessage: "Approval required: Directory creation requires user confirmation.",
|
|
920
|
+
execute: {
|
|
921
|
+
toolName: "executeMkdir",
|
|
922
|
+
buildArgs: (args) => ({
|
|
923
|
+
path: args.path,
|
|
924
|
+
recursive: args.recursive,
|
|
925
|
+
}),
|
|
926
|
+
},
|
|
927
|
+
},
|
|
928
|
+
handler: (_args) => effect_1.Effect.succeed({ success: false, result: null, error: "Approval required" }),
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
function createExecuteMkdirTool() {
|
|
932
|
+
const parameters = zod_1.z
|
|
933
|
+
.object({
|
|
934
|
+
path: zod_1.z.string().min(1).describe("Directory path to create"),
|
|
935
|
+
recursive: zod_1.z.boolean().optional().describe("Create parent directories as needed"),
|
|
936
|
+
})
|
|
937
|
+
.strict();
|
|
938
|
+
return (0, base_tool_1.defineTool)({
|
|
939
|
+
name: "executeMkdir",
|
|
940
|
+
description: "Execute mkdir after user approval",
|
|
941
|
+
hidden: true,
|
|
942
|
+
parameters,
|
|
943
|
+
validate: (args) => {
|
|
944
|
+
const result = parameters.safeParse(args);
|
|
945
|
+
return result.success
|
|
946
|
+
? {
|
|
947
|
+
valid: true,
|
|
948
|
+
value: result.data,
|
|
949
|
+
}
|
|
950
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
951
|
+
},
|
|
952
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
953
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
954
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
955
|
+
const target = yield* shell.resolvePathForMkdir(buildKeyFromContext(context), args.path);
|
|
956
|
+
// Check if directory already exists
|
|
957
|
+
const statResult = yield* fs.stat(target).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(null)));
|
|
958
|
+
if (statResult) {
|
|
959
|
+
if (statResult.type === "Directory") {
|
|
960
|
+
return { success: true, result: `Directory already exists: ${target}` };
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
return {
|
|
964
|
+
success: false,
|
|
965
|
+
result: null,
|
|
966
|
+
error: `Cannot create directory '${target}': a file already exists at this path`,
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
try {
|
|
971
|
+
yield* fs.makeDirectory(target, { recursive: args.recursive !== false });
|
|
972
|
+
return { success: true, result: `Directory created: ${target}` };
|
|
973
|
+
}
|
|
974
|
+
catch (error) {
|
|
975
|
+
return {
|
|
976
|
+
success: false,
|
|
977
|
+
result: null,
|
|
978
|
+
error: `mkdir failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
}),
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
// stat - check if file/directory exists and get info
|
|
985
|
+
function createStatTool() {
|
|
986
|
+
const parameters = zod_1.z
|
|
987
|
+
.object({
|
|
988
|
+
path: zod_1.z.string().min(1).describe("File or directory path to check"),
|
|
989
|
+
})
|
|
990
|
+
.strict();
|
|
991
|
+
return (0, base_tool_1.defineTool)({
|
|
992
|
+
name: "stat",
|
|
993
|
+
description: "Check if a file or directory exists and get its information",
|
|
994
|
+
parameters,
|
|
995
|
+
validate: (args) => {
|
|
996
|
+
const result = parameters.safeParse(args);
|
|
997
|
+
return result.success
|
|
998
|
+
? {
|
|
999
|
+
valid: true,
|
|
1000
|
+
value: result.data,
|
|
1001
|
+
}
|
|
1002
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
1003
|
+
},
|
|
1004
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
1005
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
1006
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
1007
|
+
const target = yield* shell.resolvePathForMkdir(buildKeyFromContext(context), args.path);
|
|
1008
|
+
try {
|
|
1009
|
+
const stat = yield* fs.stat(target);
|
|
1010
|
+
return {
|
|
1011
|
+
success: true,
|
|
1012
|
+
result: {
|
|
1013
|
+
path: target,
|
|
1014
|
+
exists: true,
|
|
1015
|
+
type: stat.type,
|
|
1016
|
+
size: stat.size,
|
|
1017
|
+
mtime: stat.mtime,
|
|
1018
|
+
atime: stat.atime,
|
|
1019
|
+
},
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
catch (error) {
|
|
1023
|
+
// Check if it's a "not found" error
|
|
1024
|
+
if (error instanceof Error &&
|
|
1025
|
+
error.cause.code.includes("ENOENT")) {
|
|
1026
|
+
return {
|
|
1027
|
+
success: true,
|
|
1028
|
+
result: {
|
|
1029
|
+
path: target,
|
|
1030
|
+
exists: false,
|
|
1031
|
+
type: null,
|
|
1032
|
+
size: null,
|
|
1033
|
+
mtime: null,
|
|
1034
|
+
atime: null,
|
|
1035
|
+
},
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
return {
|
|
1039
|
+
success: false,
|
|
1040
|
+
result: null,
|
|
1041
|
+
error: `stat failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
}),
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
// rm (approval required)
|
|
1048
|
+
function createRmTool() {
|
|
1049
|
+
const parameters = zod_1.z
|
|
1050
|
+
.object({
|
|
1051
|
+
path: zod_1.z.string().min(1).describe("File or directory to remove"),
|
|
1052
|
+
recursive: zod_1.z.boolean().optional().describe("Recursively remove directories"),
|
|
1053
|
+
force: zod_1.z.boolean().optional().describe("Ignore non-existent files and errors"),
|
|
1054
|
+
})
|
|
1055
|
+
.strict();
|
|
1056
|
+
return (0, base_tool_1.defineTool)({
|
|
1057
|
+
name: "rm",
|
|
1058
|
+
description: "Remove a file or directory (requires user approval)",
|
|
1059
|
+
parameters,
|
|
1060
|
+
validate: (args) => {
|
|
1061
|
+
const result = parameters.safeParse(args);
|
|
1062
|
+
return result.success
|
|
1063
|
+
? {
|
|
1064
|
+
valid: true,
|
|
1065
|
+
value: result.data,
|
|
1066
|
+
}
|
|
1067
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
1068
|
+
},
|
|
1069
|
+
approval: {
|
|
1070
|
+
message: (args, context) => effect_1.Effect.gen(function* () {
|
|
1071
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
1072
|
+
const target = yield* shell.resolvePath(buildKeyFromContext(context), args.path);
|
|
1073
|
+
const recurse = args.recursive === true ? " recursively" : "";
|
|
1074
|
+
return `About to delete${recurse}: ${target}. This action may be irreversible.\nIf the user confirms, call executeRm with the same arguments.`;
|
|
1075
|
+
}),
|
|
1076
|
+
errorMessage: "Approval required: File/directory deletion requires user confirmation.",
|
|
1077
|
+
execute: {
|
|
1078
|
+
toolName: "executeRm",
|
|
1079
|
+
buildArgs: (args) => ({
|
|
1080
|
+
path: args.path,
|
|
1081
|
+
recursive: args.recursive,
|
|
1082
|
+
force: args.force,
|
|
1083
|
+
}),
|
|
1084
|
+
},
|
|
1085
|
+
},
|
|
1086
|
+
handler: (_args) => effect_1.Effect.succeed({ success: false, result: null, error: "Approval required" }),
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
function createExecuteRmTool() {
|
|
1090
|
+
const parameters = zod_1.z
|
|
1091
|
+
.object({
|
|
1092
|
+
path: zod_1.z.string().min(1).describe("File or directory to remove"),
|
|
1093
|
+
recursive: zod_1.z.boolean().optional().describe("Recursively remove directories"),
|
|
1094
|
+
force: zod_1.z.boolean().optional().describe("Ignore non-existent files and errors"),
|
|
1095
|
+
})
|
|
1096
|
+
.strict();
|
|
1097
|
+
return (0, base_tool_1.defineTool)({
|
|
1098
|
+
name: "executeRm",
|
|
1099
|
+
description: "Execute rm after user approval",
|
|
1100
|
+
hidden: true,
|
|
1101
|
+
parameters,
|
|
1102
|
+
validate: (args) => {
|
|
1103
|
+
const result = parameters.safeParse(args);
|
|
1104
|
+
return result.success
|
|
1105
|
+
? {
|
|
1106
|
+
valid: true,
|
|
1107
|
+
value: result.data,
|
|
1108
|
+
}
|
|
1109
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
1110
|
+
},
|
|
1111
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
1112
|
+
const fs = yield* platform_1.FileSystem.FileSystem;
|
|
1113
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
1114
|
+
const target = yield* shell.resolvePath(buildKeyFromContext(context), args.path);
|
|
1115
|
+
try {
|
|
1116
|
+
// Basic safeguards: do not allow deleting root or home dir directly
|
|
1117
|
+
if (target === "/" || target === process.env["HOME"]) {
|
|
1118
|
+
return {
|
|
1119
|
+
success: false,
|
|
1120
|
+
result: null,
|
|
1121
|
+
error: `Refusing to remove critical path: ${target}`,
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1124
|
+
// If not recursive and target is directory, error
|
|
1125
|
+
const st = yield* fs
|
|
1126
|
+
.stat(target)
|
|
1127
|
+
.pipe(effect_1.Effect.catchAll((err) => args.force ? effect_1.Effect.fail(err) : effect_1.Effect.fail(err)));
|
|
1128
|
+
if (st.type === "Directory" && args.recursive !== true) {
|
|
1129
|
+
return {
|
|
1130
|
+
success: false,
|
|
1131
|
+
result: null,
|
|
1132
|
+
error: `Path is a directory, use recursive: true`,
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
yield* fs.remove(target, {
|
|
1136
|
+
recursive: args.recursive === true,
|
|
1137
|
+
force: args.force === true,
|
|
1138
|
+
});
|
|
1139
|
+
return { success: true, result: `Removed: ${target}` };
|
|
1140
|
+
}
|
|
1141
|
+
catch (error) {
|
|
1142
|
+
if (args.force) {
|
|
1143
|
+
return {
|
|
1144
|
+
success: true,
|
|
1145
|
+
result: `Removal attempted with force; error ignored: ${error instanceof Error ? error.message : String(error)}`,
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
return {
|
|
1149
|
+
success: false,
|
|
1150
|
+
result: null,
|
|
1151
|
+
error: `rm failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
}),
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
// finddir - search for directories by name
|
|
1158
|
+
function createFindDirTool() {
|
|
1159
|
+
const parameters = zod_1.z
|
|
1160
|
+
.object({
|
|
1161
|
+
name: zod_1.z.string().min(1).describe("Directory name to search for (partial matches supported)"),
|
|
1162
|
+
path: zod_1.z
|
|
1163
|
+
.string()
|
|
1164
|
+
.optional()
|
|
1165
|
+
.describe("Starting path for search (defaults to current working directory)"),
|
|
1166
|
+
maxDepth: zod_1.z
|
|
1167
|
+
.number()
|
|
1168
|
+
.int()
|
|
1169
|
+
.positive()
|
|
1170
|
+
.optional()
|
|
1171
|
+
.describe("Maximum search depth (default: 3)"),
|
|
1172
|
+
})
|
|
1173
|
+
.strict();
|
|
1174
|
+
return (0, base_tool_1.defineTool)({
|
|
1175
|
+
name: "finddir",
|
|
1176
|
+
description: "Search for directories by name with partial matching",
|
|
1177
|
+
parameters,
|
|
1178
|
+
validate: (args) => {
|
|
1179
|
+
const result = parameters.safeParse(args);
|
|
1180
|
+
return result.success
|
|
1181
|
+
? {
|
|
1182
|
+
valid: true,
|
|
1183
|
+
value: result.data,
|
|
1184
|
+
}
|
|
1185
|
+
: { valid: false, errors: result.error.issues.map((i) => i.message) };
|
|
1186
|
+
},
|
|
1187
|
+
handler: (args, context) => effect_1.Effect.gen(function* () {
|
|
1188
|
+
const shell = yield* shell_1.FileSystemContextServiceTag;
|
|
1189
|
+
const startPath = args.path
|
|
1190
|
+
? yield* shell.resolvePath(buildKeyFromContext(context), args.path)
|
|
1191
|
+
: yield* shell.getCwd(buildKeyFromContext(context));
|
|
1192
|
+
const found = yield* shell.findDirectory(buildKeyFromContext(context), args.name, args.maxDepth || 3);
|
|
1193
|
+
return {
|
|
1194
|
+
success: true,
|
|
1195
|
+
result: {
|
|
1196
|
+
searchTerm: args.name,
|
|
1197
|
+
startPath,
|
|
1198
|
+
found: found.results,
|
|
1199
|
+
count: found.results.length,
|
|
1200
|
+
},
|
|
1201
|
+
};
|
|
1202
|
+
}),
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
// Registration helper
|
|
1206
|
+
function registerFileTools() {
|
|
1207
|
+
// This function is not used directly; register-tools.ts imports specific tools and registers them.
|
|
1208
|
+
return effect_1.Effect.void;
|
|
1209
|
+
}
|
|
1210
|
+
//# sourceMappingURL=fs-tools.js.map
|