wave-agent-sdk 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/agent.d.ts +96 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +286 -0
- package/dist/hooks/executor.d.ts +56 -0
- package/dist/hooks/executor.d.ts.map +1 -0
- package/dist/hooks/executor.js +312 -0
- package/dist/hooks/index.d.ts +17 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +14 -0
- package/dist/hooks/manager.d.ts +90 -0
- package/dist/hooks/manager.d.ts.map +1 -0
- package/dist/hooks/manager.js +395 -0
- package/dist/hooks/matcher.d.ts +49 -0
- package/dist/hooks/matcher.d.ts.map +1 -0
- package/dist/hooks/matcher.js +147 -0
- package/dist/hooks/settings.d.ts +46 -0
- package/dist/hooks/settings.d.ts.map +1 -0
- package/dist/hooks/settings.js +100 -0
- package/dist/hooks/types.d.ts +80 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +59 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/managers/aiManager.d.ts +61 -0
- package/dist/managers/aiManager.d.ts.map +1 -0
- package/dist/managers/aiManager.js +415 -0
- package/dist/managers/backgroundBashManager.d.ts +27 -0
- package/dist/managers/backgroundBashManager.d.ts.map +1 -0
- package/dist/managers/backgroundBashManager.js +166 -0
- package/dist/managers/bashManager.d.ts +20 -0
- package/dist/managers/bashManager.d.ts.map +1 -0
- package/dist/managers/bashManager.js +66 -0
- package/dist/managers/mcpManager.d.ts +63 -0
- package/dist/managers/mcpManager.d.ts.map +1 -0
- package/dist/managers/mcpManager.js +378 -0
- package/dist/managers/messageManager.d.ts +85 -0
- package/dist/managers/messageManager.d.ts.map +1 -0
- package/dist/managers/messageManager.js +265 -0
- package/dist/managers/skillManager.d.ts +59 -0
- package/dist/managers/skillManager.d.ts.map +1 -0
- package/dist/managers/skillManager.js +317 -0
- package/dist/managers/slashCommandManager.d.ts +77 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -0
- package/dist/managers/slashCommandManager.js +208 -0
- package/dist/managers/toolManager.d.ts +23 -0
- package/dist/managers/toolManager.d.ts.map +1 -0
- package/dist/managers/toolManager.js +79 -0
- package/dist/services/aiService.d.ts +28 -0
- package/dist/services/aiService.d.ts.map +1 -0
- package/dist/services/aiService.js +180 -0
- package/dist/services/memory.d.ts +8 -0
- package/dist/services/memory.d.ts.map +1 -0
- package/dist/services/memory.js +128 -0
- package/dist/services/session.d.ts +54 -0
- package/dist/services/session.d.ts.map +1 -0
- package/dist/services/session.js +196 -0
- package/dist/tools/bashTool.d.ts +14 -0
- package/dist/tools/bashTool.d.ts.map +1 -0
- package/dist/tools/bashTool.js +351 -0
- package/dist/tools/deleteFileTool.d.ts +6 -0
- package/dist/tools/deleteFileTool.d.ts.map +1 -0
- package/dist/tools/deleteFileTool.js +67 -0
- package/dist/tools/editTool.d.ts +6 -0
- package/dist/tools/editTool.d.ts.map +1 -0
- package/dist/tools/editTool.js +168 -0
- package/dist/tools/globTool.d.ts +6 -0
- package/dist/tools/globTool.d.ts.map +1 -0
- package/dist/tools/globTool.js +113 -0
- package/dist/tools/grepTool.d.ts +6 -0
- package/dist/tools/grepTool.d.ts.map +1 -0
- package/dist/tools/grepTool.js +268 -0
- package/dist/tools/lsTool.d.ts +6 -0
- package/dist/tools/lsTool.d.ts.map +1 -0
- package/dist/tools/lsTool.js +160 -0
- package/dist/tools/multiEditTool.d.ts +6 -0
- package/dist/tools/multiEditTool.d.ts.map +1 -0
- package/dist/tools/multiEditTool.js +222 -0
- package/dist/tools/readTool.d.ts +6 -0
- package/dist/tools/readTool.d.ts.map +1 -0
- package/dist/tools/readTool.js +136 -0
- package/dist/tools/types.d.ts +35 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +4 -0
- package/dist/tools/writeTool.d.ts +6 -0
- package/dist/tools/writeTool.d.ts.map +1 -0
- package/dist/tools/writeTool.js +138 -0
- package/dist/types.d.ts +212 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/utils/bashHistory.d.ts +46 -0
- package/dist/utils/bashHistory.d.ts.map +1 -0
- package/dist/utils/bashHistory.js +236 -0
- package/dist/utils/commandArgumentParser.d.ts +34 -0
- package/dist/utils/commandArgumentParser.d.ts.map +1 -0
- package/dist/utils/commandArgumentParser.js +123 -0
- package/dist/utils/constants.d.ts +27 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +28 -0
- package/dist/utils/convertMessagesForAPI.d.ts +9 -0
- package/dist/utils/convertMessagesForAPI.d.ts.map +1 -0
- package/dist/utils/convertMessagesForAPI.js +189 -0
- package/dist/utils/customCommands.d.ts +14 -0
- package/dist/utils/customCommands.d.ts.map +1 -0
- package/dist/utils/customCommands.js +71 -0
- package/dist/utils/fileFilter.d.ts +26 -0
- package/dist/utils/fileFilter.d.ts.map +1 -0
- package/dist/utils/fileFilter.js +177 -0
- package/dist/utils/markdownParser.d.ts +27 -0
- package/dist/utils/markdownParser.d.ts.map +1 -0
- package/dist/utils/markdownParser.js +109 -0
- package/dist/utils/mcpUtils.d.ts +24 -0
- package/dist/utils/mcpUtils.d.ts.map +1 -0
- package/dist/utils/mcpUtils.js +51 -0
- package/dist/utils/messageOperations.d.ts +118 -0
- package/dist/utils/messageOperations.d.ts.map +1 -0
- package/dist/utils/messageOperations.js +334 -0
- package/dist/utils/path.d.ts +25 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +109 -0
- package/dist/utils/skillParser.d.ts +18 -0
- package/dist/utils/skillParser.d.ts.map +1 -0
- package/dist/utils/skillParser.js +147 -0
- package/dist/utils/stringUtils.d.ts +13 -0
- package/dist/utils/stringUtils.d.ts.map +1 -0
- package/dist/utils/stringUtils.js +44 -0
- package/package.json +51 -0
- package/src/agent.ts +405 -0
- package/src/hooks/executor.ts +440 -0
- package/src/hooks/index.ts +52 -0
- package/src/hooks/manager.ts +618 -0
- package/src/hooks/matcher.ts +187 -0
- package/src/hooks/settings.ts +129 -0
- package/src/hooks/types.ts +169 -0
- package/src/index.ts +24 -0
- package/src/managers/aiManager.ts +573 -0
- package/src/managers/backgroundBashManager.ts +203 -0
- package/src/managers/bashManager.ts +97 -0
- package/src/managers/mcpManager.ts +493 -0
- package/src/managers/messageManager.ts +415 -0
- package/src/managers/skillManager.ts +404 -0
- package/src/managers/slashCommandManager.ts +293 -0
- package/src/managers/toolManager.ts +106 -0
- package/src/services/aiService.ts +252 -0
- package/src/services/memory.ts +149 -0
- package/src/services/session.ts +265 -0
- package/src/tools/bashTool.ts +402 -0
- package/src/tools/deleteFileTool.ts +81 -0
- package/src/tools/editTool.ts +192 -0
- package/src/tools/globTool.ts +135 -0
- package/src/tools/grepTool.ts +326 -0
- package/src/tools/lsTool.ts +187 -0
- package/src/tools/multiEditTool.ts +268 -0
- package/src/tools/readTool.ts +165 -0
- package/src/tools/types.ts +47 -0
- package/src/tools/writeTool.ts +163 -0
- package/src/types.ts +260 -0
- package/src/utils/bashHistory.ts +303 -0
- package/src/utils/commandArgumentParser.ts +153 -0
- package/src/utils/constants.ts +37 -0
- package/src/utils/convertMessagesForAPI.ts +236 -0
- package/src/utils/customCommands.ts +85 -0
- package/src/utils/fileFilter.ts +202 -0
- package/src/utils/markdownParser.ts +156 -0
- package/src/utils/mcpUtils.ts +81 -0
- package/src/utils/messageOperations.ts +506 -0
- package/src/utils/path.ts +118 -0
- package/src/utils/skillParser.ts +188 -0
- package/src/utils/stringUtils.ts +50 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { unlink } from "fs/promises";
|
|
2
|
+
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
3
|
+
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Delete file tool plugin
|
|
7
|
+
*/
|
|
8
|
+
export const deleteFileTool: ToolPlugin = {
|
|
9
|
+
name: "Delete",
|
|
10
|
+
config: {
|
|
11
|
+
type: "function",
|
|
12
|
+
function: {
|
|
13
|
+
name: "Delete",
|
|
14
|
+
description: `Deletes a file at the specified path. The operation will fail gracefully if:
|
|
15
|
+
- The file doesn't exist
|
|
16
|
+
- The operation is rejected for security reasons
|
|
17
|
+
- The file cannot be deleted`,
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
target_file: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description:
|
|
24
|
+
"The path of the file to delete, relative to the workspace root.",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
required: ["target_file"],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
execute: async (
|
|
32
|
+
args: Record<string, unknown>,
|
|
33
|
+
context: ToolContext,
|
|
34
|
+
): Promise<ToolResult> => {
|
|
35
|
+
const targetFile = args.target_file as string;
|
|
36
|
+
|
|
37
|
+
if (!targetFile || typeof targetFile !== "string") {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
content: "",
|
|
41
|
+
error: "target_file parameter is required and must be a string",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const filePath = resolvePath(targetFile, context.workdir);
|
|
47
|
+
|
|
48
|
+
// Delete file
|
|
49
|
+
await unlink(filePath);
|
|
50
|
+
|
|
51
|
+
// logger.info(`Successfully deleted file: ${filePath}`);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
content: `Successfully deleted file: ${targetFile}`,
|
|
56
|
+
shortResult: "File deleted",
|
|
57
|
+
};
|
|
58
|
+
} catch (error) {
|
|
59
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
60
|
+
return {
|
|
61
|
+
success: false,
|
|
62
|
+
content: "",
|
|
63
|
+
error: `File does not exist: ${targetFile}`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
content: "",
|
|
70
|
+
error: error instanceof Error ? error.message : String(error),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
formatCompactParams: (
|
|
75
|
+
params: Record<string, unknown>,
|
|
76
|
+
context: ToolContext,
|
|
77
|
+
) => {
|
|
78
|
+
const targetFile = params.target_file as string;
|
|
79
|
+
return getDisplayPath(targetFile || "", context.workdir);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { readFile, writeFile } from "fs/promises";
|
|
2
|
+
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
3
|
+
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
4
|
+
import { diffLines } from "diff";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format compact parameter display
|
|
8
|
+
*/
|
|
9
|
+
function formatCompactParams(
|
|
10
|
+
args: Record<string, unknown>,
|
|
11
|
+
context: ToolContext,
|
|
12
|
+
): string {
|
|
13
|
+
const filePath = args.file_path as string;
|
|
14
|
+
return getDisplayPath(filePath || "", context.workdir);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Single file edit tool plugin
|
|
19
|
+
*/
|
|
20
|
+
export const editTool: ToolPlugin = {
|
|
21
|
+
name: "Edit",
|
|
22
|
+
formatCompactParams,
|
|
23
|
+
config: {
|
|
24
|
+
type: "function",
|
|
25
|
+
function: {
|
|
26
|
+
name: "Edit",
|
|
27
|
+
description:
|
|
28
|
+
"Performs exact string replacements in files. \n\nUsage:\n- You must use your `Read` tool at least once in the conversation before editing. This tool will error if you attempt an edit without reading the file. \n- When editing text from read_file tool output, ensure you preserve the exact indentation (tabs/spaces) as it appears AFTER the line number prefix. The line number prefix format is: spaces + line number + tab. Everything after that tab is the actual file content to match. Never include any part of the line number prefix in the old_string or new_string.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly required.\n- Only use emojis if the user explicitly requests it. Avoid adding emojis to files unless asked.\n- The edit will FAIL if `old_string` is not unique in the file. Either provide a larger string with more surrounding context to make it unique or use `replace_all` to change every instance of `old_string`. \n- Use `replace_all` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.",
|
|
29
|
+
parameters: {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
file_path: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "The absolute path to the file to modify",
|
|
35
|
+
},
|
|
36
|
+
old_string: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "The text to replace",
|
|
39
|
+
},
|
|
40
|
+
new_string: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description:
|
|
43
|
+
"The text to replace it with (must be different from old_string)",
|
|
44
|
+
},
|
|
45
|
+
replace_all: {
|
|
46
|
+
type: "boolean",
|
|
47
|
+
default: false,
|
|
48
|
+
description: "Replace all occurences of old_string (default false)",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ["file_path", "old_string", "new_string"],
|
|
52
|
+
additionalProperties: false,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
execute: async (
|
|
57
|
+
args: Record<string, unknown>,
|
|
58
|
+
context: ToolContext,
|
|
59
|
+
): Promise<ToolResult> => {
|
|
60
|
+
const filePath = args.file_path as string;
|
|
61
|
+
const oldString = args.old_string as string;
|
|
62
|
+
const newString = args.new_string as string;
|
|
63
|
+
const replaceAll = (args.replace_all as boolean) || false;
|
|
64
|
+
|
|
65
|
+
// Validate required parameters
|
|
66
|
+
if (!filePath || typeof filePath !== "string") {
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
content: "",
|
|
70
|
+
error: "file_path parameter is required and must be a string",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (typeof oldString !== "string") {
|
|
75
|
+
return {
|
|
76
|
+
success: false,
|
|
77
|
+
content: "",
|
|
78
|
+
error: "old_string parameter is required and must be a string",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (typeof newString !== "string") {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
content: "",
|
|
86
|
+
error: "new_string parameter is required and must be a string",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (oldString === newString) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
content: "",
|
|
94
|
+
error: "old_string and new_string must be different",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const resolvedPath = resolvePath(filePath, context.workdir);
|
|
100
|
+
|
|
101
|
+
// Read file content
|
|
102
|
+
let originalContent: string;
|
|
103
|
+
try {
|
|
104
|
+
originalContent = await readFile(resolvedPath, "utf-8");
|
|
105
|
+
} catch (readError) {
|
|
106
|
+
return {
|
|
107
|
+
success: false,
|
|
108
|
+
content: "",
|
|
109
|
+
error: `Failed to read file: ${readError instanceof Error ? readError.message : String(readError)}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check if old_string exists
|
|
114
|
+
if (!originalContent.includes(oldString)) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
content: "",
|
|
118
|
+
error: `old_string not found in file`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let newContent: string;
|
|
123
|
+
let replacementCount: number;
|
|
124
|
+
|
|
125
|
+
if (replaceAll) {
|
|
126
|
+
// Replace all matches
|
|
127
|
+
const regex = new RegExp(escapeRegExp(oldString), "g");
|
|
128
|
+
newContent = originalContent.replace(regex, newString);
|
|
129
|
+
replacementCount = (originalContent.match(regex) || []).length;
|
|
130
|
+
} else {
|
|
131
|
+
// Replace only the first match, but first check if it's unique
|
|
132
|
+
const matches = originalContent.split(oldString).length - 1;
|
|
133
|
+
if (matches > 1) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
content: "",
|
|
137
|
+
error: `old_string appears ${matches} times in the file. Either provide a larger string with more surrounding context to make it unique or use replace_all=true to change every instance.`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
newContent = originalContent.replace(oldString, newString);
|
|
142
|
+
replacementCount = 1;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Write file
|
|
146
|
+
try {
|
|
147
|
+
await writeFile(resolvedPath, newContent, "utf-8");
|
|
148
|
+
} catch (writeError) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
content: "",
|
|
152
|
+
error: `Failed to write file: ${writeError instanceof Error ? writeError.message : String(writeError)}`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Generate diff information
|
|
157
|
+
const diffResult = diffLines(originalContent, newContent);
|
|
158
|
+
|
|
159
|
+
const shortResult = replaceAll
|
|
160
|
+
? `Replaced ${replacementCount} instances`
|
|
161
|
+
: "Text replaced successfully";
|
|
162
|
+
|
|
163
|
+
// logger.info(`Edit tool: ${shortResult}`);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
success: true,
|
|
167
|
+
content: shortResult,
|
|
168
|
+
shortResult,
|
|
169
|
+
filePath: resolvedPath,
|
|
170
|
+
originalContent,
|
|
171
|
+
newContent,
|
|
172
|
+
diffResult,
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
const errorMessage =
|
|
176
|
+
error instanceof Error ? error.message : String(error);
|
|
177
|
+
// logger.error(`Edit tool error: ${errorMessage}`);
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
content: "",
|
|
181
|
+
error: errorMessage,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Escape regular expression special characters
|
|
189
|
+
*/
|
|
190
|
+
function escapeRegExp(string: string): string {
|
|
191
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
192
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { glob } from "glob";
|
|
2
|
+
import { stat } from "fs/promises";
|
|
3
|
+
import type { ToolPlugin, ToolResult, ToolContext } from "./types.js";
|
|
4
|
+
import { resolvePath, getDisplayPath } from "../utils/path.js";
|
|
5
|
+
import { getGlobIgnorePatterns } from "../utils/fileFilter.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Glob Tool Plugin - Fast file pattern matching
|
|
9
|
+
*/
|
|
10
|
+
export const globTool: ToolPlugin = {
|
|
11
|
+
name: "Glob",
|
|
12
|
+
config: {
|
|
13
|
+
type: "function",
|
|
14
|
+
function: {
|
|
15
|
+
name: "Glob",
|
|
16
|
+
description:
|
|
17
|
+
'- Fast file pattern matching tool that works with any codebase size\n- Supports glob patterns like "**/*.js" or "src/**/*.ts"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns\n- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Agent tool instead\n- You have the capability to call multiple tools in a single response. It is always better to speculatively perform multiple searches as a batch that are potentially useful.',
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
pattern: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "The glob pattern to match files against",
|
|
24
|
+
},
|
|
25
|
+
path: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description:
|
|
28
|
+
'The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: ["pattern"],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
execute: async (
|
|
36
|
+
args: Record<string, unknown>,
|
|
37
|
+
context: ToolContext,
|
|
38
|
+
): Promise<ToolResult> => {
|
|
39
|
+
const pattern = args.pattern as string;
|
|
40
|
+
const searchPath = args.path as string;
|
|
41
|
+
|
|
42
|
+
if (!pattern || typeof pattern !== "string") {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
content: "",
|
|
46
|
+
error: "pattern parameter is required and must be a string",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Determine search directory
|
|
52
|
+
const workdir = searchPath
|
|
53
|
+
? resolvePath(searchPath, context.workdir)
|
|
54
|
+
: context.workdir;
|
|
55
|
+
|
|
56
|
+
// Execute glob search
|
|
57
|
+
const matches = await glob(pattern, {
|
|
58
|
+
cwd: workdir,
|
|
59
|
+
ignore: getGlobIgnorePatterns(workdir),
|
|
60
|
+
dot: false,
|
|
61
|
+
absolute: false,
|
|
62
|
+
nocase: false, // Keep case sensitive
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (matches.length === 0) {
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
content: "No files match the pattern",
|
|
69
|
+
shortResult: "No matches found",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Get file modification time and sort
|
|
74
|
+
const filesWithStats = await Promise.allSettled(
|
|
75
|
+
matches.map(async (file) => {
|
|
76
|
+
try {
|
|
77
|
+
const fullPath = resolvePath(file, context.workdir);
|
|
78
|
+
const stats = await stat(fullPath);
|
|
79
|
+
return {
|
|
80
|
+
path: file,
|
|
81
|
+
mtime: stats.mtime,
|
|
82
|
+
};
|
|
83
|
+
} catch {
|
|
84
|
+
// If unable to get file stats, use current time
|
|
85
|
+
return {
|
|
86
|
+
path: file,
|
|
87
|
+
mtime: new Date(),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Filter successful results and sort by modification time
|
|
94
|
+
const sortedFiles = filesWithStats
|
|
95
|
+
.filter((result) => result.status === "fulfilled")
|
|
96
|
+
.map(
|
|
97
|
+
(result) =>
|
|
98
|
+
(result as PromiseFulfilledResult<{ path: string; mtime: Date }>)
|
|
99
|
+
.value,
|
|
100
|
+
)
|
|
101
|
+
.sort((a, b) => b.mtime.getTime() - a.mtime.getTime()) // Most recently modified files first
|
|
102
|
+
.map((item) => item.path);
|
|
103
|
+
|
|
104
|
+
// Format output
|
|
105
|
+
const output = sortedFiles
|
|
106
|
+
.map((file, index) => `${index + 1}. ${file}`)
|
|
107
|
+
.join("\n");
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
content: output,
|
|
112
|
+
shortResult: `Found ${sortedFiles.length} file${sortedFiles.length === 1 ? "" : "s"}`,
|
|
113
|
+
};
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
content: "",
|
|
118
|
+
error: `Glob search failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
formatCompactParams: (
|
|
123
|
+
params: Record<string, unknown>,
|
|
124
|
+
context: ToolContext,
|
|
125
|
+
) => {
|
|
126
|
+
const pattern = params.pattern as string;
|
|
127
|
+
const path = params.path as string;
|
|
128
|
+
|
|
129
|
+
if (path) {
|
|
130
|
+
const displayPath = getDisplayPath(path, context.workdir);
|
|
131
|
+
return `${pattern} in ${displayPath}`;
|
|
132
|
+
}
|
|
133
|
+
return pattern || "";
|
|
134
|
+
},
|
|
135
|
+
};
|