@compilr-dev/agents 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 +1277 -0
- package/dist/agent.d.ts +1272 -0
- package/dist/agent.js +1912 -0
- package/dist/anchors/builtin.d.ts +24 -0
- package/dist/anchors/builtin.js +61 -0
- package/dist/anchors/index.d.ts +6 -0
- package/dist/anchors/index.js +5 -0
- package/dist/anchors/manager.d.ts +115 -0
- package/dist/anchors/manager.js +412 -0
- package/dist/anchors/types.d.ts +168 -0
- package/dist/anchors/types.js +10 -0
- package/dist/context/index.d.ts +12 -0
- package/dist/context/index.js +10 -0
- package/dist/context/manager.d.ts +224 -0
- package/dist/context/manager.js +770 -0
- package/dist/context/types.d.ts +377 -0
- package/dist/context/types.js +7 -0
- package/dist/costs/index.d.ts +8 -0
- package/dist/costs/index.js +7 -0
- package/dist/costs/tracker.d.ts +121 -0
- package/dist/costs/tracker.js +295 -0
- package/dist/costs/types.d.ts +157 -0
- package/dist/costs/types.js +8 -0
- package/dist/errors.d.ts +178 -0
- package/dist/errors.js +249 -0
- package/dist/guardrails/builtin.d.ts +27 -0
- package/dist/guardrails/builtin.js +223 -0
- package/dist/guardrails/index.d.ts +6 -0
- package/dist/guardrails/index.js +5 -0
- package/dist/guardrails/manager.d.ts +117 -0
- package/dist/guardrails/manager.js +288 -0
- package/dist/guardrails/types.d.ts +159 -0
- package/dist/guardrails/types.js +7 -0
- package/dist/hooks/index.d.ts +31 -0
- package/dist/hooks/index.js +29 -0
- package/dist/hooks/manager.d.ts +147 -0
- package/dist/hooks/manager.js +600 -0
- package/dist/hooks/types.d.ts +368 -0
- package/dist/hooks/types.js +12 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +73 -0
- package/dist/mcp/client.d.ts +93 -0
- package/dist/mcp/client.js +287 -0
- package/dist/mcp/errors.d.ts +60 -0
- package/dist/mcp/errors.js +78 -0
- package/dist/mcp/index.d.ts +43 -0
- package/dist/mcp/index.js +45 -0
- package/dist/mcp/manager.d.ts +120 -0
- package/dist/mcp/manager.js +276 -0
- package/dist/mcp/tools.d.ts +54 -0
- package/dist/mcp/tools.js +99 -0
- package/dist/mcp/types.d.ts +150 -0
- package/dist/mcp/types.js +40 -0
- package/dist/memory/index.d.ts +8 -0
- package/dist/memory/index.js +7 -0
- package/dist/memory/loader.d.ts +114 -0
- package/dist/memory/loader.js +463 -0
- package/dist/memory/types.d.ts +182 -0
- package/dist/memory/types.js +8 -0
- package/dist/messages/index.d.ts +82 -0
- package/dist/messages/index.js +155 -0
- package/dist/permissions/index.d.ts +5 -0
- package/dist/permissions/index.js +4 -0
- package/dist/permissions/manager.d.ts +125 -0
- package/dist/permissions/manager.js +379 -0
- package/dist/permissions/types.d.ts +162 -0
- package/dist/permissions/types.js +7 -0
- package/dist/providers/claude.d.ts +90 -0
- package/dist/providers/claude.js +348 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +11 -0
- package/dist/providers/mock.d.ts +133 -0
- package/dist/providers/mock.js +204 -0
- package/dist/providers/types.d.ts +168 -0
- package/dist/providers/types.js +4 -0
- package/dist/rate-limit/index.d.ts +45 -0
- package/dist/rate-limit/index.js +47 -0
- package/dist/rate-limit/limiter.d.ts +104 -0
- package/dist/rate-limit/limiter.js +326 -0
- package/dist/rate-limit/provider-wrapper.d.ts +112 -0
- package/dist/rate-limit/provider-wrapper.js +201 -0
- package/dist/rate-limit/retry.d.ts +108 -0
- package/dist/rate-limit/retry.js +287 -0
- package/dist/rate-limit/types.d.ts +181 -0
- package/dist/rate-limit/types.js +22 -0
- package/dist/rehearsal/file-analyzer.d.ts +22 -0
- package/dist/rehearsal/file-analyzer.js +351 -0
- package/dist/rehearsal/git-analyzer.d.ts +22 -0
- package/dist/rehearsal/git-analyzer.js +472 -0
- package/dist/rehearsal/index.d.ts +35 -0
- package/dist/rehearsal/index.js +36 -0
- package/dist/rehearsal/manager.d.ts +100 -0
- package/dist/rehearsal/manager.js +290 -0
- package/dist/rehearsal/types.d.ts +235 -0
- package/dist/rehearsal/types.js +8 -0
- package/dist/skills/index.d.ts +160 -0
- package/dist/skills/index.js +282 -0
- package/dist/state/agent-state.d.ts +41 -0
- package/dist/state/agent-state.js +88 -0
- package/dist/state/checkpointer.d.ts +110 -0
- package/dist/state/checkpointer.js +362 -0
- package/dist/state/errors.d.ts +66 -0
- package/dist/state/errors.js +88 -0
- package/dist/state/index.d.ts +35 -0
- package/dist/state/index.js +37 -0
- package/dist/state/serializer.d.ts +55 -0
- package/dist/state/serializer.js +172 -0
- package/dist/state/types.d.ts +312 -0
- package/dist/state/types.js +14 -0
- package/dist/tools/builtin/bash-output.d.ts +61 -0
- package/dist/tools/builtin/bash-output.js +90 -0
- package/dist/tools/builtin/bash.d.ts +150 -0
- package/dist/tools/builtin/bash.js +354 -0
- package/dist/tools/builtin/edit.d.ts +50 -0
- package/dist/tools/builtin/edit.js +215 -0
- package/dist/tools/builtin/glob.d.ts +62 -0
- package/dist/tools/builtin/glob.js +244 -0
- package/dist/tools/builtin/grep.d.ts +74 -0
- package/dist/tools/builtin/grep.js +363 -0
- package/dist/tools/builtin/index.d.ts +44 -0
- package/dist/tools/builtin/index.js +69 -0
- package/dist/tools/builtin/kill-shell.d.ts +44 -0
- package/dist/tools/builtin/kill-shell.js +80 -0
- package/dist/tools/builtin/read-file.d.ts +57 -0
- package/dist/tools/builtin/read-file.js +184 -0
- package/dist/tools/builtin/shell-manager.d.ts +176 -0
- package/dist/tools/builtin/shell-manager.js +337 -0
- package/dist/tools/builtin/task.d.ts +202 -0
- package/dist/tools/builtin/task.js +350 -0
- package/dist/tools/builtin/todo.d.ts +207 -0
- package/dist/tools/builtin/todo.js +453 -0
- package/dist/tools/builtin/utils.d.ts +27 -0
- package/dist/tools/builtin/utils.js +70 -0
- package/dist/tools/builtin/web-fetch.d.ts +96 -0
- package/dist/tools/builtin/web-fetch.js +290 -0
- package/dist/tools/builtin/write-file.d.ts +54 -0
- package/dist/tools/builtin/write-file.js +147 -0
- package/dist/tools/define.d.ts +60 -0
- package/dist/tools/define.js +65 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +37 -0
- package/dist/tools/registry.d.ts +79 -0
- package/dist/tools/registry.js +151 -0
- package/dist/tools/types.d.ts +59 -0
- package/dist/tools/types.js +4 -0
- package/dist/tracing/hooks.d.ts +58 -0
- package/dist/tracing/hooks.js +377 -0
- package/dist/tracing/index.d.ts +51 -0
- package/dist/tracing/index.js +55 -0
- package/dist/tracing/logging.d.ts +78 -0
- package/dist/tracing/logging.js +310 -0
- package/dist/tracing/manager.d.ts +160 -0
- package/dist/tracing/manager.js +468 -0
- package/dist/tracing/otel.d.ts +102 -0
- package/dist/tracing/otel.js +246 -0
- package/dist/tracing/types.d.ts +346 -0
- package/dist/tracing/types.js +38 -0
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.js +44 -0
- package/package.json +79 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit Tool - Perform targeted string replacements in files
|
|
3
|
+
*/
|
|
4
|
+
import { readFile, writeFile, access, mkdir } from 'node:fs/promises';
|
|
5
|
+
import { dirname } from 'node:path';
|
|
6
|
+
import { defineTool, createSuccessResult, createErrorResult } from '../define.js';
|
|
7
|
+
import { isNodeError, isExtensionAllowed, atomicWriteFile } from './utils.js';
|
|
8
|
+
/**
|
|
9
|
+
* Edit tool definition
|
|
10
|
+
*/
|
|
11
|
+
export const editTool = defineTool({
|
|
12
|
+
name: 'edit',
|
|
13
|
+
description: 'Perform exact string replacements in files. ' +
|
|
14
|
+
'By default, the old_string must be unique in the file. ' +
|
|
15
|
+
'Use replaceAll to replace all occurrences.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
filePath: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Path to the file to edit',
|
|
22
|
+
},
|
|
23
|
+
oldString: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'The text to search for and replace',
|
|
26
|
+
},
|
|
27
|
+
newString: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'The replacement text',
|
|
30
|
+
},
|
|
31
|
+
replaceAll: {
|
|
32
|
+
type: 'boolean',
|
|
33
|
+
description: 'Replace all occurrences instead of requiring uniqueness',
|
|
34
|
+
},
|
|
35
|
+
createIfMissing: {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
description: 'Create the file with newString content if it does not exist',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['filePath', 'oldString', 'newString'],
|
|
41
|
+
},
|
|
42
|
+
execute: executeEdit,
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Execute the edit tool
|
|
46
|
+
*/
|
|
47
|
+
async function executeEdit(input) {
|
|
48
|
+
const { filePath, oldString, newString, replaceAll = false, createIfMissing = false } = input;
|
|
49
|
+
// Validate inputs
|
|
50
|
+
if (oldString === newString) {
|
|
51
|
+
return createErrorResult('oldString and newString are identical - no change needed');
|
|
52
|
+
}
|
|
53
|
+
if (oldString === '') {
|
|
54
|
+
return createErrorResult('oldString cannot be empty');
|
|
55
|
+
}
|
|
56
|
+
// Check if file exists
|
|
57
|
+
let fileExists = true;
|
|
58
|
+
try {
|
|
59
|
+
await access(filePath);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
fileExists = false;
|
|
63
|
+
}
|
|
64
|
+
if (!fileExists) {
|
|
65
|
+
if (createIfMissing) {
|
|
66
|
+
// Create new file with the newString content
|
|
67
|
+
try {
|
|
68
|
+
// Create parent directories if they don't exist
|
|
69
|
+
const dir = dirname(filePath);
|
|
70
|
+
if (dir && dir !== '.') {
|
|
71
|
+
await mkdir(dir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
await writeFile(filePath, newString, 'utf-8');
|
|
74
|
+
return createSuccessResult({
|
|
75
|
+
filePath,
|
|
76
|
+
created: true,
|
|
77
|
+
replacements: 0,
|
|
78
|
+
message: `Created new file with content`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return createErrorResult(`Failed to create file: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return createErrorResult(`File not found: ${filePath}`);
|
|
86
|
+
}
|
|
87
|
+
// Read the file
|
|
88
|
+
let content;
|
|
89
|
+
try {
|
|
90
|
+
content = await readFile(filePath, 'utf-8');
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (isNodeError(error)) {
|
|
94
|
+
switch (error.code) {
|
|
95
|
+
case 'EACCES':
|
|
96
|
+
return createErrorResult(`Permission denied: ${filePath}`);
|
|
97
|
+
case 'EISDIR':
|
|
98
|
+
return createErrorResult(`Path is a directory: ${filePath}`);
|
|
99
|
+
default:
|
|
100
|
+
return createErrorResult(`Failed to read file: ${error.message}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
104
|
+
}
|
|
105
|
+
// Count occurrences
|
|
106
|
+
const occurrences = countOccurrences(content, oldString);
|
|
107
|
+
if (occurrences === 0) {
|
|
108
|
+
return createErrorResult(`String not found in file: "${truncate(oldString, 100)}"`);
|
|
109
|
+
}
|
|
110
|
+
if (!replaceAll && occurrences > 1) {
|
|
111
|
+
return createErrorResult(`The oldString appears ${String(occurrences)} times in the file. ` +
|
|
112
|
+
`Either provide more context to make it unique, or set replaceAll: true`);
|
|
113
|
+
}
|
|
114
|
+
// Perform the replacement
|
|
115
|
+
const newContent = replaceAll
|
|
116
|
+
? content.split(oldString).join(newString)
|
|
117
|
+
: content.replace(oldString, newString);
|
|
118
|
+
// Write the file atomically (prevents corruption on interruption)
|
|
119
|
+
try {
|
|
120
|
+
await atomicWriteFile(filePath, newContent, 'utf-8');
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return createErrorResult(`Failed to write file: ${error instanceof Error ? error.message : String(error)}`);
|
|
124
|
+
}
|
|
125
|
+
return createSuccessResult({
|
|
126
|
+
filePath,
|
|
127
|
+
created: false,
|
|
128
|
+
replacements: replaceAll ? occurrences : 1,
|
|
129
|
+
message: replaceAll ? `Replaced ${String(occurrences)} occurrences` : `Replaced 1 occurrence`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Count occurrences of a substring
|
|
134
|
+
*/
|
|
135
|
+
function countOccurrences(text, search) {
|
|
136
|
+
let count = 0;
|
|
137
|
+
let position = 0;
|
|
138
|
+
let index = text.indexOf(search, position);
|
|
139
|
+
while (index !== -1) {
|
|
140
|
+
count++;
|
|
141
|
+
position = index + 1;
|
|
142
|
+
index = text.indexOf(search, position);
|
|
143
|
+
}
|
|
144
|
+
return count;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Truncate a string for display
|
|
148
|
+
*/
|
|
149
|
+
function truncate(str, maxLength) {
|
|
150
|
+
if (str.length <= maxLength)
|
|
151
|
+
return str;
|
|
152
|
+
return str.slice(0, maxLength - 3) + '...';
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Factory function to create an edit tool with custom options
|
|
156
|
+
*/
|
|
157
|
+
export function createEditTool(options) {
|
|
158
|
+
return defineTool({
|
|
159
|
+
name: 'edit',
|
|
160
|
+
description: 'Perform exact string replacements in files. ' +
|
|
161
|
+
'By default, the old_string must be unique in the file. ' +
|
|
162
|
+
'Use replaceAll to replace all occurrences.',
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
filePath: {
|
|
167
|
+
type: 'string',
|
|
168
|
+
description: 'Path to the file to edit',
|
|
169
|
+
},
|
|
170
|
+
oldString: {
|
|
171
|
+
type: 'string',
|
|
172
|
+
description: 'The text to search for and replace',
|
|
173
|
+
},
|
|
174
|
+
newString: {
|
|
175
|
+
type: 'string',
|
|
176
|
+
description: 'The replacement text',
|
|
177
|
+
},
|
|
178
|
+
replaceAll: {
|
|
179
|
+
type: 'boolean',
|
|
180
|
+
description: 'Replace all occurrences instead of requiring uniqueness',
|
|
181
|
+
},
|
|
182
|
+
createIfMissing: {
|
|
183
|
+
type: 'boolean',
|
|
184
|
+
description: 'Create the file with newString content if it does not exist',
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
required: ['filePath', 'oldString', 'newString'],
|
|
188
|
+
},
|
|
189
|
+
execute: async (input) => {
|
|
190
|
+
let targetPath = input.filePath;
|
|
191
|
+
// Resolve relative paths
|
|
192
|
+
if (options?.baseDir && !targetPath.startsWith('/')) {
|
|
193
|
+
targetPath = `${options.baseDir}/${targetPath}`;
|
|
194
|
+
}
|
|
195
|
+
// Check allowed extensions
|
|
196
|
+
if (options?.allowedExtensions && options.allowedExtensions.length > 0) {
|
|
197
|
+
if (!isExtensionAllowed(targetPath, options.allowedExtensions)) {
|
|
198
|
+
return createErrorResult(`File extension not allowed. Allowed: ${options.allowedExtensions.join(', ')}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Check disallowed paths
|
|
202
|
+
if (options?.disallowedPaths) {
|
|
203
|
+
for (const disallowed of options.disallowedPaths) {
|
|
204
|
+
if (targetPath.includes(disallowed)) {
|
|
205
|
+
return createErrorResult(`Editing files in ${disallowed} is not allowed`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return executeEdit({
|
|
210
|
+
...input,
|
|
211
|
+
filePath: targetPath,
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob Tool - Find files matching glob patterns
|
|
3
|
+
*/
|
|
4
|
+
import type { Tool } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Input parameters for glob tool
|
|
7
|
+
*/
|
|
8
|
+
export interface GlobInput {
|
|
9
|
+
/**
|
|
10
|
+
* Glob pattern to match files against (e.g., "**\/*.ts", "src/*.js")
|
|
11
|
+
*/
|
|
12
|
+
pattern: string;
|
|
13
|
+
/**
|
|
14
|
+
* Base directory to search in (default: current directory)
|
|
15
|
+
*/
|
|
16
|
+
path?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Include hidden files and directories (default: false)
|
|
19
|
+
*/
|
|
20
|
+
includeHidden?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Only match directories (default: false)
|
|
23
|
+
*/
|
|
24
|
+
onlyDirectories?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Only match files (default: true)
|
|
27
|
+
*/
|
|
28
|
+
onlyFiles?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Maximum depth to traverse (default: unlimited)
|
|
31
|
+
*/
|
|
32
|
+
maxDepth?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Maximum number of results (default: 1000)
|
|
35
|
+
*/
|
|
36
|
+
maxResults?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Return absolute paths instead of relative (default: false)
|
|
39
|
+
*/
|
|
40
|
+
absolute?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Glob tool definition
|
|
44
|
+
*/
|
|
45
|
+
export declare const globTool: Tool<GlobInput>;
|
|
46
|
+
/**
|
|
47
|
+
* Factory function to create a glob tool with custom options
|
|
48
|
+
*/
|
|
49
|
+
export declare function createGlobTool(options?: {
|
|
50
|
+
/**
|
|
51
|
+
* Base directory to resolve relative paths against
|
|
52
|
+
*/
|
|
53
|
+
baseDir?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Default patterns to ignore
|
|
56
|
+
*/
|
|
57
|
+
ignorePatterns?: string[];
|
|
58
|
+
/**
|
|
59
|
+
* Always include these patterns regardless of ignore
|
|
60
|
+
*/
|
|
61
|
+
alwaysInclude?: string[];
|
|
62
|
+
}): Tool<GlobInput>;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glob Tool - Find files matching glob patterns
|
|
3
|
+
*/
|
|
4
|
+
import { readdir } from 'node:fs/promises';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { defineTool, createSuccessResult, createErrorResult } from '../define.js';
|
|
7
|
+
import { isNodeError } from './utils.js';
|
|
8
|
+
/**
|
|
9
|
+
* Glob tool definition
|
|
10
|
+
*/
|
|
11
|
+
export const globTool = defineTool({
|
|
12
|
+
name: 'glob',
|
|
13
|
+
description: 'Find files matching glob patterns. ' +
|
|
14
|
+
'Supports common patterns like **/*.ts for recursive matching. ' +
|
|
15
|
+
'Returns a list of matching file paths.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
pattern: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Glob pattern (e.g., "**/*.ts", "src/**/*.js", "*.md")',
|
|
22
|
+
},
|
|
23
|
+
path: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Base directory to search in (default: current directory)',
|
|
26
|
+
},
|
|
27
|
+
includeHidden: {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
description: 'Include hidden files/directories (default: false)',
|
|
30
|
+
},
|
|
31
|
+
onlyDirectories: {
|
|
32
|
+
type: 'boolean',
|
|
33
|
+
description: 'Only match directories',
|
|
34
|
+
},
|
|
35
|
+
onlyFiles: {
|
|
36
|
+
type: 'boolean',
|
|
37
|
+
description: 'Only match files (default: true)',
|
|
38
|
+
},
|
|
39
|
+
maxDepth: {
|
|
40
|
+
type: 'number',
|
|
41
|
+
description: 'Maximum directory depth to traverse',
|
|
42
|
+
},
|
|
43
|
+
maxResults: {
|
|
44
|
+
type: 'number',
|
|
45
|
+
description: 'Maximum number of results (default: 1000)',
|
|
46
|
+
},
|
|
47
|
+
absolute: {
|
|
48
|
+
type: 'boolean',
|
|
49
|
+
description: 'Return absolute paths instead of relative',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ['pattern'],
|
|
53
|
+
},
|
|
54
|
+
execute: executeGlob,
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* Execute the glob tool
|
|
58
|
+
*/
|
|
59
|
+
async function executeGlob(input) {
|
|
60
|
+
const { pattern, path: basePath = '.', includeHidden = false, onlyDirectories = false, onlyFiles = !onlyDirectories, maxDepth, maxResults = 1000, absolute = false, } = input;
|
|
61
|
+
// Check if base path exists
|
|
62
|
+
try {
|
|
63
|
+
await readdir(basePath);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (isNodeError(error)) {
|
|
67
|
+
switch (error.code) {
|
|
68
|
+
case 'ENOENT':
|
|
69
|
+
return createErrorResult(`Directory not found: ${basePath}`);
|
|
70
|
+
case 'EACCES':
|
|
71
|
+
return createErrorResult(`Permission denied: ${basePath}`);
|
|
72
|
+
default:
|
|
73
|
+
return createErrorResult(`Glob failed: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
// Parse the glob pattern
|
|
80
|
+
const globRegex = patternToRegex(pattern);
|
|
81
|
+
const hasRecursive = pattern.includes('**');
|
|
82
|
+
// Collect matching files
|
|
83
|
+
const matches = [];
|
|
84
|
+
await searchDirectory(basePath, '', 0, {
|
|
85
|
+
pattern: globRegex,
|
|
86
|
+
hasRecursive,
|
|
87
|
+
includeHidden,
|
|
88
|
+
onlyDirectories,
|
|
89
|
+
onlyFiles,
|
|
90
|
+
maxDepth,
|
|
91
|
+
maxResults,
|
|
92
|
+
matches,
|
|
93
|
+
basePath,
|
|
94
|
+
});
|
|
95
|
+
// Format output
|
|
96
|
+
const results = absolute ? matches.map((m) => join(basePath, m)) : matches;
|
|
97
|
+
return createSuccessResult({
|
|
98
|
+
files: results,
|
|
99
|
+
count: results.length,
|
|
100
|
+
truncated: results.length >= maxResults,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Recursively search a directory for matching files
|
|
109
|
+
*/
|
|
110
|
+
async function searchDirectory(basePath, relativePath, depth, options) {
|
|
111
|
+
// Check if we've reached max results
|
|
112
|
+
if (options.matches.length >= options.maxResults) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Check max depth
|
|
116
|
+
if (options.maxDepth !== undefined && depth > options.maxDepth) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const currentPath = relativePath ? join(basePath, relativePath) : basePath;
|
|
120
|
+
let entries;
|
|
121
|
+
try {
|
|
122
|
+
entries = await readdir(currentPath, { withFileTypes: true });
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Skip directories we can't read
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
if (options.matches.length >= options.maxResults) {
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
// Skip hidden files unless included
|
|
133
|
+
if (!options.includeHidden && entry.name.startsWith('.')) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const entryRelativePath = relativePath ? join(relativePath, entry.name) : entry.name;
|
|
137
|
+
const isDir = entry.isDirectory();
|
|
138
|
+
const isFile = entry.isFile();
|
|
139
|
+
// Check if this entry matches the pattern
|
|
140
|
+
if (options.pattern.test(entryRelativePath)) {
|
|
141
|
+
if ((isDir && !options.onlyFiles) || (isFile && !options.onlyDirectories)) {
|
|
142
|
+
options.matches.push(entryRelativePath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Recurse into directories
|
|
146
|
+
if (isDir && (options.hasRecursive || shouldDescend(entryRelativePath, options.pattern))) {
|
|
147
|
+
await searchDirectory(basePath, entryRelativePath, depth + 1, options);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check if we should descend into a directory based on the pattern
|
|
153
|
+
*/
|
|
154
|
+
function shouldDescend(_path, _pattern) {
|
|
155
|
+
// If the pattern might match files in this directory, descend
|
|
156
|
+
// This is a simplified check - just return true for non-** patterns
|
|
157
|
+
// that might have directory prefixes
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Convert a glob pattern to a regular expression
|
|
162
|
+
*/
|
|
163
|
+
function patternToRegex(pattern) {
|
|
164
|
+
// Handle special case: **/* means match anything
|
|
165
|
+
if (pattern === '**/*') {
|
|
166
|
+
return new RegExp('^.+$');
|
|
167
|
+
}
|
|
168
|
+
// Handle **/ at the start first (matches any directory prefix including none)
|
|
169
|
+
let workingPattern = pattern;
|
|
170
|
+
let prefix = '';
|
|
171
|
+
if (workingPattern.startsWith('**/')) {
|
|
172
|
+
prefix = '(?:.*/|)';
|
|
173
|
+
workingPattern = workingPattern.slice(3); // Remove the **/ part
|
|
174
|
+
}
|
|
175
|
+
// Escape special regex characters except glob characters (* and ?)
|
|
176
|
+
let regexStr = workingPattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
177
|
+
// Handle remaining ** (matches anything including /)
|
|
178
|
+
regexStr = regexStr.replace(/\*\*/g, '.*');
|
|
179
|
+
// Handle remaining * (matches anything except /)
|
|
180
|
+
regexStr = regexStr.replace(/\*/g, '[^/]*');
|
|
181
|
+
// Handle ? (matches single character except /)
|
|
182
|
+
regexStr = regexStr.replace(/\?/g, '[^/]');
|
|
183
|
+
return new RegExp('^' + prefix + regexStr + '$');
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Factory function to create a glob tool with custom options
|
|
187
|
+
*/
|
|
188
|
+
export function createGlobTool(options) {
|
|
189
|
+
return defineTool({
|
|
190
|
+
name: 'glob',
|
|
191
|
+
description: 'Find files matching glob patterns. ' +
|
|
192
|
+
'Supports common patterns like **/*.ts for recursive matching. ' +
|
|
193
|
+
'Returns a list of matching file paths.',
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: 'object',
|
|
196
|
+
properties: {
|
|
197
|
+
pattern: {
|
|
198
|
+
type: 'string',
|
|
199
|
+
description: 'Glob pattern (e.g., "**/*.ts", "src/**/*.js", "*.md")',
|
|
200
|
+
},
|
|
201
|
+
path: {
|
|
202
|
+
type: 'string',
|
|
203
|
+
description: 'Base directory to search in (default: current directory)',
|
|
204
|
+
},
|
|
205
|
+
includeHidden: {
|
|
206
|
+
type: 'boolean',
|
|
207
|
+
description: 'Include hidden files/directories (default: false)',
|
|
208
|
+
},
|
|
209
|
+
onlyDirectories: {
|
|
210
|
+
type: 'boolean',
|
|
211
|
+
description: 'Only match directories',
|
|
212
|
+
},
|
|
213
|
+
onlyFiles: {
|
|
214
|
+
type: 'boolean',
|
|
215
|
+
description: 'Only match files (default: true)',
|
|
216
|
+
},
|
|
217
|
+
maxDepth: {
|
|
218
|
+
type: 'number',
|
|
219
|
+
description: 'Maximum directory depth to traverse',
|
|
220
|
+
},
|
|
221
|
+
maxResults: {
|
|
222
|
+
type: 'number',
|
|
223
|
+
description: 'Maximum number of results (default: 1000)',
|
|
224
|
+
},
|
|
225
|
+
absolute: {
|
|
226
|
+
type: 'boolean',
|
|
227
|
+
description: 'Return absolute paths instead of relative',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
required: ['pattern'],
|
|
231
|
+
},
|
|
232
|
+
execute: async (input) => {
|
|
233
|
+
let searchPath = input.path ?? '.';
|
|
234
|
+
// Resolve relative paths
|
|
235
|
+
if (options?.baseDir && !searchPath.startsWith('/')) {
|
|
236
|
+
searchPath = join(options.baseDir, searchPath);
|
|
237
|
+
}
|
|
238
|
+
return executeGlob({
|
|
239
|
+
...input,
|
|
240
|
+
path: searchPath,
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grep Tool - Search for patterns in files using regex
|
|
3
|
+
*/
|
|
4
|
+
import type { Tool } from '../types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Input parameters for grep tool
|
|
7
|
+
*/
|
|
8
|
+
export interface GrepInput {
|
|
9
|
+
/**
|
|
10
|
+
* Regular expression pattern to search for
|
|
11
|
+
*/
|
|
12
|
+
pattern: string;
|
|
13
|
+
/**
|
|
14
|
+
* Path to file or directory to search in
|
|
15
|
+
*/
|
|
16
|
+
path: string;
|
|
17
|
+
/**
|
|
18
|
+
* Case insensitive search (default: false)
|
|
19
|
+
*/
|
|
20
|
+
ignoreCase?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Include line numbers in output (default: true)
|
|
23
|
+
*/
|
|
24
|
+
lineNumbers?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Number of context lines before match (default: 0)
|
|
27
|
+
*/
|
|
28
|
+
before?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Number of context lines after match (default: 0)
|
|
31
|
+
*/
|
|
32
|
+
after?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Only return filenames with matches (default: false)
|
|
35
|
+
*/
|
|
36
|
+
filesOnly?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Include hidden files/directories (default: false)
|
|
39
|
+
*/
|
|
40
|
+
includeHidden?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* File extensions to include (e.g., ['.ts', '.js'])
|
|
43
|
+
*/
|
|
44
|
+
extensions?: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Maximum number of matches to return (default: 100)
|
|
47
|
+
*/
|
|
48
|
+
maxMatches?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Search recursively in directories (default: true)
|
|
51
|
+
*/
|
|
52
|
+
recursive?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Grep tool definition
|
|
56
|
+
*/
|
|
57
|
+
export declare const grepTool: Tool<GrepInput>;
|
|
58
|
+
/**
|
|
59
|
+
* Factory function to create a grep tool with custom options
|
|
60
|
+
*
|
|
61
|
+
* TODO: Future enhancements could include:
|
|
62
|
+
* - maxFileSize: Skip files larger than specified size
|
|
63
|
+
* - excludeDirs: Exclude directories by name (e.g., node_modules, .git)
|
|
64
|
+
*/
|
|
65
|
+
export declare function createGrepTool(options?: {
|
|
66
|
+
/**
|
|
67
|
+
* Base directory to resolve relative paths against
|
|
68
|
+
*/
|
|
69
|
+
baseDir?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Default file extensions to search
|
|
72
|
+
*/
|
|
73
|
+
defaultExtensions?: string[];
|
|
74
|
+
}): Tool<GrepInput>;
|