@kkeve/suite 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/dist/agent/factory.d.ts +25 -0
- package/dist/agent/factory.js +99 -0
- package/dist/agent/index.d.ts +3 -0
- package/dist/agent/index.js +7 -0
- package/dist/agent/loader.d.ts +22 -0
- package/dist/agent/loader.js +117 -0
- package/dist/agent/types.d.ts +9 -0
- package/dist/agent/types.js +2 -0
- package/dist/agent-loop/complete-task.d.ts +28 -0
- package/dist/agent-loop/complete-task.js +70 -0
- package/dist/agent-loop/index.d.ts +16 -0
- package/dist/agent-loop/index.js +26 -0
- package/dist/agent-loop/loop.d.ts +42 -0
- package/dist/agent-loop/loop.js +282 -0
- package/dist/agent-loop/runtime.d.ts +40 -0
- package/dist/agent-loop/runtime.js +163 -0
- package/dist/agent-loop/types.d.ts +161 -0
- package/dist/agent-loop/types.js +105 -0
- package/dist/context/index.d.ts +8 -0
- package/dist/context/index.js +29 -0
- package/dist/context/tokenCalculation.d.ts +55 -0
- package/dist/context/tokenCalculation.js +95 -0
- package/dist/context/truncation.d.ts +67 -0
- package/dist/context/truncation.js +142 -0
- package/dist/context/types.d.ts +7 -0
- package/dist/context/types.js +27 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +45 -0
- package/dist/llm/OpenAIClient.d.ts +31 -0
- package/dist/llm/OpenAIClient.js +455 -0
- package/dist/llm/constants.d.ts +2 -0
- package/dist/llm/constants.js +6 -0
- package/dist/llm/errors.d.ts +25 -0
- package/dist/llm/errors.js +51 -0
- package/dist/llm/index.d.ts +49 -0
- package/dist/llm/index.js +158 -0
- package/dist/llm/types.d.ts +124 -0
- package/dist/llm/types.js +2 -0
- package/dist/llm/utils.d.ts +29 -0
- package/dist/llm/utils.js +284 -0
- package/dist/skill/frontmatter-parser.d.ts +50 -0
- package/dist/skill/frontmatter-parser.js +152 -0
- package/dist/skill/index.d.ts +12 -0
- package/dist/skill/index.js +17 -0
- package/dist/skill/loader.d.ts +96 -0
- package/dist/skill/loader.js +256 -0
- package/dist/tools/ask-user.d.ts +8 -0
- package/dist/tools/ask-user.js +47 -0
- package/dist/tools/bash.d.ts +47 -0
- package/dist/tools/bash.js +213 -0
- package/dist/tools/edit-file.d.ts +9 -0
- package/dist/tools/edit-file.js +76 -0
- package/dist/tools/glob.d.ts +8 -0
- package/dist/tools/glob.js +101 -0
- package/dist/tools/grep.d.ts +10 -0
- package/dist/tools/grep.js +76 -0
- package/dist/tools/index.d.ts +24 -0
- package/dist/tools/index.js +47 -0
- package/dist/tools/read-files.d.ts +8 -0
- package/dist/tools/read-files.js +81 -0
- package/dist/tools/write-file.d.ts +8 -0
- package/dist/tools/write-file.js +63 -0
- package/package.json +59 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bash Tool — shell command execution with scope-based permission control
|
|
4
|
+
*
|
|
5
|
+
* Scope patterns (aligned with keve-agent SKILL.md allowed-tools):
|
|
6
|
+
* Bash(git:*) → only git commands allowed
|
|
7
|
+
* Bash(npx:*) → only npx commands allowed
|
|
8
|
+
* Bash(npm:*) → only npm commands allowed
|
|
9
|
+
* Bash(node:*) → only node commands allowed
|
|
10
|
+
* Bash(shell:*) → any command allowed
|
|
11
|
+
*
|
|
12
|
+
* When LLM calls bash tool, the 'scope' parameter is validated against
|
|
13
|
+
* the skill's allowed-tools list by the ToolCaller.
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.bashTool = exports.BashInputSchema = void 0;
|
|
17
|
+
exports.isCommandInScope = isCommandInScope;
|
|
18
|
+
exports.parseBashScopes = parseBashScopes;
|
|
19
|
+
exports.checkReadOnlyPathWrite = checkReadOnlyPathWrite;
|
|
20
|
+
const v4_1 = require("zod/v4");
|
|
21
|
+
const node_child_process_1 = require("node:child_process");
|
|
22
|
+
// ===== Schema =====
|
|
23
|
+
exports.BashInputSchema = v4_1.z.object({
|
|
24
|
+
/** The shell command to execute */
|
|
25
|
+
command: v4_1.z.string().describe('Shell command to execute'),
|
|
26
|
+
/** Scope constraint (e.g., 'git', 'npx', 'npm', 'node', 'shell') */
|
|
27
|
+
scope: v4_1.z.string().default('shell').describe('Command scope: git/npx/npm/node/shell'),
|
|
28
|
+
/** Working directory */
|
|
29
|
+
cwd: v4_1.z.string().optional().describe('Working directory for command execution'),
|
|
30
|
+
/** Timeout in milliseconds */
|
|
31
|
+
timeout: v4_1.z.number().default(30_000).describe('Execution timeout in ms'),
|
|
32
|
+
});
|
|
33
|
+
// ===== Scope Validation =====
|
|
34
|
+
/**
|
|
35
|
+
* Validate that a command matches the allowed scope.
|
|
36
|
+
* Extracts the first word (the binary name) and checks against scope.
|
|
37
|
+
*
|
|
38
|
+
* @param command - The shell command to validate
|
|
39
|
+
* @param allowedScopes - Array of allowed scopes (e.g., ['git', 'npx', 'shell'])
|
|
40
|
+
* @returns true if command is within allowed scopes
|
|
41
|
+
*/
|
|
42
|
+
function isCommandInScope(command, allowedScopes) {
|
|
43
|
+
// 'shell' scope allows everything
|
|
44
|
+
if (allowedScopes.includes('shell'))
|
|
45
|
+
return true;
|
|
46
|
+
// Extract the first word (binary name)
|
|
47
|
+
const firstWord = command.trim().split(/\s+/)[0];
|
|
48
|
+
// Handle path-like commands: extract basename
|
|
49
|
+
const binaryName = firstWord.split('/').pop() ?? firstWord;
|
|
50
|
+
// Check if the binary matches any allowed scope
|
|
51
|
+
return allowedScopes.some(scope => binaryName === scope);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse Bash(scope:*) patterns from allowed-tools list.
|
|
55
|
+
* Input: ['Bash(git:*)', 'Bash(npx:*)', 'codegraph_impact']
|
|
56
|
+
* Output: ['git', 'npx']
|
|
57
|
+
*/
|
|
58
|
+
function parseBashScopes(allowedTools) {
|
|
59
|
+
const scopes = [];
|
|
60
|
+
for (const tool of allowedTools) {
|
|
61
|
+
const match = tool.match(/^Bash\((\w+):\*\)$/);
|
|
62
|
+
if (match) {
|
|
63
|
+
scopes.push(match[1]);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return scopes;
|
|
67
|
+
}
|
|
68
|
+
// ===== Read-Only Path Protection =====
|
|
69
|
+
/**
|
|
70
|
+
* Write patterns that indicate a command is trying to modify files.
|
|
71
|
+
* These are checked against readOnlyPaths to prevent writes to protected directories.
|
|
72
|
+
*/
|
|
73
|
+
const WRITE_PATTERNS = [
|
|
74
|
+
// sed -i (in-place edit)
|
|
75
|
+
{ pattern: /\bsed\s+(-[^-]*i|--in-place)\b/, description: 'sed -i (in-place edit)' },
|
|
76
|
+
// Redirect overwrite: > file (but not >>, > /dev/null, > &)
|
|
77
|
+
{ pattern: /[^>]>[^>]/, description: '> redirect (file overwrite)' },
|
|
78
|
+
// Redirect append: >> file (allowed in general, but block if target is readOnly)
|
|
79
|
+
// Actually >> is also a write, let's block it too for readOnly paths
|
|
80
|
+
{ pattern: />>/, description: '>> redirect (file append)' },
|
|
81
|
+
// tee command
|
|
82
|
+
{ pattern: /\btee\b/, description: 'tee (write to file)' },
|
|
83
|
+
// mv / cp with write target
|
|
84
|
+
{ pattern: /\b(mv|cp)\s+/, description: 'mv/cp (move/copy file)' },
|
|
85
|
+
// rm
|
|
86
|
+
{ pattern: /\brm\s+/, description: 'rm (delete file)' },
|
|
87
|
+
// chmod / chown
|
|
88
|
+
{ pattern: /\b(chmod|chown)\s+/, description: 'chmod/chown (change permissions)' },
|
|
89
|
+
// patch
|
|
90
|
+
{ pattern: /\bpatch\s+/, description: 'patch (apply diff)' },
|
|
91
|
+
// npm install / pnpm install / yarn (adds node_modules)
|
|
92
|
+
{ pattern: /\b(npm|pnpm|yarn)\s+(install|i|add)\b/, description: 'npm/pnpm/yarn install (add dependencies)' },
|
|
93
|
+
// npx playwright install
|
|
94
|
+
{ pattern: /\bnpx\s+playwright\s+install\b/, description: 'npx playwright install (installs browsers)' },
|
|
95
|
+
// pip install
|
|
96
|
+
{ pattern: /\bpip\d*\s+install\b/, description: 'pip install (add Python packages)' },
|
|
97
|
+
// echo > redirect
|
|
98
|
+
{ pattern: /\becho\s+.*>\s*/, description: 'echo > redirect (write to file)' },
|
|
99
|
+
// cat > redirect
|
|
100
|
+
{ pattern: /\bcat\b.*[^>]>[^>]/, description: 'cat > redirect (write to file)' },
|
|
101
|
+
// dd
|
|
102
|
+
{ pattern: /\bdd\s+/, description: 'dd (disk write)' },
|
|
103
|
+
// install command (copy files)
|
|
104
|
+
{ pattern: /\binstall\s+/, description: 'install (copy files)' },
|
|
105
|
+
];
|
|
106
|
+
/**
|
|
107
|
+
* Check if a command attempts to write to a read-only path.
|
|
108
|
+
* Uses static analysis of the command string.
|
|
109
|
+
*
|
|
110
|
+
* @param command - The shell command to check
|
|
111
|
+
* @param readOnlyPaths - Array of directory paths that are read-only
|
|
112
|
+
* @returns Error message if write is detected, or null if command is safe
|
|
113
|
+
*/
|
|
114
|
+
function checkReadOnlyPathWrite(command, readOnlyPaths) {
|
|
115
|
+
if (!readOnlyPaths.length)
|
|
116
|
+
return null;
|
|
117
|
+
// Step 1: Check if command contains any write pattern
|
|
118
|
+
const matchedPattern = WRITE_PATTERNS.find(p => p.pattern.test(command));
|
|
119
|
+
if (!matchedPattern)
|
|
120
|
+
return null;
|
|
121
|
+
// Step 2: Check if the write target falls under a readOnlyPath
|
|
122
|
+
// Normalize readOnly paths (resolve home, remove trailing slash)
|
|
123
|
+
const normalizedROPaths = readOnlyPaths.map(p => {
|
|
124
|
+
const expanded = p.replace(/^~/, process.env.HOME || '/tmp');
|
|
125
|
+
return expanded.replace(/\/+$/, '');
|
|
126
|
+
});
|
|
127
|
+
// Extract potential target paths from the command
|
|
128
|
+
// Simple heuristic: any absolute path or relative path in the command
|
|
129
|
+
// that starts with or contains a readOnlyPath prefix
|
|
130
|
+
const commandHasReadOnlyTarget = normalizedROPaths.some(roPath => {
|
|
131
|
+
// Direct path reference in command
|
|
132
|
+
if (command.includes(roPath + '/') || command.includes(roPath + ' ') || command.endsWith(roPath)) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
// If cwd might be a readOnlyPath and command uses relative paths
|
|
136
|
+
// (covered by the general pattern match above — if write pattern
|
|
137
|
+
// matches AND readOnlyPath is in the command, we block it)
|
|
138
|
+
return false;
|
|
139
|
+
});
|
|
140
|
+
if (commandHasReadOnlyTarget) {
|
|
141
|
+
return `[BLOCKED] Write operation blocked: "${matchedPattern.description}" targets read-only path. ` +
|
|
142
|
+
`Protected paths: ${normalizedROPaths.join(', ')}. ` +
|
|
143
|
+
`Write your output to workspaceDir instead.`;
|
|
144
|
+
}
|
|
145
|
+
// Even if no explicit path target, some commands are inherently dangerous
|
|
146
|
+
// when executed inside a readOnly directory (e.g., `cd /readOnly && sed -i file`)
|
|
147
|
+
// Check if cwd might be a readOnlyPath
|
|
148
|
+
const cwdInCommand = command.match(/cd\s+(['"]?)(.+?)\1\s+&&/);
|
|
149
|
+
if (cwdInCommand) {
|
|
150
|
+
const cwdPath = cwdInCommand[2].replace(/^~/, process.env.HOME || '/tmp');
|
|
151
|
+
if (normalizedROPaths.some(ro => cwdPath.startsWith(ro))) {
|
|
152
|
+
return `[BLOCKED] Write operation blocked: "${matchedPattern.description}" executed in read-only directory "${cwdPath}". ` +
|
|
153
|
+
`Protected paths: ${normalizedROPaths.join(', ')}. ` +
|
|
154
|
+
`Write your output to workspaceDir instead.`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
// ===== Tool Definition =====
|
|
160
|
+
exports.bashTool = {
|
|
161
|
+
name: 'bash',
|
|
162
|
+
description: `Execute a shell command with scope-based permission control.
|
|
163
|
+
|
|
164
|
+
Scope patterns:
|
|
165
|
+
- Bash(git:*): only git commands
|
|
166
|
+
- Bash(npx:*): only npx commands
|
|
167
|
+
- Bash(npm:*): only npm commands
|
|
168
|
+
- Bash(node:*): only node commands
|
|
169
|
+
- Bash(shell:*): any command
|
|
170
|
+
|
|
171
|
+
The scope parameter is validated against the skill's allowed-tools list.
|
|
172
|
+
|
|
173
|
+
Read-only path protection:
|
|
174
|
+
- If readOnlyPaths is set in context, commands that write to those paths will be blocked.
|
|
175
|
+
- This prevents accidental modification of source code repositories.`,
|
|
176
|
+
inputSchema: exports.BashInputSchema,
|
|
177
|
+
customJsonSchema: {
|
|
178
|
+
type: 'object',
|
|
179
|
+
properties: {
|
|
180
|
+
command: { type: 'string', description: 'Shell command to execute' },
|
|
181
|
+
scope: { type: 'string', description: 'Command scope: git/npx/npm/node/shell', default: 'shell' },
|
|
182
|
+
cwd: { type: 'string', description: 'Working directory' },
|
|
183
|
+
timeout: { type: 'number', description: 'Timeout in ms', default: 30000 },
|
|
184
|
+
},
|
|
185
|
+
required: ['command'],
|
|
186
|
+
},
|
|
187
|
+
async execute(args, context) {
|
|
188
|
+
const { command, scope = 'shell', cwd, timeout = 30_000 } = args;
|
|
189
|
+
// 🆕 Read-only path protection
|
|
190
|
+
if (context?.readOnlyPaths?.length) {
|
|
191
|
+
const blockReason = checkReadOnlyPathWrite(command, context.readOnlyPaths);
|
|
192
|
+
if (blockReason) {
|
|
193
|
+
return { ok: false, stdout: '', stderr: blockReason, exitCode: 126 };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const stdout = (0, node_child_process_1.execSync)(command, {
|
|
198
|
+
cwd,
|
|
199
|
+
timeout,
|
|
200
|
+
encoding: 'utf-8',
|
|
201
|
+
maxBuffer: 1024 * 1024, // 1MB
|
|
202
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
203
|
+
});
|
|
204
|
+
return { ok: true, stdout: stdout.trim(), stderr: '', exitCode: 0 };
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
const stdout = err.stdout?.trim() ?? '';
|
|
208
|
+
const stderr = err.stderr?.trim() ?? '';
|
|
209
|
+
const exitCode = err.status ?? 1;
|
|
210
|
+
return { ok: false, stdout, stderr, exitCode };
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type { ToolDefinition } from '../agent-loop/types';
|
|
3
|
+
export declare const EditFileInputSchema: z.ZodObject<{
|
|
4
|
+
path: z.ZodString;
|
|
5
|
+
oldString: z.ZodString;
|
|
6
|
+
newString: z.ZodString;
|
|
7
|
+
replaceAll: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export declare const editFileTool: ToolDefinition;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.editFileTool = exports.EditFileInputSchema = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* edit_file tool — precise string replacement editing
|
|
39
|
+
*
|
|
40
|
+
* Reference: gemini-cli EditTool (replace oldString → newString)
|
|
41
|
+
*/
|
|
42
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
44
|
+
const v4_1 = require("zod/v4");
|
|
45
|
+
exports.EditFileInputSchema = v4_1.z.object({
|
|
46
|
+
path: v4_1.z.string().describe('File path'),
|
|
47
|
+
oldString: v4_1.z.string().describe('Original text to replace (must match exactly)'),
|
|
48
|
+
newString: v4_1.z.string().describe('New text to replace with'),
|
|
49
|
+
replaceAll: v4_1.z.boolean().default(false).describe('Replace all occurrences (default: first only)'),
|
|
50
|
+
});
|
|
51
|
+
exports.editFileTool = {
|
|
52
|
+
name: 'edit_file',
|
|
53
|
+
description: 'Edit file by precise string replacement. oldString must match exactly, replaced with newString.',
|
|
54
|
+
inputSchema: exports.EditFileInputSchema,
|
|
55
|
+
execute: async (args, context) => {
|
|
56
|
+
const cwd = context?.cwd ?? process.cwd();
|
|
57
|
+
const filePath = path.isAbsolute(args.path) ? args.path : path.resolve(cwd, args.path);
|
|
58
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
59
|
+
// Check if oldString exists
|
|
60
|
+
if (!content.includes(args.oldString)) {
|
|
61
|
+
return { ok: false, error: `oldString not found in ${filePath}` };
|
|
62
|
+
}
|
|
63
|
+
// Check uniqueness (if not replaceAll)
|
|
64
|
+
if (!args.replaceAll) {
|
|
65
|
+
const count = content.split(args.oldString).length - 1;
|
|
66
|
+
if (count > 1) {
|
|
67
|
+
return { ok: false, error: `oldString found ${count} times in ${filePath}, but replaceAll=false. Provide more context to make it unique.` };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const newContent = args.replaceAll
|
|
71
|
+
? content.split(args.oldString).join(args.newString)
|
|
72
|
+
: content.replace(args.oldString, args.newString);
|
|
73
|
+
await fs.writeFile(filePath, newContent, 'utf-8');
|
|
74
|
+
return { ok: true, path: filePath, replaced: args.oldString.length };
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type { ToolDefinition } from '../agent-loop/types';
|
|
3
|
+
export declare const GlobInputSchema: z.ZodObject<{
|
|
4
|
+
pattern: z.ZodString;
|
|
5
|
+
cwd: z.ZodOptional<z.ZodString>;
|
|
6
|
+
maxResults: z.ZodDefault<z.ZodNumber>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export declare const globTool: ToolDefinition;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.globTool = exports.GlobInputSchema = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* glob tool — file search with glob pattern matching
|
|
39
|
+
*
|
|
40
|
+
* Reference: gemini-cli GlobTool
|
|
41
|
+
*/
|
|
42
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
44
|
+
const v4_1 = require("zod/v4");
|
|
45
|
+
exports.GlobInputSchema = v4_1.z.object({
|
|
46
|
+
pattern: v4_1.z.string().describe('Glob pattern (e.g. **/*.vue, src/**/*.ts)'),
|
|
47
|
+
cwd: v4_1.z.string().optional().describe('Root directory to search in'),
|
|
48
|
+
maxResults: v4_1.z.number().default(100).describe('Maximum number of results'),
|
|
49
|
+
});
|
|
50
|
+
exports.globTool = {
|
|
51
|
+
name: 'glob',
|
|
52
|
+
description: 'Search files using glob patterns. Supports **/*.vue, src/**/*.ts etc. Returns matching file paths.',
|
|
53
|
+
inputSchema: exports.GlobInputSchema,
|
|
54
|
+
execute: async (args, context) => {
|
|
55
|
+
const searchDir = args.cwd ?? context?.cwd ?? process.cwd();
|
|
56
|
+
// Simple glob using Node.js fs
|
|
57
|
+
// Production: replace with fast-glob / glob package
|
|
58
|
+
const results = [];
|
|
59
|
+
const pattern = args.pattern;
|
|
60
|
+
async function walk(dir, depth) {
|
|
61
|
+
if (depth > 10 || results.length >= args.maxResults)
|
|
62
|
+
return;
|
|
63
|
+
let entries;
|
|
64
|
+
try {
|
|
65
|
+
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
if (results.length >= args.maxResults)
|
|
72
|
+
break;
|
|
73
|
+
const fullPath = path.join(dir, entry.name);
|
|
74
|
+
// Skip node_modules, .git, dist
|
|
75
|
+
if (entry.isDirectory() && ['node_modules', '.git', 'dist', '.next', '.nuxt'].includes(entry.name))
|
|
76
|
+
continue;
|
|
77
|
+
if (entry.isDirectory()) {
|
|
78
|
+
await walk(fullPath, depth + 1);
|
|
79
|
+
}
|
|
80
|
+
else if (entry.isFile()) {
|
|
81
|
+
const relativePath = path.relative(searchDir, fullPath);
|
|
82
|
+
if (matchGlob(relativePath, pattern)) {
|
|
83
|
+
results.push(relativePath);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
await walk(searchDir, 0);
|
|
89
|
+
return results.sort();
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
/** Simple glob matching — supports **, *, ? */
|
|
93
|
+
function matchGlob(filePath, pattern) {
|
|
94
|
+
const regex = pattern
|
|
95
|
+
.replace(/\./g, '\\.')
|
|
96
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
97
|
+
.replace(/\*/g, '[^/]*')
|
|
98
|
+
.replace(/\?/g, '[^/]')
|
|
99
|
+
.replace(/{{GLOBSTAR}}/g, '.*');
|
|
100
|
+
return new RegExp(`^${regex}$`).test(filePath);
|
|
101
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type { ToolDefinition } from '../agent-loop/types';
|
|
3
|
+
export declare const GrepInputSchema: z.ZodObject<{
|
|
4
|
+
pattern: z.ZodString;
|
|
5
|
+
path: z.ZodOptional<z.ZodString>;
|
|
6
|
+
include: z.ZodOptional<z.ZodString>;
|
|
7
|
+
maxResults: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
contextLines: z.ZodDefault<z.ZodNumber>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export declare const grepTool: ToolDefinition;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.grepTool = exports.GrepInputSchema = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* grep tool — content search with regex
|
|
39
|
+
*
|
|
40
|
+
* Reference: gemini-cli GrepTool / ripgrep
|
|
41
|
+
*/
|
|
42
|
+
const node_child_process_1 = require("node:child_process");
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
44
|
+
const v4_1 = require("zod/v4");
|
|
45
|
+
exports.GrepInputSchema = v4_1.z.object({
|
|
46
|
+
pattern: v4_1.z.string().describe('Regex pattern to search for'),
|
|
47
|
+
path: v4_1.z.string().optional().describe('Directory to search in'),
|
|
48
|
+
include: v4_1.z.string().optional().describe('File filter pattern (e.g. *.vue, *.ts)'),
|
|
49
|
+
maxResults: v4_1.z.number().default(50).describe('Maximum number of matches to return'),
|
|
50
|
+
contextLines: v4_1.z.number().default(2).describe('Number of context lines around matches'),
|
|
51
|
+
});
|
|
52
|
+
exports.grepTool = {
|
|
53
|
+
name: 'grep',
|
|
54
|
+
description: 'Search file contents with regex. Returns matching file paths, line numbers, and content.',
|
|
55
|
+
inputSchema: exports.GrepInputSchema,
|
|
56
|
+
execute: async (args, context) => {
|
|
57
|
+
const searchDir = args.path ?? context?.cwd ?? process.cwd();
|
|
58
|
+
const absPath = path.isAbsolute(searchDir) ? searchDir : path.resolve(context?.cwd ?? process.cwd(), searchDir);
|
|
59
|
+
const includeFlag = args.include ? `--include="${args.include}"` : '';
|
|
60
|
+
const contextFlag = args.contextLines > 0 ? `-C ${args.contextLines}` : '';
|
|
61
|
+
const maxCount = args.maxResults > 0 ? `-m ${args.maxResults}` : '';
|
|
62
|
+
try {
|
|
63
|
+
const result = (0, node_child_process_1.execSync)(`grep -rn ${contextFlag} ${maxCount} ${includeFlag} -E "${args.pattern.replace(/"/g, '\\"')}" "${absPath}" 2>/dev/null || true`, { encoding: 'utf-8', timeout: 15_000, maxBuffer: 1024 * 1024 }).trim();
|
|
64
|
+
if (!result)
|
|
65
|
+
return { matches: [], count: 0 };
|
|
66
|
+
const lines = result.split('\n').filter(Boolean);
|
|
67
|
+
return {
|
|
68
|
+
matches: lines.slice(0, args.maxResults),
|
|
69
|
+
count: lines.length,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return { matches: [], count: 0 };
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry factory — general tools
|
|
3
|
+
*
|
|
4
|
+
* General tools (gemini-cli aligned):
|
|
5
|
+
* - bash, read_files, write_file, edit_file, glob, grep, ask_user
|
|
6
|
+
*
|
|
7
|
+
* Business tools (keve, record_step_result) have been moved to agent package.
|
|
8
|
+
* MCP browser tools (pw_*) have been removed — use keve-suite or agent package instead.
|
|
9
|
+
*/
|
|
10
|
+
import { ToolRegistry } from '../agent-loop/types';
|
|
11
|
+
import type { RuntimeEnvironment } from '../agent-loop/runtime';
|
|
12
|
+
import { bashTool } from './bash';
|
|
13
|
+
import { readFilesTool } from './read-files';
|
|
14
|
+
import { writeFileTool } from './write-file';
|
|
15
|
+
import { editFileTool } from './edit-file';
|
|
16
|
+
import { globTool } from './glob';
|
|
17
|
+
import { grepTool } from './grep';
|
|
18
|
+
import { askUserTool } from './ask-user';
|
|
19
|
+
export { bashTool, readFilesTool, writeFileTool, editFileTool, globTool, grepTool, askUserTool };
|
|
20
|
+
/**
|
|
21
|
+
* Create default tool registry — general tools only
|
|
22
|
+
*/
|
|
23
|
+
export declare function createDefaultToolRegistry(runtime: RuntimeEnvironment): ToolRegistry;
|
|
24
|
+
export declare function getBuiltinToolNames(): string[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tool registry factory — general tools
|
|
4
|
+
*
|
|
5
|
+
* General tools (gemini-cli aligned):
|
|
6
|
+
* - bash, read_files, write_file, edit_file, glob, grep, ask_user
|
|
7
|
+
*
|
|
8
|
+
* Business tools (keve, record_step_result) have been moved to agent package.
|
|
9
|
+
* MCP browser tools (pw_*) have been removed — use keve-suite or agent package instead.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.askUserTool = exports.grepTool = exports.globTool = exports.editFileTool = exports.writeFileTool = exports.readFilesTool = exports.bashTool = void 0;
|
|
13
|
+
exports.createDefaultToolRegistry = createDefaultToolRegistry;
|
|
14
|
+
exports.getBuiltinToolNames = getBuiltinToolNames;
|
|
15
|
+
const types_1 = require("../agent-loop/types");
|
|
16
|
+
const bash_1 = require("./bash");
|
|
17
|
+
Object.defineProperty(exports, "bashTool", { enumerable: true, get: function () { return bash_1.bashTool; } });
|
|
18
|
+
const read_files_1 = require("./read-files");
|
|
19
|
+
Object.defineProperty(exports, "readFilesTool", { enumerable: true, get: function () { return read_files_1.readFilesTool; } });
|
|
20
|
+
const write_file_1 = require("./write-file");
|
|
21
|
+
Object.defineProperty(exports, "writeFileTool", { enumerable: true, get: function () { return write_file_1.writeFileTool; } });
|
|
22
|
+
const edit_file_1 = require("./edit-file");
|
|
23
|
+
Object.defineProperty(exports, "editFileTool", { enumerable: true, get: function () { return edit_file_1.editFileTool; } });
|
|
24
|
+
const glob_1 = require("./glob");
|
|
25
|
+
Object.defineProperty(exports, "globTool", { enumerable: true, get: function () { return glob_1.globTool; } });
|
|
26
|
+
const grep_1 = require("./grep");
|
|
27
|
+
Object.defineProperty(exports, "grepTool", { enumerable: true, get: function () { return grep_1.grepTool; } });
|
|
28
|
+
const ask_user_1 = require("./ask-user");
|
|
29
|
+
Object.defineProperty(exports, "askUserTool", { enumerable: true, get: function () { return ask_user_1.askUserTool; } });
|
|
30
|
+
/**
|
|
31
|
+
* Create default tool registry — general tools only
|
|
32
|
+
*/
|
|
33
|
+
function createDefaultToolRegistry(runtime) {
|
|
34
|
+
globalThis.__runtime = runtime;
|
|
35
|
+
const registry = new types_1.ToolRegistry();
|
|
36
|
+
registry.register(bash_1.bashTool);
|
|
37
|
+
registry.register(read_files_1.readFilesTool);
|
|
38
|
+
registry.register(write_file_1.writeFileTool);
|
|
39
|
+
registry.register(edit_file_1.editFileTool);
|
|
40
|
+
registry.register(glob_1.globTool);
|
|
41
|
+
registry.register(grep_1.grepTool);
|
|
42
|
+
registry.register(ask_user_1.askUserTool);
|
|
43
|
+
return registry;
|
|
44
|
+
}
|
|
45
|
+
function getBuiltinToolNames() {
|
|
46
|
+
return ['bash', 'read_files', 'write_file', 'edit_file', 'glob', 'grep', 'ask_user'];
|
|
47
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* read_files Tool — multi-file reader (reference: gemini-cli ReadManyFilesTool)
|
|
3
|
+
*
|
|
4
|
+
* Reads multiple files at once, with content truncation.
|
|
5
|
+
* Used by skills to read code, configs, wiki content, etc.
|
|
6
|
+
*/
|
|
7
|
+
import type { ToolDefinition } from '../agent-loop/types';
|
|
8
|
+
export declare const readFilesTool: ToolDefinition;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* read_files Tool — multi-file reader (reference: gemini-cli ReadManyFilesTool)
|
|
4
|
+
*
|
|
5
|
+
* Reads multiple files at once, with content truncation.
|
|
6
|
+
* Used by skills to read code, configs, wiki content, etc.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.readFilesTool = void 0;
|
|
43
|
+
const v4_1 = require("zod/v4");
|
|
44
|
+
const fs = __importStar(require("node:fs/promises"));
|
|
45
|
+
const path = __importStar(require("node:path"));
|
|
46
|
+
const ReadFilesSchema = v4_1.z.object({
|
|
47
|
+
/** File paths to read (absolute or relative to cwd) */
|
|
48
|
+
paths: v4_1.z.array(v4_1.z.string()).describe('Array of file paths to read'),
|
|
49
|
+
/** Base directory for resolving relative paths */
|
|
50
|
+
cwd: v4_1.z.string().optional().describe('Base directory for relative paths'),
|
|
51
|
+
/** Max bytes per file (prevents OOM on large files) */
|
|
52
|
+
maxBytes: v4_1.z.number().default(50_000).describe('Max bytes per file (truncated if larger)'),
|
|
53
|
+
});
|
|
54
|
+
exports.readFilesTool = {
|
|
55
|
+
name: 'read_files',
|
|
56
|
+
description: `Read multiple files at once. Returns file contents with path headers.
|
|
57
|
+
Files larger than maxBytes are truncated. Non-existent files produce an error message.`,
|
|
58
|
+
inputSchema: ReadFilesSchema,
|
|
59
|
+
async execute(args) {
|
|
60
|
+
const { paths, cwd, maxBytes = 50_000 } = args;
|
|
61
|
+
const baseDir = cwd ?? process.cwd();
|
|
62
|
+
const results = [];
|
|
63
|
+
for (const filePath of paths) {
|
|
64
|
+
const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(baseDir, filePath);
|
|
65
|
+
const header = `\n=== ${filePath} ===`;
|
|
66
|
+
try {
|
|
67
|
+
const content = await fs.readFile(absPath, 'utf-8');
|
|
68
|
+
if (content.length > maxBytes) {
|
|
69
|
+
results.push(`${header}\n${content.slice(0, maxBytes)}\n... [truncated at ${maxBytes} chars]`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
results.push(`${header}\n${content}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
results.push(`${header}\n[Error: ${err.message}]`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return results.join('\n');
|
|
80
|
+
},
|
|
81
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type { ToolDefinition } from '../agent-loop/types';
|
|
3
|
+
export declare const WriteFileInputSchema: z.ZodObject<{
|
|
4
|
+
path: z.ZodString;
|
|
5
|
+
content: z.ZodString;
|
|
6
|
+
createDirs: z.ZodDefault<z.ZodBoolean>;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
export declare const writeFileTool: ToolDefinition;
|