@claude-code-kit/tools 0.2.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 +38 -0
- package/dist/index.d.mts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +412 -0
- package/dist/index.mjs +368 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Minnzen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @claude-code-kit/tools
|
|
2
|
+
|
|
3
|
+
Built-in tool collection for the [claude-code-kit](https://github.com/Minnzen/claude-code-kit) agent framework.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
| Tool | Description | Read-only |
|
|
8
|
+
|------|-------------|-----------|
|
|
9
|
+
| `bash` | Execute shell commands | No |
|
|
10
|
+
| `read` | Read file contents with line numbers | Yes |
|
|
11
|
+
| `edit` | Edit files via unique string replacement | No |
|
|
12
|
+
| `write` | Write/create files with auto-mkdir | No |
|
|
13
|
+
| `glob` | Find files by glob pattern | Yes |
|
|
14
|
+
| `grep` | Search file contents with regex | Yes |
|
|
15
|
+
| `web_fetch` | Make HTTP requests | Yes (GET) |
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { Agent, AnthropicProvider } from "@claude-code-kit/agent";
|
|
21
|
+
import { builtinTools } from "@claude-code-kit/tools";
|
|
22
|
+
|
|
23
|
+
const agent = new Agent({
|
|
24
|
+
provider: new AnthropicProvider({ apiKey: "..." }),
|
|
25
|
+
model: "claude-sonnet-4-20250514",
|
|
26
|
+
tools: builtinTools,
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or import individual tools:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { bashTool, readTool, editTool } from "@claude-code-kit/tools";
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ToolDefinition } from '@claude-code-kit/agent';
|
|
3
|
+
|
|
4
|
+
declare const inputSchema$6: z.ZodObject<{
|
|
5
|
+
command: z.ZodString;
|
|
6
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
7
|
+
timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
type Input$6 = z.infer<typeof inputSchema$6>;
|
|
10
|
+
declare const bashTool: ToolDefinition<Input$6>;
|
|
11
|
+
|
|
12
|
+
declare const inputSchema$5: z.ZodObject<{
|
|
13
|
+
path: z.ZodString;
|
|
14
|
+
offset: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
type Input$5 = z.infer<typeof inputSchema$5>;
|
|
18
|
+
declare const readTool: ToolDefinition<Input$5>;
|
|
19
|
+
|
|
20
|
+
declare const inputSchema$4: z.ZodObject<{
|
|
21
|
+
path: z.ZodString;
|
|
22
|
+
oldString: z.ZodString;
|
|
23
|
+
newString: z.ZodString;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
type Input$4 = z.infer<typeof inputSchema$4>;
|
|
26
|
+
declare const editTool: ToolDefinition<Input$4>;
|
|
27
|
+
|
|
28
|
+
declare const inputSchema$3: z.ZodObject<{
|
|
29
|
+
path: z.ZodString;
|
|
30
|
+
content: z.ZodString;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
type Input$3 = z.infer<typeof inputSchema$3>;
|
|
33
|
+
declare const writeTool: ToolDefinition<Input$3>;
|
|
34
|
+
|
|
35
|
+
declare const inputSchema$2: z.ZodObject<{
|
|
36
|
+
pattern: z.ZodString;
|
|
37
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
type Input$2 = z.infer<typeof inputSchema$2>;
|
|
40
|
+
declare const globTool: ToolDefinition<Input$2>;
|
|
41
|
+
|
|
42
|
+
declare const inputSchema$1: z.ZodObject<{
|
|
43
|
+
pattern: z.ZodString;
|
|
44
|
+
path: z.ZodOptional<z.ZodString>;
|
|
45
|
+
glob: z.ZodOptional<z.ZodString>;
|
|
46
|
+
}, z.core.$strip>;
|
|
47
|
+
type Input$1 = z.infer<typeof inputSchema$1>;
|
|
48
|
+
declare const grepTool: ToolDefinition<Input$1>;
|
|
49
|
+
|
|
50
|
+
declare const inputSchema: z.ZodObject<{
|
|
51
|
+
url: z.ZodString;
|
|
52
|
+
method: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
53
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
54
|
+
body: z.ZodOptional<z.ZodString>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
type Input = z.infer<typeof inputSchema>;
|
|
57
|
+
declare const webFetchTool: ToolDefinition<Input>;
|
|
58
|
+
|
|
59
|
+
/** All built-in tools as an array, ready to pass to AgentConfig.tools */
|
|
60
|
+
declare const builtinTools: ToolDefinition[];
|
|
61
|
+
|
|
62
|
+
export { bashTool, builtinTools, editTool, globTool, grepTool, readTool, webFetchTool, writeTool };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ToolDefinition } from '@claude-code-kit/agent';
|
|
3
|
+
|
|
4
|
+
declare const inputSchema$6: z.ZodObject<{
|
|
5
|
+
command: z.ZodString;
|
|
6
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
7
|
+
timeout: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
type Input$6 = z.infer<typeof inputSchema$6>;
|
|
10
|
+
declare const bashTool: ToolDefinition<Input$6>;
|
|
11
|
+
|
|
12
|
+
declare const inputSchema$5: z.ZodObject<{
|
|
13
|
+
path: z.ZodString;
|
|
14
|
+
offset: z.ZodOptional<z.ZodNumber>;
|
|
15
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
type Input$5 = z.infer<typeof inputSchema$5>;
|
|
18
|
+
declare const readTool: ToolDefinition<Input$5>;
|
|
19
|
+
|
|
20
|
+
declare const inputSchema$4: z.ZodObject<{
|
|
21
|
+
path: z.ZodString;
|
|
22
|
+
oldString: z.ZodString;
|
|
23
|
+
newString: z.ZodString;
|
|
24
|
+
}, z.core.$strip>;
|
|
25
|
+
type Input$4 = z.infer<typeof inputSchema$4>;
|
|
26
|
+
declare const editTool: ToolDefinition<Input$4>;
|
|
27
|
+
|
|
28
|
+
declare const inputSchema$3: z.ZodObject<{
|
|
29
|
+
path: z.ZodString;
|
|
30
|
+
content: z.ZodString;
|
|
31
|
+
}, z.core.$strip>;
|
|
32
|
+
type Input$3 = z.infer<typeof inputSchema$3>;
|
|
33
|
+
declare const writeTool: ToolDefinition<Input$3>;
|
|
34
|
+
|
|
35
|
+
declare const inputSchema$2: z.ZodObject<{
|
|
36
|
+
pattern: z.ZodString;
|
|
37
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
type Input$2 = z.infer<typeof inputSchema$2>;
|
|
40
|
+
declare const globTool: ToolDefinition<Input$2>;
|
|
41
|
+
|
|
42
|
+
declare const inputSchema$1: z.ZodObject<{
|
|
43
|
+
pattern: z.ZodString;
|
|
44
|
+
path: z.ZodOptional<z.ZodString>;
|
|
45
|
+
glob: z.ZodOptional<z.ZodString>;
|
|
46
|
+
}, z.core.$strip>;
|
|
47
|
+
type Input$1 = z.infer<typeof inputSchema$1>;
|
|
48
|
+
declare const grepTool: ToolDefinition<Input$1>;
|
|
49
|
+
|
|
50
|
+
declare const inputSchema: z.ZodObject<{
|
|
51
|
+
url: z.ZodString;
|
|
52
|
+
method: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
53
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
54
|
+
body: z.ZodOptional<z.ZodString>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
type Input = z.infer<typeof inputSchema>;
|
|
57
|
+
declare const webFetchTool: ToolDefinition<Input>;
|
|
58
|
+
|
|
59
|
+
/** All built-in tools as an array, ready to pass to AgentConfig.tools */
|
|
60
|
+
declare const builtinTools: ToolDefinition[];
|
|
61
|
+
|
|
62
|
+
export { bashTool, builtinTools, editTool, globTool, grepTool, readTool, webFetchTool, writeTool };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
bashTool: () => bashTool,
|
|
34
|
+
builtinTools: () => builtinTools,
|
|
35
|
+
editTool: () => editTool,
|
|
36
|
+
globTool: () => globTool,
|
|
37
|
+
grepTool: () => grepTool,
|
|
38
|
+
readTool: () => readTool,
|
|
39
|
+
webFetchTool: () => webFetchTool,
|
|
40
|
+
writeTool: () => writeTool
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(index_exports);
|
|
43
|
+
|
|
44
|
+
// src/bash.ts
|
|
45
|
+
var import_node_child_process = require("child_process");
|
|
46
|
+
var import_zod = require("zod");
|
|
47
|
+
var MAX_RESULT_SIZE = 1e5;
|
|
48
|
+
var inputSchema = import_zod.z.object({
|
|
49
|
+
command: import_zod.z.string().describe("The shell command to execute"),
|
|
50
|
+
cwd: import_zod.z.string().optional().describe("Working directory for the command"),
|
|
51
|
+
timeout: import_zod.z.number().optional().default(3e4).describe("Timeout in milliseconds")
|
|
52
|
+
});
|
|
53
|
+
async function execute(input, ctx) {
|
|
54
|
+
const cwd = input.cwd ?? ctx.workingDirectory;
|
|
55
|
+
const timeout = input.timeout;
|
|
56
|
+
return new Promise((resolve5) => {
|
|
57
|
+
const onAbort = () => {
|
|
58
|
+
child.kill("SIGTERM");
|
|
59
|
+
resolve5({ content: "Command aborted", isError: true });
|
|
60
|
+
};
|
|
61
|
+
const child = (0, import_node_child_process.exec)(input.command, { cwd, timeout, env: { ...process.env, ...ctx.env } }, (err, stdout, stderr) => {
|
|
62
|
+
ctx.abortSignal.removeEventListener("abort", onAbort);
|
|
63
|
+
const output = (stdout + (stderr ? `
|
|
64
|
+
${stderr}` : "")).slice(0, MAX_RESULT_SIZE);
|
|
65
|
+
if (err && err.killed) {
|
|
66
|
+
resolve5({ content: `Command timed out after ${timeout}ms
|
|
67
|
+
${output}`, isError: true });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (err) {
|
|
71
|
+
resolve5({ content: output || err.message, isError: true, metadata: { exitCode: err.code } });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
resolve5({ content: output || "(no output)" });
|
|
75
|
+
});
|
|
76
|
+
if (ctx.abortSignal.aborted) {
|
|
77
|
+
onAbort();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
ctx.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
var bashTool = {
|
|
84
|
+
name: "bash",
|
|
85
|
+
description: "Execute a shell command and return its stdout/stderr output",
|
|
86
|
+
inputSchema,
|
|
87
|
+
execute,
|
|
88
|
+
isReadOnly: false,
|
|
89
|
+
requiresConfirmation: true,
|
|
90
|
+
timeout: 3e4
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/read.ts
|
|
94
|
+
var fs = __toESM(require("fs/promises"));
|
|
95
|
+
var path = __toESM(require("path"));
|
|
96
|
+
var import_zod2 = require("zod");
|
|
97
|
+
var MAX_RESULT_SIZE2 = 1e5;
|
|
98
|
+
var inputSchema2 = import_zod2.z.object({
|
|
99
|
+
path: import_zod2.z.string().describe("Absolute or relative file path to read"),
|
|
100
|
+
offset: import_zod2.z.number().optional().describe("Line number to start reading from (1-based)"),
|
|
101
|
+
limit: import_zod2.z.number().optional().describe("Maximum number of lines to read")
|
|
102
|
+
});
|
|
103
|
+
async function execute2(input, ctx) {
|
|
104
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
105
|
+
const filePath = path.resolve(ctx.workingDirectory, input.path);
|
|
106
|
+
if (!filePath.startsWith(ctx.workingDirectory + path.sep) && filePath !== ctx.workingDirectory) {
|
|
107
|
+
return { content: `Error: path traversal denied \u2014 ${input.path} escapes working directory`, isError: true };
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
111
|
+
let lines = raw.split("\n");
|
|
112
|
+
if (input.offset !== void 0) {
|
|
113
|
+
lines = lines.slice(Math.max(0, input.offset - 1));
|
|
114
|
+
}
|
|
115
|
+
if (input.limit !== void 0) {
|
|
116
|
+
lines = lines.slice(0, input.limit);
|
|
117
|
+
}
|
|
118
|
+
const startLine = input.offset ?? 1;
|
|
119
|
+
const numbered = lines.map((line, i) => `${startLine + i} ${line}`);
|
|
120
|
+
const content = numbered.join("\n").slice(0, MAX_RESULT_SIZE2);
|
|
121
|
+
return { content, metadata: { totalLines: raw.split("\n").length } };
|
|
122
|
+
} catch (err) {
|
|
123
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
124
|
+
return { content: `Error reading file: ${msg}`, isError: true };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var readTool = {
|
|
128
|
+
name: "read",
|
|
129
|
+
description: "Read file contents with optional line offset and limit, returning numbered lines",
|
|
130
|
+
inputSchema: inputSchema2,
|
|
131
|
+
execute: execute2,
|
|
132
|
+
isReadOnly: true,
|
|
133
|
+
timeout: 1e4
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/edit.ts
|
|
137
|
+
var fs2 = __toESM(require("fs/promises"));
|
|
138
|
+
var path2 = __toESM(require("path"));
|
|
139
|
+
var import_zod3 = require("zod");
|
|
140
|
+
var inputSchema3 = import_zod3.z.object({
|
|
141
|
+
path: import_zod3.z.string().describe("Absolute or relative file path to edit"),
|
|
142
|
+
oldString: import_zod3.z.string().describe("Exact string to find and replace (must be unique in file)"),
|
|
143
|
+
newString: import_zod3.z.string().describe("Replacement string")
|
|
144
|
+
});
|
|
145
|
+
async function execute3(input, ctx) {
|
|
146
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
147
|
+
const filePath = path2.resolve(ctx.workingDirectory, input.path);
|
|
148
|
+
if (!filePath.startsWith(ctx.workingDirectory + path2.sep) && filePath !== ctx.workingDirectory) {
|
|
149
|
+
return { content: `Error: path traversal denied \u2014 ${input.path} escapes working directory`, isError: true };
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
153
|
+
const occurrences = content.split(input.oldString).length - 1;
|
|
154
|
+
if (occurrences === 0) {
|
|
155
|
+
return { content: "Error: oldString not found in file", isError: true };
|
|
156
|
+
}
|
|
157
|
+
if (occurrences > 1) {
|
|
158
|
+
return {
|
|
159
|
+
content: `Error: oldString found ${occurrences} times \u2014 must be unique. Provide more context to disambiguate.`,
|
|
160
|
+
isError: true
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const updated = content.replace(input.oldString, input.newString);
|
|
164
|
+
await fs2.writeFile(filePath, updated, "utf-8");
|
|
165
|
+
return { content: `Successfully edited ${filePath}` };
|
|
166
|
+
} catch (err) {
|
|
167
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
168
|
+
return { content: `Error editing file: ${msg}`, isError: true };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
var editTool = {
|
|
172
|
+
name: "edit",
|
|
173
|
+
description: "Edit a file by replacing a unique string occurrence with a new string",
|
|
174
|
+
inputSchema: inputSchema3,
|
|
175
|
+
execute: execute3,
|
|
176
|
+
isReadOnly: false,
|
|
177
|
+
requiresConfirmation: true,
|
|
178
|
+
timeout: 1e4
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// src/write.ts
|
|
182
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
183
|
+
var path3 = __toESM(require("path"));
|
|
184
|
+
var import_zod4 = require("zod");
|
|
185
|
+
var inputSchema4 = import_zod4.z.object({
|
|
186
|
+
path: import_zod4.z.string().describe("Absolute or relative file path to write"),
|
|
187
|
+
content: import_zod4.z.string().describe("Content to write to the file")
|
|
188
|
+
});
|
|
189
|
+
async function execute4(input, ctx) {
|
|
190
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
191
|
+
const filePath = path3.resolve(ctx.workingDirectory, input.path);
|
|
192
|
+
if (!filePath.startsWith(ctx.workingDirectory + path3.sep) && filePath !== ctx.workingDirectory) {
|
|
193
|
+
return { content: `Error: path traversal denied \u2014 ${input.path} escapes working directory`, isError: true };
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
await fs3.mkdir(path3.dirname(filePath), { recursive: true });
|
|
197
|
+
await fs3.writeFile(filePath, input.content, "utf-8");
|
|
198
|
+
return {
|
|
199
|
+
content: `Successfully wrote ${Buffer.byteLength(input.content)} bytes to ${filePath}`
|
|
200
|
+
};
|
|
201
|
+
} catch (err) {
|
|
202
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
203
|
+
return { content: `Error writing file: ${msg}`, isError: true };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
var writeTool = {
|
|
207
|
+
name: "write",
|
|
208
|
+
description: "Write content to a file, creating parent directories as needed",
|
|
209
|
+
inputSchema: inputSchema4,
|
|
210
|
+
execute: execute4,
|
|
211
|
+
isReadOnly: false,
|
|
212
|
+
requiresConfirmation: true,
|
|
213
|
+
timeout: 1e4
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// src/glob.ts
|
|
217
|
+
var import_fast_glob = __toESM(require("fast-glob"));
|
|
218
|
+
var import_zod5 = require("zod");
|
|
219
|
+
var MAX_RESULT_SIZE3 = 1e5;
|
|
220
|
+
var inputSchema5 = import_zod5.z.object({
|
|
221
|
+
pattern: import_zod5.z.string().describe("Glob pattern to match files (e.g. **/*.ts)"),
|
|
222
|
+
cwd: import_zod5.z.string().optional().describe("Directory to search in")
|
|
223
|
+
});
|
|
224
|
+
async function execute5(input, ctx) {
|
|
225
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
226
|
+
const cwd = input.cwd ?? ctx.workingDirectory;
|
|
227
|
+
try {
|
|
228
|
+
const files = await (0, import_fast_glob.default)(input.pattern, {
|
|
229
|
+
cwd,
|
|
230
|
+
dot: false,
|
|
231
|
+
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
232
|
+
onlyFiles: true,
|
|
233
|
+
absolute: false
|
|
234
|
+
});
|
|
235
|
+
files.sort();
|
|
236
|
+
if (files.length === 0) {
|
|
237
|
+
return { content: "No files matched the pattern" };
|
|
238
|
+
}
|
|
239
|
+
const content = files.join("\n").slice(0, MAX_RESULT_SIZE3);
|
|
240
|
+
return { content, metadata: { matchCount: files.length } };
|
|
241
|
+
} catch (err) {
|
|
242
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
243
|
+
return { content: `Error searching files: ${msg}`, isError: true };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
var globTool = {
|
|
247
|
+
name: "glob",
|
|
248
|
+
description: "Find files matching a glob pattern, excluding node_modules and .git",
|
|
249
|
+
inputSchema: inputSchema5,
|
|
250
|
+
execute: execute5,
|
|
251
|
+
isReadOnly: true,
|
|
252
|
+
timeout: 15e3
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/grep.ts
|
|
256
|
+
var fs4 = __toESM(require("fs/promises"));
|
|
257
|
+
var path4 = __toESM(require("path"));
|
|
258
|
+
var import_zod6 = require("zod");
|
|
259
|
+
var import_fast_glob2 = __toESM(require("fast-glob"));
|
|
260
|
+
var MAX_RESULT_SIZE4 = 1e5;
|
|
261
|
+
var MAX_FILES = 5e3;
|
|
262
|
+
var inputSchema6 = import_zod6.z.object({
|
|
263
|
+
pattern: import_zod6.z.string().describe("Regex pattern to search for in file contents"),
|
|
264
|
+
path: import_zod6.z.string().optional().describe("Directory or file to search in"),
|
|
265
|
+
glob: import_zod6.z.string().optional().describe("Glob filter for files (e.g. *.ts)")
|
|
266
|
+
});
|
|
267
|
+
async function execute6(input, ctx) {
|
|
268
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
269
|
+
const searchPath = input.path ? path4.resolve(ctx.workingDirectory, input.path) : ctx.workingDirectory;
|
|
270
|
+
const globPattern = input.glob ?? "**/*";
|
|
271
|
+
try {
|
|
272
|
+
const regex = new RegExp(input.pattern);
|
|
273
|
+
const stat2 = await fs4.stat(searchPath);
|
|
274
|
+
let files;
|
|
275
|
+
if (stat2.isFile()) {
|
|
276
|
+
files = [searchPath];
|
|
277
|
+
} else {
|
|
278
|
+
files = await (0, import_fast_glob2.default)(globPattern, {
|
|
279
|
+
cwd: searchPath,
|
|
280
|
+
absolute: true,
|
|
281
|
+
onlyFiles: true,
|
|
282
|
+
ignore: ["**/node_modules/**", "**/.git/**", "**/*.min.*"]
|
|
283
|
+
});
|
|
284
|
+
files = files.slice(0, MAX_FILES);
|
|
285
|
+
}
|
|
286
|
+
const matches = [];
|
|
287
|
+
let totalSize = 0;
|
|
288
|
+
for (const file of files) {
|
|
289
|
+
if (ctx.abortSignal.aborted) break;
|
|
290
|
+
try {
|
|
291
|
+
const content = await fs4.readFile(file, "utf-8");
|
|
292
|
+
const lines = content.split("\n");
|
|
293
|
+
for (let i = 0; i < lines.length; i++) {
|
|
294
|
+
if (regex.test(lines[i])) {
|
|
295
|
+
const rel = path4.relative(ctx.workingDirectory, file);
|
|
296
|
+
const line = `${rel}:${i + 1}: ${lines[i]}`;
|
|
297
|
+
totalSize += line.length;
|
|
298
|
+
if (totalSize > MAX_RESULT_SIZE4) break;
|
|
299
|
+
matches.push(line);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
if (totalSize > MAX_RESULT_SIZE4) break;
|
|
305
|
+
}
|
|
306
|
+
if (matches.length === 0) {
|
|
307
|
+
return { content: "No matches found" };
|
|
308
|
+
}
|
|
309
|
+
return { content: matches.join("\n"), metadata: { matchCount: matches.length } };
|
|
310
|
+
} catch (err) {
|
|
311
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
312
|
+
return { content: `Error searching: ${msg}`, isError: true };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
var grepTool = {
|
|
316
|
+
name: "grep",
|
|
317
|
+
description: "Search file contents using regex, returning matching lines with file:line format",
|
|
318
|
+
inputSchema: inputSchema6,
|
|
319
|
+
execute: execute6,
|
|
320
|
+
isReadOnly: true,
|
|
321
|
+
timeout: 3e4
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/web-fetch.ts
|
|
325
|
+
var import_zod7 = require("zod");
|
|
326
|
+
var MAX_RESULT_SIZE5 = 5e4;
|
|
327
|
+
function isPrivateUrl(urlStr) {
|
|
328
|
+
const url = new URL(urlStr);
|
|
329
|
+
const hostname = url.hostname;
|
|
330
|
+
const blocked = [
|
|
331
|
+
/^127\./,
|
|
332
|
+
/^10\./,
|
|
333
|
+
/^172\.(1[6-9]|2\d|3[01])\./,
|
|
334
|
+
/^192\.168\./,
|
|
335
|
+
/^169\.254\./,
|
|
336
|
+
/^0\./,
|
|
337
|
+
/^localhost$/i,
|
|
338
|
+
/^::1$/,
|
|
339
|
+
/^\[::1\]$/,
|
|
340
|
+
/^metadata\.google/,
|
|
341
|
+
/^169\.254\.169\.254$/
|
|
342
|
+
];
|
|
343
|
+
return blocked.some((re) => re.test(hostname));
|
|
344
|
+
}
|
|
345
|
+
var inputSchema7 = import_zod7.z.object({
|
|
346
|
+
url: import_zod7.z.string().url().describe("URL to fetch"),
|
|
347
|
+
method: import_zod7.z.string().optional().default("GET").describe("HTTP method"),
|
|
348
|
+
headers: import_zod7.z.record(import_zod7.z.string(), import_zod7.z.string()).optional().describe("HTTP headers"),
|
|
349
|
+
body: import_zod7.z.string().optional().describe("Request body")
|
|
350
|
+
});
|
|
351
|
+
async function execute7(input, ctx) {
|
|
352
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
353
|
+
try {
|
|
354
|
+
if (isPrivateUrl(input.url)) {
|
|
355
|
+
return { content: `Error: request to private/internal address denied \u2014 ${input.url}`, isError: true };
|
|
356
|
+
}
|
|
357
|
+
} catch {
|
|
358
|
+
return { content: `Error: invalid URL \u2014 ${input.url}`, isError: true };
|
|
359
|
+
}
|
|
360
|
+
try {
|
|
361
|
+
const res = await fetch(input.url, {
|
|
362
|
+
method: input.method,
|
|
363
|
+
headers: input.headers,
|
|
364
|
+
body: input.body,
|
|
365
|
+
signal: ctx.abortSignal
|
|
366
|
+
});
|
|
367
|
+
const text = await res.text();
|
|
368
|
+
const truncated = text.slice(0, MAX_RESULT_SIZE5);
|
|
369
|
+
const suffix = text.length > MAX_RESULT_SIZE5 ? "\n...(truncated)" : "";
|
|
370
|
+
const content = `HTTP ${res.status} ${res.statusText}
|
|
371
|
+
|
|
372
|
+
${truncated}${suffix}`;
|
|
373
|
+
return {
|
|
374
|
+
content,
|
|
375
|
+
isError: res.status >= 400,
|
|
376
|
+
metadata: { status: res.status, headers: Object.fromEntries(res.headers.entries()) }
|
|
377
|
+
};
|
|
378
|
+
} catch (err) {
|
|
379
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
380
|
+
return { content: `Fetch error: ${msg}`, isError: true };
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
var webFetchTool = {
|
|
384
|
+
name: "web_fetch",
|
|
385
|
+
description: "Make HTTP requests and return the response body",
|
|
386
|
+
inputSchema: inputSchema7,
|
|
387
|
+
execute: execute7,
|
|
388
|
+
isReadOnly: false,
|
|
389
|
+
timeout: 3e4
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// src/index.ts
|
|
393
|
+
var builtinTools = [
|
|
394
|
+
bashTool,
|
|
395
|
+
readTool,
|
|
396
|
+
editTool,
|
|
397
|
+
writeTool,
|
|
398
|
+
globTool,
|
|
399
|
+
grepTool,
|
|
400
|
+
webFetchTool
|
|
401
|
+
];
|
|
402
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
403
|
+
0 && (module.exports = {
|
|
404
|
+
bashTool,
|
|
405
|
+
builtinTools,
|
|
406
|
+
editTool,
|
|
407
|
+
globTool,
|
|
408
|
+
grepTool,
|
|
409
|
+
readTool,
|
|
410
|
+
webFetchTool,
|
|
411
|
+
writeTool
|
|
412
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
// src/bash.ts
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var MAX_RESULT_SIZE = 1e5;
|
|
5
|
+
var inputSchema = z.object({
|
|
6
|
+
command: z.string().describe("The shell command to execute"),
|
|
7
|
+
cwd: z.string().optional().describe("Working directory for the command"),
|
|
8
|
+
timeout: z.number().optional().default(3e4).describe("Timeout in milliseconds")
|
|
9
|
+
});
|
|
10
|
+
async function execute(input, ctx) {
|
|
11
|
+
const cwd = input.cwd ?? ctx.workingDirectory;
|
|
12
|
+
const timeout = input.timeout;
|
|
13
|
+
return new Promise((resolve5) => {
|
|
14
|
+
const onAbort = () => {
|
|
15
|
+
child.kill("SIGTERM");
|
|
16
|
+
resolve5({ content: "Command aborted", isError: true });
|
|
17
|
+
};
|
|
18
|
+
const child = exec(input.command, { cwd, timeout, env: { ...process.env, ...ctx.env } }, (err, stdout, stderr) => {
|
|
19
|
+
ctx.abortSignal.removeEventListener("abort", onAbort);
|
|
20
|
+
const output = (stdout + (stderr ? `
|
|
21
|
+
${stderr}` : "")).slice(0, MAX_RESULT_SIZE);
|
|
22
|
+
if (err && err.killed) {
|
|
23
|
+
resolve5({ content: `Command timed out after ${timeout}ms
|
|
24
|
+
${output}`, isError: true });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (err) {
|
|
28
|
+
resolve5({ content: output || err.message, isError: true, metadata: { exitCode: err.code } });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
resolve5({ content: output || "(no output)" });
|
|
32
|
+
});
|
|
33
|
+
if (ctx.abortSignal.aborted) {
|
|
34
|
+
onAbort();
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
ctx.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
var bashTool = {
|
|
41
|
+
name: "bash",
|
|
42
|
+
description: "Execute a shell command and return its stdout/stderr output",
|
|
43
|
+
inputSchema,
|
|
44
|
+
execute,
|
|
45
|
+
isReadOnly: false,
|
|
46
|
+
requiresConfirmation: true,
|
|
47
|
+
timeout: 3e4
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// src/read.ts
|
|
51
|
+
import * as fs from "fs/promises";
|
|
52
|
+
import * as path from "path";
|
|
53
|
+
import { z as z2 } from "zod";
|
|
54
|
+
var MAX_RESULT_SIZE2 = 1e5;
|
|
55
|
+
var inputSchema2 = z2.object({
|
|
56
|
+
path: z2.string().describe("Absolute or relative file path to read"),
|
|
57
|
+
offset: z2.number().optional().describe("Line number to start reading from (1-based)"),
|
|
58
|
+
limit: z2.number().optional().describe("Maximum number of lines to read")
|
|
59
|
+
});
|
|
60
|
+
async function execute2(input, ctx) {
|
|
61
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
62
|
+
const filePath = path.resolve(ctx.workingDirectory, input.path);
|
|
63
|
+
if (!filePath.startsWith(ctx.workingDirectory + path.sep) && filePath !== ctx.workingDirectory) {
|
|
64
|
+
return { content: `Error: path traversal denied \u2014 ${input.path} escapes working directory`, isError: true };
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
68
|
+
let lines = raw.split("\n");
|
|
69
|
+
if (input.offset !== void 0) {
|
|
70
|
+
lines = lines.slice(Math.max(0, input.offset - 1));
|
|
71
|
+
}
|
|
72
|
+
if (input.limit !== void 0) {
|
|
73
|
+
lines = lines.slice(0, input.limit);
|
|
74
|
+
}
|
|
75
|
+
const startLine = input.offset ?? 1;
|
|
76
|
+
const numbered = lines.map((line, i) => `${startLine + i} ${line}`);
|
|
77
|
+
const content = numbered.join("\n").slice(0, MAX_RESULT_SIZE2);
|
|
78
|
+
return { content, metadata: { totalLines: raw.split("\n").length } };
|
|
79
|
+
} catch (err) {
|
|
80
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
81
|
+
return { content: `Error reading file: ${msg}`, isError: true };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
var readTool = {
|
|
85
|
+
name: "read",
|
|
86
|
+
description: "Read file contents with optional line offset and limit, returning numbered lines",
|
|
87
|
+
inputSchema: inputSchema2,
|
|
88
|
+
execute: execute2,
|
|
89
|
+
isReadOnly: true,
|
|
90
|
+
timeout: 1e4
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// src/edit.ts
|
|
94
|
+
import * as fs2 from "fs/promises";
|
|
95
|
+
import * as path2 from "path";
|
|
96
|
+
import { z as z3 } from "zod";
|
|
97
|
+
var inputSchema3 = z3.object({
|
|
98
|
+
path: z3.string().describe("Absolute or relative file path to edit"),
|
|
99
|
+
oldString: z3.string().describe("Exact string to find and replace (must be unique in file)"),
|
|
100
|
+
newString: z3.string().describe("Replacement string")
|
|
101
|
+
});
|
|
102
|
+
async function execute3(input, ctx) {
|
|
103
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
104
|
+
const filePath = path2.resolve(ctx.workingDirectory, input.path);
|
|
105
|
+
if (!filePath.startsWith(ctx.workingDirectory + path2.sep) && filePath !== ctx.workingDirectory) {
|
|
106
|
+
return { content: `Error: path traversal denied \u2014 ${input.path} escapes working directory`, isError: true };
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
110
|
+
const occurrences = content.split(input.oldString).length - 1;
|
|
111
|
+
if (occurrences === 0) {
|
|
112
|
+
return { content: "Error: oldString not found in file", isError: true };
|
|
113
|
+
}
|
|
114
|
+
if (occurrences > 1) {
|
|
115
|
+
return {
|
|
116
|
+
content: `Error: oldString found ${occurrences} times \u2014 must be unique. Provide more context to disambiguate.`,
|
|
117
|
+
isError: true
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const updated = content.replace(input.oldString, input.newString);
|
|
121
|
+
await fs2.writeFile(filePath, updated, "utf-8");
|
|
122
|
+
return { content: `Successfully edited ${filePath}` };
|
|
123
|
+
} catch (err) {
|
|
124
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
125
|
+
return { content: `Error editing file: ${msg}`, isError: true };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
var editTool = {
|
|
129
|
+
name: "edit",
|
|
130
|
+
description: "Edit a file by replacing a unique string occurrence with a new string",
|
|
131
|
+
inputSchema: inputSchema3,
|
|
132
|
+
execute: execute3,
|
|
133
|
+
isReadOnly: false,
|
|
134
|
+
requiresConfirmation: true,
|
|
135
|
+
timeout: 1e4
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// src/write.ts
|
|
139
|
+
import * as fs3 from "fs/promises";
|
|
140
|
+
import * as path3 from "path";
|
|
141
|
+
import { z as z4 } from "zod";
|
|
142
|
+
var inputSchema4 = z4.object({
|
|
143
|
+
path: z4.string().describe("Absolute or relative file path to write"),
|
|
144
|
+
content: z4.string().describe("Content to write to the file")
|
|
145
|
+
});
|
|
146
|
+
async function execute4(input, ctx) {
|
|
147
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
148
|
+
const filePath = path3.resolve(ctx.workingDirectory, input.path);
|
|
149
|
+
if (!filePath.startsWith(ctx.workingDirectory + path3.sep) && filePath !== ctx.workingDirectory) {
|
|
150
|
+
return { content: `Error: path traversal denied \u2014 ${input.path} escapes working directory`, isError: true };
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
await fs3.mkdir(path3.dirname(filePath), { recursive: true });
|
|
154
|
+
await fs3.writeFile(filePath, input.content, "utf-8");
|
|
155
|
+
return {
|
|
156
|
+
content: `Successfully wrote ${Buffer.byteLength(input.content)} bytes to ${filePath}`
|
|
157
|
+
};
|
|
158
|
+
} catch (err) {
|
|
159
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
160
|
+
return { content: `Error writing file: ${msg}`, isError: true };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
var writeTool = {
|
|
164
|
+
name: "write",
|
|
165
|
+
description: "Write content to a file, creating parent directories as needed",
|
|
166
|
+
inputSchema: inputSchema4,
|
|
167
|
+
execute: execute4,
|
|
168
|
+
isReadOnly: false,
|
|
169
|
+
requiresConfirmation: true,
|
|
170
|
+
timeout: 1e4
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/glob.ts
|
|
174
|
+
import fg from "fast-glob";
|
|
175
|
+
import { z as z5 } from "zod";
|
|
176
|
+
var MAX_RESULT_SIZE3 = 1e5;
|
|
177
|
+
var inputSchema5 = z5.object({
|
|
178
|
+
pattern: z5.string().describe("Glob pattern to match files (e.g. **/*.ts)"),
|
|
179
|
+
cwd: z5.string().optional().describe("Directory to search in")
|
|
180
|
+
});
|
|
181
|
+
async function execute5(input, ctx) {
|
|
182
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
183
|
+
const cwd = input.cwd ?? ctx.workingDirectory;
|
|
184
|
+
try {
|
|
185
|
+
const files = await fg(input.pattern, {
|
|
186
|
+
cwd,
|
|
187
|
+
dot: false,
|
|
188
|
+
ignore: ["**/node_modules/**", "**/.git/**"],
|
|
189
|
+
onlyFiles: true,
|
|
190
|
+
absolute: false
|
|
191
|
+
});
|
|
192
|
+
files.sort();
|
|
193
|
+
if (files.length === 0) {
|
|
194
|
+
return { content: "No files matched the pattern" };
|
|
195
|
+
}
|
|
196
|
+
const content = files.join("\n").slice(0, MAX_RESULT_SIZE3);
|
|
197
|
+
return { content, metadata: { matchCount: files.length } };
|
|
198
|
+
} catch (err) {
|
|
199
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
200
|
+
return { content: `Error searching files: ${msg}`, isError: true };
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
var globTool = {
|
|
204
|
+
name: "glob",
|
|
205
|
+
description: "Find files matching a glob pattern, excluding node_modules and .git",
|
|
206
|
+
inputSchema: inputSchema5,
|
|
207
|
+
execute: execute5,
|
|
208
|
+
isReadOnly: true,
|
|
209
|
+
timeout: 15e3
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/grep.ts
|
|
213
|
+
import * as fs4 from "fs/promises";
|
|
214
|
+
import * as path4 from "path";
|
|
215
|
+
import { z as z6 } from "zod";
|
|
216
|
+
import fg2 from "fast-glob";
|
|
217
|
+
var MAX_RESULT_SIZE4 = 1e5;
|
|
218
|
+
var MAX_FILES = 5e3;
|
|
219
|
+
var inputSchema6 = z6.object({
|
|
220
|
+
pattern: z6.string().describe("Regex pattern to search for in file contents"),
|
|
221
|
+
path: z6.string().optional().describe("Directory or file to search in"),
|
|
222
|
+
glob: z6.string().optional().describe("Glob filter for files (e.g. *.ts)")
|
|
223
|
+
});
|
|
224
|
+
async function execute6(input, ctx) {
|
|
225
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
226
|
+
const searchPath = input.path ? path4.resolve(ctx.workingDirectory, input.path) : ctx.workingDirectory;
|
|
227
|
+
const globPattern = input.glob ?? "**/*";
|
|
228
|
+
try {
|
|
229
|
+
const regex = new RegExp(input.pattern);
|
|
230
|
+
const stat2 = await fs4.stat(searchPath);
|
|
231
|
+
let files;
|
|
232
|
+
if (stat2.isFile()) {
|
|
233
|
+
files = [searchPath];
|
|
234
|
+
} else {
|
|
235
|
+
files = await fg2(globPattern, {
|
|
236
|
+
cwd: searchPath,
|
|
237
|
+
absolute: true,
|
|
238
|
+
onlyFiles: true,
|
|
239
|
+
ignore: ["**/node_modules/**", "**/.git/**", "**/*.min.*"]
|
|
240
|
+
});
|
|
241
|
+
files = files.slice(0, MAX_FILES);
|
|
242
|
+
}
|
|
243
|
+
const matches = [];
|
|
244
|
+
let totalSize = 0;
|
|
245
|
+
for (const file of files) {
|
|
246
|
+
if (ctx.abortSignal.aborted) break;
|
|
247
|
+
try {
|
|
248
|
+
const content = await fs4.readFile(file, "utf-8");
|
|
249
|
+
const lines = content.split("\n");
|
|
250
|
+
for (let i = 0; i < lines.length; i++) {
|
|
251
|
+
if (regex.test(lines[i])) {
|
|
252
|
+
const rel = path4.relative(ctx.workingDirectory, file);
|
|
253
|
+
const line = `${rel}:${i + 1}: ${lines[i]}`;
|
|
254
|
+
totalSize += line.length;
|
|
255
|
+
if (totalSize > MAX_RESULT_SIZE4) break;
|
|
256
|
+
matches.push(line);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
if (totalSize > MAX_RESULT_SIZE4) break;
|
|
262
|
+
}
|
|
263
|
+
if (matches.length === 0) {
|
|
264
|
+
return { content: "No matches found" };
|
|
265
|
+
}
|
|
266
|
+
return { content: matches.join("\n"), metadata: { matchCount: matches.length } };
|
|
267
|
+
} catch (err) {
|
|
268
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
269
|
+
return { content: `Error searching: ${msg}`, isError: true };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
var grepTool = {
|
|
273
|
+
name: "grep",
|
|
274
|
+
description: "Search file contents using regex, returning matching lines with file:line format",
|
|
275
|
+
inputSchema: inputSchema6,
|
|
276
|
+
execute: execute6,
|
|
277
|
+
isReadOnly: true,
|
|
278
|
+
timeout: 3e4
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// src/web-fetch.ts
|
|
282
|
+
import { z as z7 } from "zod";
|
|
283
|
+
var MAX_RESULT_SIZE5 = 5e4;
|
|
284
|
+
function isPrivateUrl(urlStr) {
|
|
285
|
+
const url = new URL(urlStr);
|
|
286
|
+
const hostname = url.hostname;
|
|
287
|
+
const blocked = [
|
|
288
|
+
/^127\./,
|
|
289
|
+
/^10\./,
|
|
290
|
+
/^172\.(1[6-9]|2\d|3[01])\./,
|
|
291
|
+
/^192\.168\./,
|
|
292
|
+
/^169\.254\./,
|
|
293
|
+
/^0\./,
|
|
294
|
+
/^localhost$/i,
|
|
295
|
+
/^::1$/,
|
|
296
|
+
/^\[::1\]$/,
|
|
297
|
+
/^metadata\.google/,
|
|
298
|
+
/^169\.254\.169\.254$/
|
|
299
|
+
];
|
|
300
|
+
return blocked.some((re) => re.test(hostname));
|
|
301
|
+
}
|
|
302
|
+
var inputSchema7 = z7.object({
|
|
303
|
+
url: z7.string().url().describe("URL to fetch"),
|
|
304
|
+
method: z7.string().optional().default("GET").describe("HTTP method"),
|
|
305
|
+
headers: z7.record(z7.string(), z7.string()).optional().describe("HTTP headers"),
|
|
306
|
+
body: z7.string().optional().describe("Request body")
|
|
307
|
+
});
|
|
308
|
+
async function execute7(input, ctx) {
|
|
309
|
+
if (ctx.abortSignal.aborted) return { content: "Aborted", isError: true };
|
|
310
|
+
try {
|
|
311
|
+
if (isPrivateUrl(input.url)) {
|
|
312
|
+
return { content: `Error: request to private/internal address denied \u2014 ${input.url}`, isError: true };
|
|
313
|
+
}
|
|
314
|
+
} catch {
|
|
315
|
+
return { content: `Error: invalid URL \u2014 ${input.url}`, isError: true };
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const res = await fetch(input.url, {
|
|
319
|
+
method: input.method,
|
|
320
|
+
headers: input.headers,
|
|
321
|
+
body: input.body,
|
|
322
|
+
signal: ctx.abortSignal
|
|
323
|
+
});
|
|
324
|
+
const text = await res.text();
|
|
325
|
+
const truncated = text.slice(0, MAX_RESULT_SIZE5);
|
|
326
|
+
const suffix = text.length > MAX_RESULT_SIZE5 ? "\n...(truncated)" : "";
|
|
327
|
+
const content = `HTTP ${res.status} ${res.statusText}
|
|
328
|
+
|
|
329
|
+
${truncated}${suffix}`;
|
|
330
|
+
return {
|
|
331
|
+
content,
|
|
332
|
+
isError: res.status >= 400,
|
|
333
|
+
metadata: { status: res.status, headers: Object.fromEntries(res.headers.entries()) }
|
|
334
|
+
};
|
|
335
|
+
} catch (err) {
|
|
336
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
337
|
+
return { content: `Fetch error: ${msg}`, isError: true };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
var webFetchTool = {
|
|
341
|
+
name: "web_fetch",
|
|
342
|
+
description: "Make HTTP requests and return the response body",
|
|
343
|
+
inputSchema: inputSchema7,
|
|
344
|
+
execute: execute7,
|
|
345
|
+
isReadOnly: false,
|
|
346
|
+
timeout: 3e4
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
// src/index.ts
|
|
350
|
+
var builtinTools = [
|
|
351
|
+
bashTool,
|
|
352
|
+
readTool,
|
|
353
|
+
editTool,
|
|
354
|
+
writeTool,
|
|
355
|
+
globTool,
|
|
356
|
+
grepTool,
|
|
357
|
+
webFetchTool
|
|
358
|
+
];
|
|
359
|
+
export {
|
|
360
|
+
bashTool,
|
|
361
|
+
builtinTools,
|
|
362
|
+
editTool,
|
|
363
|
+
globTool,
|
|
364
|
+
grepTool,
|
|
365
|
+
readTool,
|
|
366
|
+
webFetchTool,
|
|
367
|
+
writeTool
|
|
368
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@claude-code-kit/tools",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Built-in tool collection for claude-code-kit agent framework",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.mjs",
|
|
17
|
+
"require": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"fast-glob": "^3.3.0",
|
|
22
|
+
"zod": ">=3.20.0",
|
|
23
|
+
"@claude-code-kit/agent": "0.2.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "*",
|
|
27
|
+
"tsup": "*",
|
|
28
|
+
"typescript": "*"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/Minnzen/claude-code-kit.git",
|
|
33
|
+
"directory": "packages/tools"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/Minnzen/claude-code-kit",
|
|
36
|
+
"bugs": "https://github.com/Minnzen/claude-code-kit/issues",
|
|
37
|
+
"keywords": [
|
|
38
|
+
"tools",
|
|
39
|
+
"agent",
|
|
40
|
+
"claude",
|
|
41
|
+
"bash",
|
|
42
|
+
"file",
|
|
43
|
+
"glob",
|
|
44
|
+
"grep"
|
|
45
|
+
],
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=18"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
55
|
+
"lint": "biome check src/"
|
|
56
|
+
}
|
|
57
|
+
}
|