@probelabs/probe 0.6.0-rc199 → 0.6.0-rc200
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/bin/binaries/probe-v0.6.0-rc200-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc200-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc200-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc200-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc200-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/index.js +1578 -1529
- package/build/delegate.js +5 -1
- package/build/tools/common.js +11 -3
- package/build/tools/vercel.js +17 -4
- package/cjs/agent/ProbeAgent.cjs +2236 -2187
- package/cjs/index.cjs +424 -415
- package/package.json +1 -1
- package/src/delegate.js +5 -1
- package/src/tools/common.js +11 -3
- package/src/tools/vercel.js +17 -4
- package/bin/binaries/probe-v0.6.0-rc199-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc199-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc199-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc199-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc199-x86_64-unknown-linux-musl.tar.gz +0 -0
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -30505,7 +30505,9 @@ async function delegate({
|
|
|
30505
30505
|
debug,
|
|
30506
30506
|
tracer,
|
|
30507
30507
|
path: path9,
|
|
30508
|
-
//
|
|
30508
|
+
// Workspace root (from delegateTool)
|
|
30509
|
+
cwd: path9,
|
|
30510
|
+
// Explicitly set cwd to workspace root to prevent path doubling
|
|
30509
30511
|
provider,
|
|
30510
30512
|
// Inherit from parent
|
|
30511
30513
|
model,
|
|
@@ -34821,2320 +34823,2367 @@ var init_zod = __esm({
|
|
|
34821
34823
|
}
|
|
34822
34824
|
});
|
|
34823
34825
|
|
|
34824
|
-
// src/tools/
|
|
34825
|
-
function
|
|
34826
|
-
|
|
34827
|
-
|
|
34828
|
-
|
|
34829
|
-
|
|
34830
|
-
const escaped = allTools.map((t4) => t4.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
34831
|
-
return new RegExp(`<(${escaped.join("|")})>`);
|
|
34832
|
-
}
|
|
34833
|
-
function getValidParamsForTool(toolName) {
|
|
34834
|
-
const schemaMap = {
|
|
34835
|
-
search: searchSchema,
|
|
34836
|
-
query: querySchema,
|
|
34837
|
-
extract: extractSchema,
|
|
34838
|
-
delegate: delegateSchema,
|
|
34839
|
-
bash: bashSchema,
|
|
34840
|
-
attempt_completion: attemptCompletionSchema
|
|
34841
|
-
};
|
|
34842
|
-
const schema = schemaMap[toolName];
|
|
34843
|
-
if (!schema) {
|
|
34844
|
-
return ["path", "directory", "pattern", "recursive", "includeHidden", "task", "files", "autoCommits", "result"];
|
|
34845
|
-
}
|
|
34846
|
-
if (toolName === "attempt_completion") {
|
|
34847
|
-
return ["result"];
|
|
34848
|
-
}
|
|
34849
|
-
if (schema && schema._def && schema._def.shape) {
|
|
34850
|
-
return Object.keys(schema._def.shape());
|
|
34851
|
-
}
|
|
34852
|
-
return [];
|
|
34853
|
-
}
|
|
34854
|
-
function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
34855
|
-
for (const toolName of validTools) {
|
|
34856
|
-
const openTag = `<${toolName}>`;
|
|
34857
|
-
const closeTag = `</${toolName}>`;
|
|
34858
|
-
const openIndex = xmlString.indexOf(openTag);
|
|
34859
|
-
if (openIndex === -1) {
|
|
34860
|
-
continue;
|
|
34861
|
-
}
|
|
34862
|
-
let closeIndex;
|
|
34863
|
-
if (toolName === "attempt_completion") {
|
|
34864
|
-
closeIndex = xmlString.lastIndexOf(closeTag);
|
|
34865
|
-
if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
|
|
34866
|
-
closeIndex = -1;
|
|
34867
|
-
}
|
|
34868
|
-
} else {
|
|
34869
|
-
closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
|
|
34870
|
-
}
|
|
34871
|
-
let hasClosingTag = closeIndex !== -1;
|
|
34872
|
-
if (closeIndex === -1) {
|
|
34873
|
-
closeIndex = xmlString.length;
|
|
34874
|
-
}
|
|
34875
|
-
const innerContent = xmlString.substring(
|
|
34876
|
-
openIndex + openTag.length,
|
|
34877
|
-
closeIndex
|
|
34878
|
-
);
|
|
34879
|
-
const params = {};
|
|
34880
|
-
const validParams = getValidParamsForTool(toolName);
|
|
34881
|
-
for (const paramName of validParams) {
|
|
34882
|
-
const paramOpenTag = `<${paramName}>`;
|
|
34883
|
-
const paramCloseTag = `</${paramName}>`;
|
|
34884
|
-
const paramOpenIndex = innerContent.indexOf(paramOpenTag);
|
|
34885
|
-
if (paramOpenIndex === -1) {
|
|
34886
|
-
continue;
|
|
34887
|
-
}
|
|
34888
|
-
let paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
|
|
34889
|
-
if (paramCloseIndex === -1) {
|
|
34890
|
-
let nextTagIndex = innerContent.length;
|
|
34891
|
-
for (const nextParam of validParams) {
|
|
34892
|
-
const nextOpenTag = `<${nextParam}>`;
|
|
34893
|
-
const nextIndex = innerContent.indexOf(nextOpenTag, paramOpenIndex + paramOpenTag.length);
|
|
34894
|
-
if (nextIndex !== -1 && nextIndex < nextTagIndex) {
|
|
34895
|
-
nextTagIndex = nextIndex;
|
|
34896
|
-
}
|
|
34897
|
-
}
|
|
34898
|
-
paramCloseIndex = nextTagIndex;
|
|
34899
|
-
}
|
|
34900
|
-
let paramValue = innerContent.substring(
|
|
34901
|
-
paramOpenIndex + paramOpenTag.length,
|
|
34902
|
-
paramCloseIndex
|
|
34903
|
-
).trim();
|
|
34904
|
-
if (paramValue.toLowerCase() === "true") {
|
|
34905
|
-
paramValue = true;
|
|
34906
|
-
} else if (paramValue.toLowerCase() === "false") {
|
|
34907
|
-
paramValue = false;
|
|
34908
|
-
} else if (!isNaN(paramValue) && paramValue.trim() !== "") {
|
|
34909
|
-
const num = Number(paramValue);
|
|
34910
|
-
if (Number.isFinite(num)) {
|
|
34911
|
-
paramValue = num;
|
|
34912
|
-
}
|
|
34913
|
-
}
|
|
34914
|
-
params[paramName] = paramValue;
|
|
34915
|
-
}
|
|
34916
|
-
if (toolName === "attempt_completion") {
|
|
34917
|
-
params["result"] = innerContent.trim();
|
|
34918
|
-
if (params.command) {
|
|
34919
|
-
delete params.command;
|
|
34920
|
-
}
|
|
34921
|
-
}
|
|
34922
|
-
return { toolName, params };
|
|
34923
|
-
}
|
|
34924
|
-
return null;
|
|
34925
|
-
}
|
|
34926
|
-
function createMessagePreview(message, charsPerSide = 200) {
|
|
34927
|
-
if (message === null || message === void 0) {
|
|
34928
|
-
return "null/undefined";
|
|
34929
|
-
}
|
|
34930
|
-
if (typeof message !== "string") {
|
|
34931
|
-
return "null/undefined";
|
|
34932
|
-
}
|
|
34933
|
-
const totalChars = charsPerSide * 2;
|
|
34934
|
-
if (message.length <= totalChars) {
|
|
34935
|
-
return message;
|
|
34936
|
-
}
|
|
34937
|
-
const start = message.substring(0, charsPerSide);
|
|
34938
|
-
const end = message.substring(message.length - charsPerSide);
|
|
34939
|
-
return `${start}...${end}`;
|
|
34940
|
-
}
|
|
34941
|
-
function parseTargets(targets) {
|
|
34942
|
-
if (!targets || typeof targets !== "string") {
|
|
34943
|
-
return [];
|
|
34826
|
+
// src/tools/edit.js
|
|
34827
|
+
function isPathAllowed(filePath, allowedFolders) {
|
|
34828
|
+
if (!allowedFolders || allowedFolders.length === 0) {
|
|
34829
|
+
const resolvedPath3 = (0, import_path5.resolve)(filePath);
|
|
34830
|
+
const cwd = (0, import_path5.resolve)(process.cwd());
|
|
34831
|
+
return resolvedPath3 === cwd || resolvedPath3.startsWith(cwd + import_path5.sep);
|
|
34944
34832
|
}
|
|
34945
|
-
|
|
34946
|
-
|
|
34947
|
-
|
|
34948
|
-
|
|
34949
|
-
const paths = pathStr.split(",").map((p4) => p4.trim()).filter((p4) => p4.length > 0);
|
|
34950
|
-
return paths.map((p4) => {
|
|
34951
|
-
if ((0, import_path5.isAbsolute)(p4)) {
|
|
34952
|
-
return p4;
|
|
34953
|
-
}
|
|
34954
|
-
return cwd ? (0, import_path5.resolve)(cwd, p4) : p4;
|
|
34833
|
+
const resolvedPath2 = (0, import_path5.resolve)(filePath);
|
|
34834
|
+
return allowedFolders.some((folder) => {
|
|
34835
|
+
const allowedPath = (0, import_path5.resolve)(folder);
|
|
34836
|
+
return resolvedPath2 === allowedPath || resolvedPath2.startsWith(allowedPath + import_path5.sep);
|
|
34955
34837
|
});
|
|
34956
34838
|
}
|
|
34957
|
-
function
|
|
34958
|
-
|
|
34959
|
-
|
|
34960
|
-
|
|
34961
|
-
|
|
34962
|
-
|
|
34963
|
-
filePart = target.substring(0, colonIdx);
|
|
34964
|
-
suffix = target.substring(colonIdx);
|
|
34965
|
-
} else if (hashIdx !== -1) {
|
|
34966
|
-
filePart = target.substring(0, hashIdx);
|
|
34967
|
-
suffix = target.substring(hashIdx);
|
|
34968
|
-
} else {
|
|
34969
|
-
filePart = target;
|
|
34970
|
-
suffix = "";
|
|
34971
|
-
}
|
|
34972
|
-
if (!(0, import_path5.isAbsolute)(filePart) && cwd) {
|
|
34973
|
-
filePart = (0, import_path5.resolve)(cwd, filePart);
|
|
34974
|
-
}
|
|
34975
|
-
return filePart + suffix;
|
|
34839
|
+
function parseFileToolOptions(options = {}) {
|
|
34840
|
+
return {
|
|
34841
|
+
debug: options.debug || false,
|
|
34842
|
+
allowedFolders: options.allowedFolders || [],
|
|
34843
|
+
cwd: options.cwd
|
|
34844
|
+
};
|
|
34976
34845
|
}
|
|
34977
|
-
var
|
|
34978
|
-
var
|
|
34979
|
-
"src/tools/
|
|
34846
|
+
var import_ai, import_fs4, import_path5, import_fs5, editTool, createTool, editSchema, createSchema, editDescription, createDescription, editToolDefinition, createToolDefinition;
|
|
34847
|
+
var init_edit = __esm({
|
|
34848
|
+
"src/tools/edit.js"() {
|
|
34980
34849
|
"use strict";
|
|
34981
|
-
|
|
34850
|
+
import_ai = require("ai");
|
|
34851
|
+
import_fs4 = require("fs");
|
|
34982
34852
|
import_path5 = require("path");
|
|
34983
|
-
|
|
34984
|
-
|
|
34985
|
-
|
|
34986
|
-
|
|
34987
|
-
|
|
34988
|
-
|
|
34989
|
-
path: external_exports.string().optional().default(".").describe("Path to search in"),
|
|
34990
|
-
language: external_exports.string().optional().default("rust").describe("Programming language to use for parsing"),
|
|
34991
|
-
allow_tests: external_exports.boolean().optional().default(true).describe("Allow test files in search results")
|
|
34992
|
-
});
|
|
34993
|
-
extractSchema = external_exports.object({
|
|
34994
|
-
targets: external_exports.string().optional().describe('File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (symbol). Multiple targets separated by spaces.'),
|
|
34995
|
-
input_content: external_exports.string().optional().describe("Text content to extract file paths from (alternative to targets)"),
|
|
34996
|
-
allow_tests: external_exports.boolean().optional().default(true).describe("Include test files in extraction results")
|
|
34997
|
-
});
|
|
34998
|
-
delegateSchema = external_exports.object({
|
|
34999
|
-
task: external_exports.string().describe("The task to delegate to a subagent. Be specific about what needs to be accomplished.")
|
|
35000
|
-
});
|
|
35001
|
-
bashSchema = external_exports.object({
|
|
35002
|
-
command: external_exports.string().describe("The bash command to execute"),
|
|
35003
|
-
workingDirectory: external_exports.string().optional().describe("Directory to execute the command in (optional)"),
|
|
35004
|
-
timeout: external_exports.number().optional().describe("Command timeout in milliseconds (optional)"),
|
|
35005
|
-
env: external_exports.record(external_exports.string()).optional().describe("Additional environment variables (optional)")
|
|
35006
|
-
});
|
|
35007
|
-
attemptCompletionSchema = {
|
|
35008
|
-
// Custom validation that requires result parameter but allows direct XML response
|
|
35009
|
-
safeParse: (params) => {
|
|
35010
|
-
if (!params || typeof params !== "object") {
|
|
35011
|
-
return {
|
|
35012
|
-
success: false,
|
|
35013
|
-
error: {
|
|
35014
|
-
issues: [{
|
|
35015
|
-
code: "invalid_type",
|
|
35016
|
-
expected: "object",
|
|
35017
|
-
received: typeof params,
|
|
35018
|
-
path: [],
|
|
35019
|
-
message: "Expected object"
|
|
35020
|
-
}]
|
|
35021
|
-
}
|
|
35022
|
-
};
|
|
35023
|
-
}
|
|
35024
|
-
if (!("result" in params)) {
|
|
35025
|
-
return {
|
|
35026
|
-
success: false,
|
|
35027
|
-
error: {
|
|
35028
|
-
issues: [{
|
|
35029
|
-
code: "invalid_type",
|
|
35030
|
-
expected: "string",
|
|
35031
|
-
received: "undefined",
|
|
35032
|
-
path: ["result"],
|
|
35033
|
-
message: "Required"
|
|
35034
|
-
}]
|
|
35035
|
-
}
|
|
35036
|
-
};
|
|
35037
|
-
}
|
|
35038
|
-
if (typeof params.result !== "string") {
|
|
35039
|
-
return {
|
|
35040
|
-
success: false,
|
|
35041
|
-
error: {
|
|
35042
|
-
issues: [{
|
|
35043
|
-
code: "invalid_type",
|
|
35044
|
-
expected: "string",
|
|
35045
|
-
received: typeof params.result,
|
|
35046
|
-
path: ["result"],
|
|
35047
|
-
message: "Expected string"
|
|
35048
|
-
}]
|
|
35049
|
-
}
|
|
35050
|
-
};
|
|
35051
|
-
}
|
|
35052
|
-
const filteredData = { result: params.result };
|
|
35053
|
-
return {
|
|
35054
|
-
success: true,
|
|
35055
|
-
data: filteredData
|
|
35056
|
-
};
|
|
35057
|
-
}
|
|
35058
|
-
};
|
|
35059
|
-
searchToolDefinition = `
|
|
35060
|
-
## search
|
|
35061
|
-
Description: Search code in the repository using Elasticsearch query syntax (except field based queries, e.g. "filename:..." NOT supported).
|
|
35062
|
-
|
|
35063
|
-
You need to focus on main keywords when constructing the query, and always use elastic search syntax like OR AND and brackets to group keywords.
|
|
34853
|
+
import_fs5 = require("fs");
|
|
34854
|
+
editTool = (options = {}) => {
|
|
34855
|
+
const { debug, allowedFolders, cwd } = parseFileToolOptions(options);
|
|
34856
|
+
return (0, import_ai.tool)({
|
|
34857
|
+
name: "edit",
|
|
34858
|
+
description: `Edit files using exact string replacement (Claude Code style).
|
|
35064
34859
|
|
|
35065
|
-
|
|
35066
|
-
- Ensure not to re-read the same symbols twice - reuse context from previous tool calls
|
|
35067
|
-
- Probe returns a session ID on first run - reuse it for subsequent calls to avoid redundant searches
|
|
35068
|
-
- Once data is returned, it's cached and won't return on next runs (this is expected behavior)
|
|
34860
|
+
This tool performs exact string replacements in files. It requires the old_string to match exactly what's in the file, including all whitespace and indentation.
|
|
35069
34861
|
|
|
35070
34862
|
Parameters:
|
|
35071
|
-
-
|
|
35072
|
-
-
|
|
35073
|
-
|
|
35074
|
-
|
|
35075
|
-
|
|
35076
|
-
Usage Example:
|
|
35077
|
-
|
|
35078
|
-
<examples>
|
|
35079
|
-
|
|
35080
|
-
User: Where is the login logic?
|
|
35081
|
-
Assistant workflow:
|
|
35082
|
-
1. <search>
|
|
35083
|
-
<query>login AND auth AND token</query>
|
|
35084
|
-
<path>.</path>
|
|
35085
|
-
</search>
|
|
35086
|
-
2. Now lets look closer: <extract>
|
|
35087
|
-
<targets>session.rs#AuthService.login auth.rs:2-100</targets>
|
|
35088
|
-
</extract>
|
|
35089
|
-
|
|
35090
|
-
User: How to calculate the total amount in the payments module?
|
|
35091
|
-
<search>
|
|
35092
|
-
<query>calculate AND payment</query>
|
|
35093
|
-
<path>src/utils</path>
|
|
35094
|
-
</search>
|
|
35095
|
-
|
|
35096
|
-
User: How do the user authentication and authorization work?
|
|
35097
|
-
<search>
|
|
35098
|
-
<query>+user AND (authentication OR authorization OR authz)</query>
|
|
35099
|
-
<path>.</path>
|
|
35100
|
-
</search>
|
|
34863
|
+
- file_path: Path to the file to edit (absolute or relative)
|
|
34864
|
+
- old_string: Exact text to find and replace (must be unique in the file unless replace_all is true)
|
|
34865
|
+
- new_string: Text to replace with
|
|
34866
|
+
- replace_all: (optional) Replace all occurrences instead of requiring uniqueness
|
|
35101
34867
|
|
|
35102
|
-
|
|
35103
|
-
|
|
35104
|
-
|
|
35105
|
-
|
|
35106
|
-
|
|
34868
|
+
Important:
|
|
34869
|
+
- The old_string must match EXACTLY including whitespace
|
|
34870
|
+
- If old_string appears multiple times and replace_all is false, the edit will fail
|
|
34871
|
+
- Use larger context around the string to ensure uniqueness when needed`,
|
|
34872
|
+
inputSchema: {
|
|
34873
|
+
type: "object",
|
|
34874
|
+
properties: {
|
|
34875
|
+
file_path: {
|
|
34876
|
+
type: "string",
|
|
34877
|
+
description: "Path to the file to edit"
|
|
34878
|
+
},
|
|
34879
|
+
old_string: {
|
|
34880
|
+
type: "string",
|
|
34881
|
+
description: "Exact text to find and replace"
|
|
34882
|
+
},
|
|
34883
|
+
new_string: {
|
|
34884
|
+
type: "string",
|
|
34885
|
+
description: "Text to replace with"
|
|
34886
|
+
},
|
|
34887
|
+
replace_all: {
|
|
34888
|
+
type: "boolean",
|
|
34889
|
+
description: "Replace all occurrences (default: false)",
|
|
34890
|
+
default: false
|
|
34891
|
+
}
|
|
34892
|
+
},
|
|
34893
|
+
required: ["file_path", "old_string", "new_string"]
|
|
34894
|
+
},
|
|
34895
|
+
execute: async ({ file_path, old_string, new_string, replace_all = false }) => {
|
|
34896
|
+
try {
|
|
34897
|
+
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
34898
|
+
return `Error editing file: Invalid file_path - must be a non-empty string`;
|
|
34899
|
+
}
|
|
34900
|
+
if (old_string === void 0 || old_string === null || typeof old_string !== "string") {
|
|
34901
|
+
return `Error editing file: Invalid old_string - must be a string`;
|
|
34902
|
+
}
|
|
34903
|
+
if (new_string === void 0 || new_string === null || typeof new_string !== "string") {
|
|
34904
|
+
return `Error editing file: Invalid new_string - must be a string`;
|
|
34905
|
+
}
|
|
34906
|
+
const resolvedPath2 = (0, import_path5.isAbsolute)(file_path) ? file_path : (0, import_path5.resolve)(cwd || process.cwd(), file_path);
|
|
34907
|
+
if (debug) {
|
|
34908
|
+
console.error(`[Edit] Attempting to edit file: ${resolvedPath2}`);
|
|
34909
|
+
}
|
|
34910
|
+
if (!isPathAllowed(resolvedPath2, allowedFolders)) {
|
|
34911
|
+
return `Error editing file: Permission denied - ${file_path} is outside allowed directories`;
|
|
34912
|
+
}
|
|
34913
|
+
if (!(0, import_fs5.existsSync)(resolvedPath2)) {
|
|
34914
|
+
return `Error editing file: File not found - ${file_path}`;
|
|
34915
|
+
}
|
|
34916
|
+
const content = await import_fs4.promises.readFile(resolvedPath2, "utf-8");
|
|
34917
|
+
if (!content.includes(old_string)) {
|
|
34918
|
+
return `Error editing file: String not found - the specified old_string was not found in ${file_path}`;
|
|
34919
|
+
}
|
|
34920
|
+
const occurrences = content.split(old_string).length - 1;
|
|
34921
|
+
if (!replace_all && occurrences > 1) {
|
|
34922
|
+
return `Error editing file: Multiple occurrences found - the old_string appears ${occurrences} times. Use replace_all: true to replace all occurrences, or provide more context to make the string unique.`;
|
|
34923
|
+
}
|
|
34924
|
+
let newContent;
|
|
34925
|
+
if (replace_all) {
|
|
34926
|
+
newContent = content.replaceAll(old_string, new_string);
|
|
34927
|
+
} else {
|
|
34928
|
+
newContent = content.replace(old_string, new_string);
|
|
34929
|
+
}
|
|
34930
|
+
if (newContent === content) {
|
|
34931
|
+
return `Error editing file: No changes made - old_string and new_string might be the same`;
|
|
34932
|
+
}
|
|
34933
|
+
await import_fs4.promises.writeFile(resolvedPath2, newContent, "utf-8");
|
|
34934
|
+
const replacedCount = replace_all ? occurrences : 1;
|
|
34935
|
+
if (debug) {
|
|
34936
|
+
console.error(`[Edit] Successfully edited ${resolvedPath2}, replaced ${replacedCount} occurrence(s)`);
|
|
34937
|
+
}
|
|
34938
|
+
return `Successfully edited ${file_path} (${replacedCount} replacement${replacedCount !== 1 ? "s" : ""})`;
|
|
34939
|
+
} catch (error2) {
|
|
34940
|
+
console.error("[Edit] Error:", error2);
|
|
34941
|
+
return `Error editing file: ${error2.message}`;
|
|
34942
|
+
}
|
|
34943
|
+
}
|
|
34944
|
+
});
|
|
34945
|
+
};
|
|
34946
|
+
createTool = (options = {}) => {
|
|
34947
|
+
const { debug, allowedFolders, cwd } = parseFileToolOptions(options);
|
|
34948
|
+
return (0, import_ai.tool)({
|
|
34949
|
+
name: "create",
|
|
34950
|
+
description: `Create new files with specified content.
|
|
35107
34951
|
|
|
35108
|
-
|
|
35109
|
-
<search>
|
|
35110
|
-
<query>decompound</query>
|
|
35111
|
-
<path>/dep/rust/decompound</path>
|
|
35112
|
-
</search>
|
|
34952
|
+
This tool creates new files in the filesystem. It will create parent directories if they don't exist.
|
|
35113
34953
|
|
|
35114
|
-
</examples>
|
|
35115
|
-
`;
|
|
35116
|
-
queryToolDefinition = `
|
|
35117
|
-
## query
|
|
35118
|
-
Description: Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.
|
|
35119
34954
|
Parameters:
|
|
35120
|
-
-
|
|
35121
|
-
-
|
|
35122
|
-
-
|
|
35123
|
-
- allow_tests: (optional, default: true) Allow test files in search results (true/false).
|
|
35124
|
-
Usage Example:
|
|
35125
|
-
|
|
35126
|
-
<examples>
|
|
35127
|
-
|
|
35128
|
-
<query>
|
|
35129
|
-
<pattern>function $FUNC($$$PARAMS) { $$$BODY }</pattern>
|
|
35130
|
-
<path>src/parser</path>
|
|
35131
|
-
<language>js</language>
|
|
35132
|
-
</query>
|
|
34955
|
+
- file_path: Path where the file should be created (absolute or relative)
|
|
34956
|
+
- content: Content to write to the file
|
|
34957
|
+
- overwrite: (optional) Whether to overwrite if file exists (default: false)
|
|
35133
34958
|
|
|
35134
|
-
|
|
35135
|
-
|
|
35136
|
-
|
|
35137
|
-
|
|
35138
|
-
|
|
35139
|
-
|
|
34959
|
+
Important:
|
|
34960
|
+
- By default, will fail if the file already exists
|
|
34961
|
+
- Set overwrite: true to replace existing files
|
|
34962
|
+
- Parent directories will be created automatically if needed`,
|
|
34963
|
+
inputSchema: {
|
|
34964
|
+
type: "object",
|
|
34965
|
+
properties: {
|
|
34966
|
+
file_path: {
|
|
34967
|
+
type: "string",
|
|
34968
|
+
description: "Path where the file should be created"
|
|
34969
|
+
},
|
|
34970
|
+
content: {
|
|
34971
|
+
type: "string",
|
|
34972
|
+
description: "Content to write to the file"
|
|
34973
|
+
},
|
|
34974
|
+
overwrite: {
|
|
34975
|
+
type: "boolean",
|
|
34976
|
+
description: "Overwrite if file exists (default: false)",
|
|
34977
|
+
default: false
|
|
34978
|
+
}
|
|
34979
|
+
},
|
|
34980
|
+
required: ["file_path", "content"]
|
|
34981
|
+
},
|
|
34982
|
+
execute: async ({ file_path, content, overwrite = false }) => {
|
|
34983
|
+
try {
|
|
34984
|
+
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
34985
|
+
return `Error creating file: Invalid file_path - must be a non-empty string`;
|
|
34986
|
+
}
|
|
34987
|
+
if (content === void 0 || content === null || typeof content !== "string") {
|
|
34988
|
+
return `Error creating file: Invalid content - must be a string`;
|
|
34989
|
+
}
|
|
34990
|
+
const resolvedPath2 = (0, import_path5.isAbsolute)(file_path) ? file_path : (0, import_path5.resolve)(cwd || process.cwd(), file_path);
|
|
34991
|
+
if (debug) {
|
|
34992
|
+
console.error(`[Create] Attempting to create file: ${resolvedPath2}`);
|
|
34993
|
+
}
|
|
34994
|
+
if (!isPathAllowed(resolvedPath2, allowedFolders)) {
|
|
34995
|
+
return `Error creating file: Permission denied - ${file_path} is outside allowed directories`;
|
|
34996
|
+
}
|
|
34997
|
+
if ((0, import_fs5.existsSync)(resolvedPath2) && !overwrite) {
|
|
34998
|
+
return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
|
|
34999
|
+
}
|
|
35000
|
+
const dir = (0, import_path5.dirname)(resolvedPath2);
|
|
35001
|
+
await import_fs4.promises.mkdir(dir, { recursive: true });
|
|
35002
|
+
await import_fs4.promises.writeFile(resolvedPath2, content, "utf-8");
|
|
35003
|
+
const action = (0, import_fs5.existsSync)(resolvedPath2) && overwrite ? "overwrote" : "created";
|
|
35004
|
+
const bytes = Buffer.byteLength(content, "utf-8");
|
|
35005
|
+
if (debug) {
|
|
35006
|
+
console.error(`[Create] Successfully ${action} ${resolvedPath2}`);
|
|
35007
|
+
}
|
|
35008
|
+
return `Successfully ${action} ${file_path} (${bytes} bytes)`;
|
|
35009
|
+
} catch (error2) {
|
|
35010
|
+
console.error("[Create] Error:", error2);
|
|
35011
|
+
return `Error creating file: ${error2.message}`;
|
|
35012
|
+
}
|
|
35013
|
+
}
|
|
35014
|
+
});
|
|
35015
|
+
};
|
|
35016
|
+
editSchema = {
|
|
35017
|
+
type: "object",
|
|
35018
|
+
properties: {
|
|
35019
|
+
file_path: {
|
|
35020
|
+
type: "string",
|
|
35021
|
+
description: "Path to the file to edit"
|
|
35022
|
+
},
|
|
35023
|
+
old_string: {
|
|
35024
|
+
type: "string",
|
|
35025
|
+
description: "Exact text to find and replace"
|
|
35026
|
+
},
|
|
35027
|
+
new_string: {
|
|
35028
|
+
type: "string",
|
|
35029
|
+
description: "Text to replace with"
|
|
35030
|
+
},
|
|
35031
|
+
replace_all: {
|
|
35032
|
+
type: "boolean",
|
|
35033
|
+
description: "Replace all occurrences (default: false)"
|
|
35034
|
+
}
|
|
35035
|
+
},
|
|
35036
|
+
required: ["file_path", "old_string", "new_string"]
|
|
35037
|
+
};
|
|
35038
|
+
createSchema = {
|
|
35039
|
+
type: "object",
|
|
35040
|
+
properties: {
|
|
35041
|
+
file_path: {
|
|
35042
|
+
type: "string",
|
|
35043
|
+
description: "Path where the file should be created"
|
|
35044
|
+
},
|
|
35045
|
+
content: {
|
|
35046
|
+
type: "string",
|
|
35047
|
+
description: "Content to write to the file"
|
|
35048
|
+
},
|
|
35049
|
+
overwrite: {
|
|
35050
|
+
type: "boolean",
|
|
35051
|
+
description: "Overwrite if file exists (default: false)"
|
|
35052
|
+
}
|
|
35053
|
+
},
|
|
35054
|
+
required: ["file_path", "content"]
|
|
35055
|
+
};
|
|
35056
|
+
editDescription = "Edit files using exact string replacement. Requires exact match including whitespace.";
|
|
35057
|
+
createDescription = "Create new files with specified content. Will create parent directories if needed.";
|
|
35058
|
+
editToolDefinition = `
|
|
35059
|
+
## edit
|
|
35060
|
+
Description: ${editDescription}
|
|
35140
35061
|
|
|
35141
|
-
|
|
35062
|
+
When to use:
|
|
35063
|
+
- For precise, surgical edits to existing files
|
|
35064
|
+
- When you need to change specific lines or blocks of code
|
|
35065
|
+
- For renaming functions, variables, or updating configuration values
|
|
35066
|
+
- When the exact text to replace is known and unique (or use replace_all for multiple occurrences)
|
|
35142
35067
|
|
|
35143
|
-
|
|
35068
|
+
When NOT to use:
|
|
35069
|
+
- For creating new files (use 'create' tool instead)
|
|
35070
|
+
- When you cannot determine the exact text to replace
|
|
35071
|
+
- When changes span multiple locations that would be better handled together
|
|
35144
35072
|
|
|
35145
35073
|
Parameters:
|
|
35146
|
-
-
|
|
35147
|
-
-
|
|
35148
|
-
-
|
|
35149
|
-
|
|
35150
|
-
Usage Example:
|
|
35151
|
-
|
|
35152
|
-
<examples>
|
|
35074
|
+
- file_path: (required) Path to the file to edit
|
|
35075
|
+
- old_string: (required) Exact text to find and replace (must match including whitespace, newlines, and indentation)
|
|
35076
|
+
- new_string: (required) Text to replace with
|
|
35077
|
+
- replace_all: (optional, default: false) Replace all occurrences if the string appears multiple times
|
|
35153
35078
|
|
|
35154
|
-
|
|
35155
|
-
|
|
35156
|
-
|
|
35157
|
-
|
|
35079
|
+
Important notes:
|
|
35080
|
+
- The old_string MUST match EXACTLY, including all whitespace, indentation, and line breaks
|
|
35081
|
+
- If old_string appears multiple times and replace_all is false, the tool will fail
|
|
35082
|
+
- Always verify the exact formatting of the text you want to replace
|
|
35158
35083
|
|
|
35159
|
-
|
|
35160
|
-
<
|
|
35161
|
-
<
|
|
35162
|
-
|
|
35163
|
-
|
|
35164
|
-
|
|
35165
|
-
<
|
|
35166
|
-
|
|
35167
|
-
</
|
|
35168
|
-
|
|
35169
|
-
User: Lets read the whole file
|
|
35170
|
-
<extract>
|
|
35171
|
-
<targets>src/search/ranking.rs</targets>
|
|
35172
|
-
</extract>
|
|
35173
|
-
|
|
35174
|
-
User: Read the first 10 lines of the file
|
|
35175
|
-
<extract>
|
|
35176
|
-
<targets>src/search/ranking.rs:1-10</targets>
|
|
35177
|
-
</extract>
|
|
35178
|
-
|
|
35179
|
-
User: Read file inside the dependency
|
|
35180
|
-
<extract>
|
|
35181
|
-
<targets>/dep/go/github.com/gorilla/mux/router.go</targets>
|
|
35182
|
-
</extract>
|
|
35183
|
-
|
|
35184
|
-
</examples>
|
|
35185
|
-
`;
|
|
35186
|
-
delegateToolDefinition = `
|
|
35187
|
-
## delegate
|
|
35188
|
-
Description: Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Use this when you recognize that a user's request involves multiple large, distinct components that would benefit from parallel processing or specialized focus. The AI agent should automatically identify opportunities for task separation and use delegation without explicit user instruction.
|
|
35189
|
-
|
|
35190
|
-
Parameters:
|
|
35191
|
-
- task: (required) A complete, self-contained task that can be executed independently by a subagent. Should be specific and focused on one area of expertise.
|
|
35192
|
-
|
|
35193
|
-
Usage Pattern:
|
|
35194
|
-
When the AI agent encounters complex multi-part requests, it should automatically break them down and delegate:
|
|
35084
|
+
Examples:
|
|
35085
|
+
<edit>
|
|
35086
|
+
<file_path>src/main.js</file_path>
|
|
35087
|
+
<old_string>function oldName() {
|
|
35088
|
+
return 42;
|
|
35089
|
+
}</old_string>
|
|
35090
|
+
<new_string>function newName() {
|
|
35091
|
+
return 42;
|
|
35092
|
+
}</new_string>
|
|
35093
|
+
</edit>
|
|
35195
35094
|
|
|
35196
|
-
<
|
|
35197
|
-
<
|
|
35198
|
-
</
|
|
35095
|
+
<edit>
|
|
35096
|
+
<file_path>config.json</file_path>
|
|
35097
|
+
<old_string>"debug": false</old_string>
|
|
35098
|
+
<new_string>"debug": true</new_string>
|
|
35099
|
+
<replace_all>true</replace_all>
|
|
35100
|
+
</edit>`;
|
|
35101
|
+
createToolDefinition = `
|
|
35102
|
+
## create
|
|
35103
|
+
Description: ${createDescription}
|
|
35199
35104
|
|
|
35200
|
-
|
|
35201
|
-
|
|
35202
|
-
|
|
35105
|
+
When to use:
|
|
35106
|
+
- For creating brand new files from scratch
|
|
35107
|
+
- When you need to add configuration files, documentation, or new modules
|
|
35108
|
+
- For generating boilerplate code or templates
|
|
35109
|
+
- When you have the complete content ready to write
|
|
35203
35110
|
|
|
35204
|
-
|
|
35205
|
-
|
|
35206
|
-
|
|
35207
|
-
## attempt_completion
|
|
35208
|
-
Description: Use this tool ONLY when the task is fully complete and you have received confirmation of success for all previous tool uses. Presents the final result to the user. You can provide your response directly inside the XML tags without any parameter wrapper.
|
|
35209
|
-
Parameters:
|
|
35210
|
-
- No validation required - provide your complete answer directly inside the XML tags.
|
|
35211
|
-
Usage Example:
|
|
35212
|
-
<attempt_completion>
|
|
35213
|
-
I have refactored the search module according to the requirements and verified the tests pass. The module now uses the new BM25 ranking algorithm and has improved error handling.
|
|
35214
|
-
</attempt_completion>
|
|
35215
|
-
`;
|
|
35216
|
-
bashToolDefinition = `
|
|
35217
|
-
## bash
|
|
35218
|
-
Description: Execute bash commands for system exploration and development tasks. This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
|
|
35111
|
+
When NOT to use:
|
|
35112
|
+
- For editing existing files (use 'edit' tool instead)
|
|
35113
|
+
- When a file already exists unless you explicitly want to overwrite it
|
|
35219
35114
|
|
|
35220
35115
|
Parameters:
|
|
35221
|
-
-
|
|
35222
|
-
-
|
|
35223
|
-
-
|
|
35224
|
-
- env: (optional) Additional environment variables as an object
|
|
35225
|
-
|
|
35226
|
-
Security: Commands are filtered through allow/deny lists for safety:
|
|
35227
|
-
- Allowed by default: ls, cat, git status, npm list, find, grep, etc.
|
|
35228
|
-
- Denied by default: rm -rf, sudo, npm install, dangerous system commands
|
|
35229
|
-
|
|
35230
|
-
Usage Examples:
|
|
35231
|
-
|
|
35232
|
-
<examples>
|
|
35233
|
-
|
|
35234
|
-
User: What files are in the src directory?
|
|
35235
|
-
<bash>
|
|
35236
|
-
<command>ls -la src/</command>
|
|
35237
|
-
</bash>
|
|
35238
|
-
|
|
35239
|
-
User: Show me the git status
|
|
35240
|
-
<bash>
|
|
35241
|
-
<command>git status</command>
|
|
35242
|
-
</bash>
|
|
35243
|
-
|
|
35244
|
-
User: Find all TypeScript files
|
|
35245
|
-
<bash>
|
|
35246
|
-
<command>find . -name "*.ts" -type f</command>
|
|
35247
|
-
</bash>
|
|
35248
|
-
|
|
35249
|
-
User: Check installed npm packages
|
|
35250
|
-
<bash>
|
|
35251
|
-
<command>npm list --depth=0</command>
|
|
35252
|
-
</bash>
|
|
35116
|
+
- file_path: (required) Path where the file should be created
|
|
35117
|
+
- content: (required) Complete content to write to the file
|
|
35118
|
+
- overwrite: (optional, default: false) Whether to overwrite if file already exists
|
|
35253
35119
|
|
|
35254
|
-
|
|
35255
|
-
|
|
35256
|
-
|
|
35257
|
-
|
|
35120
|
+
Important notes:
|
|
35121
|
+
- Parent directories will be created automatically if they don't exist
|
|
35122
|
+
- The tool will fail if the file already exists and overwrite is false
|
|
35123
|
+
- Be careful with the overwrite option as it completely replaces existing files
|
|
35258
35124
|
|
|
35259
|
-
|
|
35260
|
-
<
|
|
35261
|
-
<
|
|
35262
|
-
|
|
35125
|
+
Examples:
|
|
35126
|
+
<create>
|
|
35127
|
+
<file_path>src/newFile.js</file_path>
|
|
35128
|
+
<content>export function hello() {
|
|
35129
|
+
return "Hello, world!";
|
|
35130
|
+
}</content>
|
|
35131
|
+
</create>
|
|
35263
35132
|
|
|
35264
|
-
|
|
35265
|
-
<
|
|
35266
|
-
<
|
|
35267
|
-
</bash>
|
|
35133
|
+
<create>
|
|
35134
|
+
<file_path>README.md</file_path>
|
|
35135
|
+
<content># My Project
|
|
35268
35136
|
|
|
35269
|
-
|
|
35270
|
-
|
|
35271
|
-
|
|
35272
|
-
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
35273
|
-
extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.";
|
|
35274
|
-
delegateDescription = "Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.";
|
|
35275
|
-
DEFAULT_VALID_TOOLS = [
|
|
35276
|
-
"search",
|
|
35277
|
-
"query",
|
|
35278
|
-
"extract",
|
|
35279
|
-
"delegate",
|
|
35280
|
-
"listFiles",
|
|
35281
|
-
"searchFiles",
|
|
35282
|
-
"implement",
|
|
35283
|
-
"bash",
|
|
35284
|
-
"attempt_completion"
|
|
35285
|
-
];
|
|
35137
|
+
This is a new project.</content>
|
|
35138
|
+
<overwrite>true</overwrite>
|
|
35139
|
+
</create>`;
|
|
35286
35140
|
}
|
|
35287
35141
|
});
|
|
35288
35142
|
|
|
35289
|
-
// src/tools/
|
|
35290
|
-
|
|
35291
|
-
|
|
35292
|
-
"
|
|
35293
|
-
"
|
|
35294
|
-
|
|
35295
|
-
|
|
35296
|
-
|
|
35297
|
-
|
|
35298
|
-
|
|
35299
|
-
|
|
35300
|
-
|
|
35301
|
-
|
|
35302
|
-
|
|
35303
|
-
|
|
35304
|
-
|
|
35305
|
-
|
|
35306
|
-
|
|
35307
|
-
|
|
35308
|
-
|
|
35309
|
-
|
|
35310
|
-
|
|
35311
|
-
|
|
35312
|
-
|
|
35313
|
-
|
|
35314
|
-
|
|
35315
|
-
|
|
35316
|
-
|
|
35317
|
-
|
|
35318
|
-
|
|
35319
|
-
|
|
35320
|
-
|
|
35321
|
-
|
|
35322
|
-
|
|
35323
|
-
|
|
35324
|
-
|
|
35325
|
-
|
|
35326
|
-
|
|
35327
|
-
|
|
35328
|
-
|
|
35329
|
-
|
|
35330
|
-
|
|
35331
|
-
|
|
35332
|
-
|
|
35333
|
-
|
|
35334
|
-
|
|
35335
|
-
|
|
35336
|
-
|
|
35337
|
-
|
|
35338
|
-
|
|
35339
|
-
|
|
35340
|
-
|
|
35341
|
-
|
|
35342
|
-
|
|
35343
|
-
|
|
35344
|
-
|
|
35345
|
-
|
|
35346
|
-
|
|
35347
|
-
|
|
35348
|
-
|
|
35349
|
-
|
|
35350
|
-
|
|
35351
|
-
|
|
35352
|
-
|
|
35353
|
-
|
|
35354
|
-
|
|
35355
|
-
|
|
35356
|
-
|
|
35357
|
-
|
|
35358
|
-
|
|
35359
|
-
|
|
35360
|
-
|
|
35361
|
-
|
|
35362
|
-
|
|
35363
|
-
|
|
35364
|
-
|
|
35365
|
-
|
|
35366
|
-
pattern,
|
|
35367
|
-
path: queryPath,
|
|
35368
|
-
cwd: options.cwd,
|
|
35369
|
-
// Working directory for resolving relative paths
|
|
35370
|
-
language,
|
|
35371
|
-
allowTests: allow_tests ?? true,
|
|
35372
|
-
json: false
|
|
35373
|
-
});
|
|
35374
|
-
return results;
|
|
35375
|
-
} catch (error2) {
|
|
35376
|
-
console.error("Error executing query command:", error2);
|
|
35377
|
-
return `Error executing query command: ${error2.message}`;
|
|
35378
|
-
}
|
|
35379
|
-
}
|
|
35380
|
-
});
|
|
35381
|
-
};
|
|
35382
|
-
extractTool = (options = {}) => {
|
|
35383
|
-
const { debug = false, outline = false } = options;
|
|
35384
|
-
return (0, import_ai.tool)({
|
|
35385
|
-
name: "extract",
|
|
35386
|
-
description: extractDescription,
|
|
35387
|
-
inputSchema: extractSchema,
|
|
35388
|
-
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format: format2 }) => {
|
|
35389
|
-
try {
|
|
35390
|
-
const effectiveCwd = options.cwd || ".";
|
|
35391
|
-
if (debug) {
|
|
35392
|
-
if (targets) {
|
|
35393
|
-
console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
|
35394
|
-
} else if (input_content) {
|
|
35395
|
-
console.error(`Executing extract with input content, cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
|
35396
|
-
}
|
|
35397
|
-
}
|
|
35398
|
-
let tempFilePath = null;
|
|
35399
|
-
let extractOptions = { cwd: effectiveCwd };
|
|
35400
|
-
if (input_content) {
|
|
35401
|
-
const { writeFileSync: writeFileSync2, unlinkSync } = await import("fs");
|
|
35402
|
-
const { join: join3 } = await import("path");
|
|
35403
|
-
const { tmpdir } = await import("os");
|
|
35404
|
-
const { randomUUID: randomUUID6 } = await import("crypto");
|
|
35405
|
-
tempFilePath = join3(tmpdir(), `probe-extract-${randomUUID6()}.txt`);
|
|
35406
|
-
writeFileSync2(tempFilePath, input_content);
|
|
35407
|
-
if (debug) {
|
|
35408
|
-
console.error(`Created temporary file for input content: ${tempFilePath}`);
|
|
35409
|
-
}
|
|
35410
|
-
let effectiveFormat = format2;
|
|
35411
|
-
if (outline && format2 === "outline-xml") {
|
|
35412
|
-
effectiveFormat = "xml";
|
|
35413
|
-
}
|
|
35414
|
-
extractOptions = {
|
|
35415
|
-
inputFile: tempFilePath,
|
|
35416
|
-
cwd: effectiveCwd,
|
|
35417
|
-
allowTests: allow_tests ?? true,
|
|
35418
|
-
contextLines: context_lines,
|
|
35419
|
-
format: effectiveFormat
|
|
35420
|
-
};
|
|
35421
|
-
} else if (targets) {
|
|
35422
|
-
const parsedTargets = parseTargets(targets);
|
|
35423
|
-
const files = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
|
|
35424
|
-
let effectiveFormat = format2;
|
|
35425
|
-
if (outline && format2 === "outline-xml") {
|
|
35426
|
-
effectiveFormat = "xml";
|
|
35427
|
-
}
|
|
35428
|
-
extractOptions = {
|
|
35429
|
-
files,
|
|
35430
|
-
cwd: effectiveCwd,
|
|
35431
|
-
allowTests: allow_tests ?? true,
|
|
35432
|
-
contextLines: context_lines,
|
|
35433
|
-
format: effectiveFormat
|
|
35434
|
-
};
|
|
35435
|
-
} else {
|
|
35436
|
-
throw new Error("Either targets or input_content must be provided");
|
|
35437
|
-
}
|
|
35438
|
-
const results = await extract(extractOptions);
|
|
35439
|
-
if (tempFilePath) {
|
|
35440
|
-
const { unlinkSync } = await import("fs");
|
|
35441
|
-
try {
|
|
35442
|
-
unlinkSync(tempFilePath);
|
|
35443
|
-
if (debug) {
|
|
35444
|
-
console.error(`Removed temporary file: ${tempFilePath}`);
|
|
35445
|
-
}
|
|
35446
|
-
} catch (cleanupError) {
|
|
35447
|
-
console.error(`Warning: Failed to remove temporary file: ${cleanupError.message}`);
|
|
35448
|
-
}
|
|
35449
|
-
}
|
|
35450
|
-
return results;
|
|
35451
|
-
} catch (error2) {
|
|
35452
|
-
console.error("Error executing extract command:", error2);
|
|
35453
|
-
return `Error executing extract command: ${error2.message}`;
|
|
35143
|
+
// src/tools/common.js
|
|
35144
|
+
function buildToolTagPattern(tools2 = DEFAULT_VALID_TOOLS) {
|
|
35145
|
+
const allTools = [...tools2];
|
|
35146
|
+
if (allTools.includes("attempt_completion") && !allTools.includes("attempt_complete")) {
|
|
35147
|
+
allTools.push("attempt_complete");
|
|
35148
|
+
}
|
|
35149
|
+
const escaped = allTools.map((t4) => t4.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
35150
|
+
return new RegExp(`<(${escaped.join("|")})>`);
|
|
35151
|
+
}
|
|
35152
|
+
function getValidParamsForTool(toolName) {
|
|
35153
|
+
const schemaMap = {
|
|
35154
|
+
search: searchSchema,
|
|
35155
|
+
query: querySchema,
|
|
35156
|
+
extract: extractSchema,
|
|
35157
|
+
delegate: delegateSchema,
|
|
35158
|
+
bash: bashSchema,
|
|
35159
|
+
attempt_completion: attemptCompletionSchema,
|
|
35160
|
+
edit: editSchema,
|
|
35161
|
+
create: createSchema
|
|
35162
|
+
};
|
|
35163
|
+
const schema = schemaMap[toolName];
|
|
35164
|
+
if (!schema) {
|
|
35165
|
+
return ["path", "directory", "pattern", "recursive", "includeHidden", "task", "files", "autoCommits", "result"];
|
|
35166
|
+
}
|
|
35167
|
+
if (toolName === "attempt_completion") {
|
|
35168
|
+
return ["result"];
|
|
35169
|
+
}
|
|
35170
|
+
if (schema._def && schema._def.shape) {
|
|
35171
|
+
return Object.keys(schema._def.shape());
|
|
35172
|
+
}
|
|
35173
|
+
if (schema.properties) {
|
|
35174
|
+
return Object.keys(schema.properties);
|
|
35175
|
+
}
|
|
35176
|
+
return [];
|
|
35177
|
+
}
|
|
35178
|
+
function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
35179
|
+
for (const toolName of validTools) {
|
|
35180
|
+
const openTag = `<${toolName}>`;
|
|
35181
|
+
const closeTag = `</${toolName}>`;
|
|
35182
|
+
const openIndex = xmlString.indexOf(openTag);
|
|
35183
|
+
if (openIndex === -1) {
|
|
35184
|
+
continue;
|
|
35185
|
+
}
|
|
35186
|
+
let closeIndex;
|
|
35187
|
+
if (toolName === "attempt_completion") {
|
|
35188
|
+
closeIndex = xmlString.lastIndexOf(closeTag);
|
|
35189
|
+
if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
|
|
35190
|
+
closeIndex = -1;
|
|
35191
|
+
}
|
|
35192
|
+
} else {
|
|
35193
|
+
closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
|
|
35194
|
+
}
|
|
35195
|
+
let hasClosingTag = closeIndex !== -1;
|
|
35196
|
+
if (closeIndex === -1) {
|
|
35197
|
+
closeIndex = xmlString.length;
|
|
35198
|
+
}
|
|
35199
|
+
const innerContent = xmlString.substring(
|
|
35200
|
+
openIndex + openTag.length,
|
|
35201
|
+
closeIndex
|
|
35202
|
+
);
|
|
35203
|
+
const params = {};
|
|
35204
|
+
const validParams = getValidParamsForTool(toolName);
|
|
35205
|
+
for (const paramName of validParams) {
|
|
35206
|
+
const paramOpenTag = `<${paramName}>`;
|
|
35207
|
+
const paramCloseTag = `</${paramName}>`;
|
|
35208
|
+
const paramOpenIndex = innerContent.indexOf(paramOpenTag);
|
|
35209
|
+
if (paramOpenIndex === -1) {
|
|
35210
|
+
continue;
|
|
35211
|
+
}
|
|
35212
|
+
let paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
|
|
35213
|
+
if (paramCloseIndex === -1) {
|
|
35214
|
+
let nextTagIndex = innerContent.length;
|
|
35215
|
+
for (const nextParam of validParams) {
|
|
35216
|
+
const nextOpenTag = `<${nextParam}>`;
|
|
35217
|
+
const nextIndex = innerContent.indexOf(nextOpenTag, paramOpenIndex + paramOpenTag.length);
|
|
35218
|
+
if (nextIndex !== -1 && nextIndex < nextTagIndex) {
|
|
35219
|
+
nextTagIndex = nextIndex;
|
|
35454
35220
|
}
|
|
35455
35221
|
}
|
|
35456
|
-
|
|
35457
|
-
|
|
35458
|
-
|
|
35459
|
-
|
|
35460
|
-
|
|
35461
|
-
|
|
35462
|
-
|
|
35463
|
-
|
|
35464
|
-
|
|
35465
|
-
|
|
35466
|
-
|
|
35467
|
-
|
|
35468
|
-
|
|
35469
|
-
|
|
35470
|
-
}
|
|
35471
|
-
if (currentIteration !== void 0 && (typeof currentIteration !== "number" || currentIteration < 0)) {
|
|
35472
|
-
throw new Error("currentIteration must be a non-negative number");
|
|
35473
|
-
}
|
|
35474
|
-
if (maxIterations !== void 0 && (typeof maxIterations !== "number" || maxIterations < 1)) {
|
|
35475
|
-
throw new Error("maxIterations must be a positive number");
|
|
35476
|
-
}
|
|
35477
|
-
if (parentSessionId !== void 0 && parentSessionId !== null && typeof parentSessionId !== "string") {
|
|
35478
|
-
throw new TypeError("parentSessionId must be a string, null, or undefined");
|
|
35479
|
-
}
|
|
35480
|
-
if (path9 !== void 0 && path9 !== null && typeof path9 !== "string") {
|
|
35481
|
-
throw new TypeError("path must be a string, null, or undefined");
|
|
35482
|
-
}
|
|
35483
|
-
if (provider !== void 0 && provider !== null && typeof provider !== "string") {
|
|
35484
|
-
throw new TypeError("provider must be a string, null, or undefined");
|
|
35485
|
-
}
|
|
35486
|
-
if (model !== void 0 && model !== null && typeof model !== "string") {
|
|
35487
|
-
throw new TypeError("model must be a string, null, or undefined");
|
|
35488
|
-
}
|
|
35489
|
-
const effectivePath = path9 || cwd || allowedFolders && allowedFolders[0];
|
|
35490
|
-
if (debug) {
|
|
35491
|
-
console.error(`Executing delegate with task: "${task.substring(0, 100)}${task.length > 100 ? "..." : ""}"`);
|
|
35492
|
-
if (parentSessionId) {
|
|
35493
|
-
console.error(`Parent session: ${parentSessionId}`);
|
|
35494
|
-
}
|
|
35495
|
-
if (effectivePath && effectivePath !== path9) {
|
|
35496
|
-
console.error(`Using inherited path: ${effectivePath}`);
|
|
35497
|
-
}
|
|
35498
|
-
}
|
|
35499
|
-
const result = await delegate({
|
|
35500
|
-
task,
|
|
35501
|
-
timeout,
|
|
35502
|
-
debug,
|
|
35503
|
-
currentIteration: currentIteration || 0,
|
|
35504
|
-
maxIterations: maxIterations || 30,
|
|
35505
|
-
parentSessionId,
|
|
35506
|
-
path: effectivePath,
|
|
35507
|
-
provider,
|
|
35508
|
-
model,
|
|
35509
|
-
tracer,
|
|
35510
|
-
enableBash,
|
|
35511
|
-
bashConfig
|
|
35512
|
-
});
|
|
35513
|
-
return result;
|
|
35222
|
+
paramCloseIndex = nextTagIndex;
|
|
35223
|
+
}
|
|
35224
|
+
let paramValue = innerContent.substring(
|
|
35225
|
+
paramOpenIndex + paramOpenTag.length,
|
|
35226
|
+
paramCloseIndex
|
|
35227
|
+
).trim();
|
|
35228
|
+
if (paramValue.toLowerCase() === "true") {
|
|
35229
|
+
paramValue = true;
|
|
35230
|
+
} else if (paramValue.toLowerCase() === "false") {
|
|
35231
|
+
paramValue = false;
|
|
35232
|
+
} else if (!isNaN(paramValue) && paramValue.trim() !== "") {
|
|
35233
|
+
const num = Number(paramValue);
|
|
35234
|
+
if (Number.isFinite(num)) {
|
|
35235
|
+
paramValue = num;
|
|
35514
35236
|
}
|
|
35515
|
-
}
|
|
35516
|
-
|
|
35237
|
+
}
|
|
35238
|
+
params[paramName] = paramValue;
|
|
35239
|
+
}
|
|
35240
|
+
if (toolName === "attempt_completion") {
|
|
35241
|
+
params["result"] = innerContent.trim();
|
|
35242
|
+
if (params.command) {
|
|
35243
|
+
delete params.command;
|
|
35244
|
+
}
|
|
35245
|
+
}
|
|
35246
|
+
return { toolName, params };
|
|
35517
35247
|
}
|
|
35518
|
-
|
|
35519
|
-
|
|
35520
|
-
|
|
35521
|
-
|
|
35522
|
-
|
|
35523
|
-
"src/agent/bashDefaults.js"() {
|
|
35524
|
-
"use strict";
|
|
35525
|
-
DEFAULT_ALLOW_PATTERNS = [
|
|
35526
|
-
// Basic navigation and listing
|
|
35527
|
-
"ls",
|
|
35528
|
-
"dir",
|
|
35529
|
-
"pwd",
|
|
35530
|
-
"cd",
|
|
35531
|
-
"cd:*",
|
|
35532
|
-
// File reading commands
|
|
35533
|
-
"cat",
|
|
35534
|
-
"cat:*",
|
|
35535
|
-
"head",
|
|
35536
|
-
"head:*",
|
|
35537
|
-
"tail",
|
|
35538
|
-
"tail:*",
|
|
35539
|
-
"less",
|
|
35540
|
-
"more",
|
|
35541
|
-
"view",
|
|
35542
|
-
// File information and metadata
|
|
35543
|
-
"file",
|
|
35544
|
-
"file:*",
|
|
35545
|
-
"stat",
|
|
35546
|
-
"stat:*",
|
|
35547
|
-
"wc",
|
|
35548
|
-
"wc:*",
|
|
35549
|
-
"du",
|
|
35550
|
-
"du:*",
|
|
35551
|
-
"df",
|
|
35552
|
-
"df:*",
|
|
35553
|
-
"realpath",
|
|
35554
|
-
"realpath:*",
|
|
35555
|
-
// Search and find commands (read-only) - find restricted to safe operations
|
|
35556
|
-
"find",
|
|
35557
|
-
"find:-name:*",
|
|
35558
|
-
"find:-type:*",
|
|
35559
|
-
"find:-size:*",
|
|
35560
|
-
"find:-mtime:*",
|
|
35561
|
-
"find:-newer:*",
|
|
35562
|
-
"find:-path:*",
|
|
35563
|
-
"find:-iname:*",
|
|
35564
|
-
"find:-maxdepth:*",
|
|
35565
|
-
"find:-mindepth:*",
|
|
35566
|
-
"find:-print",
|
|
35567
|
-
"grep",
|
|
35568
|
-
"grep:*",
|
|
35569
|
-
"egrep",
|
|
35570
|
-
"egrep:*",
|
|
35571
|
-
"fgrep",
|
|
35572
|
-
"fgrep:*",
|
|
35573
|
-
"rg",
|
|
35574
|
-
"rg:*",
|
|
35575
|
-
"ag",
|
|
35576
|
-
"ag:*",
|
|
35577
|
-
"ack",
|
|
35578
|
-
"ack:*",
|
|
35579
|
-
"which",
|
|
35580
|
-
"which:*",
|
|
35581
|
-
"whereis",
|
|
35582
|
-
"whereis:*",
|
|
35583
|
-
"locate",
|
|
35584
|
-
"locate:*",
|
|
35585
|
-
"type",
|
|
35586
|
-
"type:*",
|
|
35587
|
-
"command",
|
|
35588
|
-
"command:*",
|
|
35589
|
-
// Tree and structure visualization
|
|
35590
|
-
"tree",
|
|
35591
|
-
"tree:*",
|
|
35592
|
-
// Git read-only operations
|
|
35593
|
-
"git:status",
|
|
35594
|
-
"git:log",
|
|
35595
|
-
"git:log:*",
|
|
35596
|
-
"git:diff",
|
|
35597
|
-
"git:diff:*",
|
|
35598
|
-
"git:show",
|
|
35599
|
-
"git:show:*",
|
|
35600
|
-
"git:branch",
|
|
35601
|
-
"git:branch:*",
|
|
35602
|
-
"git:tag",
|
|
35603
|
-
"git:tag:*",
|
|
35604
|
-
"git:describe",
|
|
35605
|
-
"git:describe:*",
|
|
35606
|
-
"git:remote",
|
|
35607
|
-
"git:remote:*",
|
|
35608
|
-
"git:config:*",
|
|
35609
|
-
"git:blame",
|
|
35610
|
-
"git:blame:*",
|
|
35611
|
-
"git:shortlog",
|
|
35612
|
-
"git:reflog",
|
|
35613
|
-
"git:ls-files",
|
|
35614
|
-
"git:ls-tree",
|
|
35615
|
-
"git:rev-parse",
|
|
35616
|
-
"git:rev-list",
|
|
35617
|
-
"git:--version",
|
|
35618
|
-
"git:help",
|
|
35619
|
-
"git:help:*",
|
|
35620
|
-
// Package managers (information only)
|
|
35621
|
-
"npm:list",
|
|
35622
|
-
"npm:ls",
|
|
35623
|
-
"npm:view",
|
|
35624
|
-
"npm:info",
|
|
35625
|
-
"npm:show",
|
|
35626
|
-
"npm:outdated",
|
|
35627
|
-
"npm:audit",
|
|
35628
|
-
"npm:--version",
|
|
35629
|
-
"yarn:list",
|
|
35630
|
-
"yarn:info",
|
|
35631
|
-
"yarn:--version",
|
|
35632
|
-
"pnpm:list",
|
|
35633
|
-
"pnpm:--version",
|
|
35634
|
-
"pip:list",
|
|
35635
|
-
"pip:show",
|
|
35636
|
-
"pip:--version",
|
|
35637
|
-
"pip3:list",
|
|
35638
|
-
"pip3:show",
|
|
35639
|
-
"pip3:--version",
|
|
35640
|
-
"gem:list",
|
|
35641
|
-
"gem:--version",
|
|
35642
|
-
"bundle:list",
|
|
35643
|
-
"bundle:show",
|
|
35644
|
-
"bundle:--version",
|
|
35645
|
-
"composer:show",
|
|
35646
|
-
"composer:--version",
|
|
35647
|
-
// Language and runtime versions
|
|
35648
|
-
"node:--version",
|
|
35649
|
-
"node:-v",
|
|
35650
|
-
"python:--version",
|
|
35651
|
-
"python:-V",
|
|
35652
|
-
"python3:--version",
|
|
35653
|
-
"python3:-V",
|
|
35654
|
-
"ruby:--version",
|
|
35655
|
-
"ruby:-v",
|
|
35656
|
-
"go:version",
|
|
35657
|
-
"go:env",
|
|
35658
|
-
"go:list",
|
|
35659
|
-
"go:mod:graph",
|
|
35660
|
-
"rustc:--version",
|
|
35661
|
-
"cargo:--version",
|
|
35662
|
-
"cargo:tree",
|
|
35663
|
-
"cargo:metadata",
|
|
35664
|
-
"java:--version",
|
|
35665
|
-
"java:-version",
|
|
35666
|
-
"javac:--version",
|
|
35667
|
-
"mvn:--version",
|
|
35668
|
-
"gradle:--version",
|
|
35669
|
-
"php:--version",
|
|
35670
|
-
"dotnet:--version",
|
|
35671
|
-
"dotnet:list",
|
|
35672
|
-
// Database client versions (connection info only)
|
|
35673
|
-
"psql:--version",
|
|
35674
|
-
"mysql:--version",
|
|
35675
|
-
"redis-cli:--version",
|
|
35676
|
-
"mongo:--version",
|
|
35677
|
-
"sqlite3:--version",
|
|
35678
|
-
// System information
|
|
35679
|
-
"uname",
|
|
35680
|
-
"uname:*",
|
|
35681
|
-
"hostname",
|
|
35682
|
-
"whoami",
|
|
35683
|
-
"id",
|
|
35684
|
-
"groups",
|
|
35685
|
-
"date",
|
|
35686
|
-
"cal",
|
|
35687
|
-
"uptime",
|
|
35688
|
-
"w",
|
|
35689
|
-
"users",
|
|
35690
|
-
"sleep",
|
|
35691
|
-
"sleep:*",
|
|
35692
|
-
// Environment and shell
|
|
35693
|
-
"env",
|
|
35694
|
-
"printenv",
|
|
35695
|
-
"echo",
|
|
35696
|
-
"echo:*",
|
|
35697
|
-
"printf",
|
|
35698
|
-
"printf:*",
|
|
35699
|
-
"export",
|
|
35700
|
-
"export:*",
|
|
35701
|
-
"set",
|
|
35702
|
-
"unset",
|
|
35703
|
-
// Process information (read-only)
|
|
35704
|
-
"ps",
|
|
35705
|
-
"ps:*",
|
|
35706
|
-
"pgrep",
|
|
35707
|
-
"pgrep:*",
|
|
35708
|
-
"jobs",
|
|
35709
|
-
"top:-n:1",
|
|
35710
|
-
// Network information (read-only)
|
|
35711
|
-
"ifconfig",
|
|
35712
|
-
"ip:addr",
|
|
35713
|
-
"ip:link",
|
|
35714
|
-
"hostname:-I",
|
|
35715
|
-
"ping:-c:*",
|
|
35716
|
-
"traceroute",
|
|
35717
|
-
"nslookup",
|
|
35718
|
-
"dig",
|
|
35719
|
-
// Text processing and utilities (awk removed - too powerful)
|
|
35720
|
-
"sed:-n:*",
|
|
35721
|
-
"cut",
|
|
35722
|
-
"cut:*",
|
|
35723
|
-
"sort",
|
|
35724
|
-
"sort:*",
|
|
35725
|
-
"uniq",
|
|
35726
|
-
"uniq:*",
|
|
35727
|
-
"tr",
|
|
35728
|
-
"tr:*",
|
|
35729
|
-
"column",
|
|
35730
|
-
"column:*",
|
|
35731
|
-
"paste",
|
|
35732
|
-
"paste:*",
|
|
35733
|
-
"join",
|
|
35734
|
-
"join:*",
|
|
35735
|
-
"comm",
|
|
35736
|
-
"comm:*",
|
|
35737
|
-
"diff",
|
|
35738
|
-
"diff:*",
|
|
35739
|
-
"cmp",
|
|
35740
|
-
"cmp:*",
|
|
35741
|
-
"patch:--dry-run:*",
|
|
35742
|
-
// Hashing and encoding (read-only)
|
|
35743
|
-
"md5sum",
|
|
35744
|
-
"md5sum:*",
|
|
35745
|
-
"sha1sum",
|
|
35746
|
-
"sha1sum:*",
|
|
35747
|
-
"sha256sum",
|
|
35748
|
-
"sha256sum:*",
|
|
35749
|
-
"base64",
|
|
35750
|
-
"base64:-d",
|
|
35751
|
-
"od",
|
|
35752
|
-
"od:*",
|
|
35753
|
-
"hexdump",
|
|
35754
|
-
"hexdump:*",
|
|
35755
|
-
// Archive and compression (list/view only)
|
|
35756
|
-
"tar:-tf:*",
|
|
35757
|
-
"tar:-tzf:*",
|
|
35758
|
-
"unzip:-l:*",
|
|
35759
|
-
"zip:-l:*",
|
|
35760
|
-
"gzip:-l:*",
|
|
35761
|
-
"gunzip:-l:*",
|
|
35762
|
-
// Help and documentation
|
|
35763
|
-
"man",
|
|
35764
|
-
"man:*",
|
|
35765
|
-
"--help",
|
|
35766
|
-
"help",
|
|
35767
|
-
"info",
|
|
35768
|
-
"info:*",
|
|
35769
|
-
"whatis",
|
|
35770
|
-
"whatis:*",
|
|
35771
|
-
"apropos",
|
|
35772
|
-
"apropos:*",
|
|
35773
|
-
// Make (dry run and info)
|
|
35774
|
-
"make:-n",
|
|
35775
|
-
"make:--dry-run",
|
|
35776
|
-
"make:-p",
|
|
35777
|
-
"make:--print-data-base",
|
|
35778
|
-
// Docker (read-only operations)
|
|
35779
|
-
"docker:ps",
|
|
35780
|
-
"docker:images",
|
|
35781
|
-
"docker:version",
|
|
35782
|
-
"docker:info",
|
|
35783
|
-
"docker:logs:*",
|
|
35784
|
-
"docker:inspect:*",
|
|
35785
|
-
// Test runners (list/info only)
|
|
35786
|
-
"jest:--listTests",
|
|
35787
|
-
"mocha:--help",
|
|
35788
|
-
"pytest:--collect-only"
|
|
35789
|
-
];
|
|
35790
|
-
DEFAULT_DENY_PATTERNS = [
|
|
35791
|
-
// Dangerous file operations
|
|
35792
|
-
"rm:-rf",
|
|
35793
|
-
"rm:-f:/",
|
|
35794
|
-
"rm:/",
|
|
35795
|
-
"rm:-rf:*",
|
|
35796
|
-
"rmdir",
|
|
35797
|
-
"chmod:777",
|
|
35798
|
-
"chmod:-R:777",
|
|
35799
|
-
"chown",
|
|
35800
|
-
"chgrp",
|
|
35801
|
-
"dd",
|
|
35802
|
-
"dd:*",
|
|
35803
|
-
"shred",
|
|
35804
|
-
"shred:*",
|
|
35805
|
-
// Dangerous find operations that can execute arbitrary commands
|
|
35806
|
-
"find:-exec:*",
|
|
35807
|
-
"find:*:-exec:*",
|
|
35808
|
-
"find:-execdir:*",
|
|
35809
|
-
"find:*:-execdir:*",
|
|
35810
|
-
"find:-ok:*",
|
|
35811
|
-
"find:*:-ok:*",
|
|
35812
|
-
"find:-okdir:*",
|
|
35813
|
-
"find:*:-okdir:*",
|
|
35814
|
-
// Powerful scripting tools that can execute arbitrary commands
|
|
35815
|
-
"awk",
|
|
35816
|
-
"awk:*",
|
|
35817
|
-
"perl",
|
|
35818
|
-
"perl:*",
|
|
35819
|
-
"python:-c:*",
|
|
35820
|
-
"node:-e:*",
|
|
35821
|
-
// System administration and modification
|
|
35822
|
-
"sudo:*",
|
|
35823
|
-
"su",
|
|
35824
|
-
"su:*",
|
|
35825
|
-
"passwd",
|
|
35826
|
-
"adduser",
|
|
35827
|
-
"useradd",
|
|
35828
|
-
"userdel",
|
|
35829
|
-
"usermod",
|
|
35830
|
-
"groupadd",
|
|
35831
|
-
"groupdel",
|
|
35832
|
-
"visudo",
|
|
35833
|
-
// Package installation and removal
|
|
35834
|
-
"npm:install",
|
|
35835
|
-
"npm:i",
|
|
35836
|
-
"npm:uninstall",
|
|
35837
|
-
"npm:publish",
|
|
35838
|
-
"npm:unpublish",
|
|
35839
|
-
"npm:link",
|
|
35840
|
-
"npm:update",
|
|
35841
|
-
"yarn:install",
|
|
35842
|
-
"yarn:add",
|
|
35843
|
-
"yarn:remove",
|
|
35844
|
-
"yarn:upgrade",
|
|
35845
|
-
"pnpm:install",
|
|
35846
|
-
"pnpm:add",
|
|
35847
|
-
"pnpm:remove",
|
|
35848
|
-
"pip:install",
|
|
35849
|
-
"pip:uninstall",
|
|
35850
|
-
"pip:upgrade",
|
|
35851
|
-
"pip3:install",
|
|
35852
|
-
"pip3:uninstall",
|
|
35853
|
-
"pip3:upgrade",
|
|
35854
|
-
"gem:install",
|
|
35855
|
-
"gem:uninstall",
|
|
35856
|
-
"gem:update",
|
|
35857
|
-
"bundle:install",
|
|
35858
|
-
"bundle:update",
|
|
35859
|
-
"composer:install",
|
|
35860
|
-
"composer:update",
|
|
35861
|
-
"composer:remove",
|
|
35862
|
-
"apt:*",
|
|
35863
|
-
"apt-get:*",
|
|
35864
|
-
"yum:*",
|
|
35865
|
-
"dnf:*",
|
|
35866
|
-
"zypper:*",
|
|
35867
|
-
"brew:install",
|
|
35868
|
-
"brew:uninstall",
|
|
35869
|
-
"brew:upgrade",
|
|
35870
|
-
"conda:install",
|
|
35871
|
-
"conda:remove",
|
|
35872
|
-
"conda:update",
|
|
35873
|
-
// Service and system control
|
|
35874
|
-
"systemctl:*",
|
|
35875
|
-
"service:*",
|
|
35876
|
-
"chkconfig:*",
|
|
35877
|
-
"initctl:*",
|
|
35878
|
-
"upstart:*",
|
|
35879
|
-
// Network operations that could be dangerous
|
|
35880
|
-
"curl:-d:*",
|
|
35881
|
-
"curl:--data:*",
|
|
35882
|
-
"curl:-X:POST:*",
|
|
35883
|
-
"curl:-X:PUT:*",
|
|
35884
|
-
"wget:-O:/",
|
|
35885
|
-
"wget:--post-data:*",
|
|
35886
|
-
"ssh",
|
|
35887
|
-
"ssh:*",
|
|
35888
|
-
"scp",
|
|
35889
|
-
"scp:*",
|
|
35890
|
-
"sftp",
|
|
35891
|
-
"sftp:*",
|
|
35892
|
-
"rsync:*",
|
|
35893
|
-
"nc",
|
|
35894
|
-
"nc:*",
|
|
35895
|
-
"netcat",
|
|
35896
|
-
"netcat:*",
|
|
35897
|
-
"telnet",
|
|
35898
|
-
"telnet:*",
|
|
35899
|
-
"ftp",
|
|
35900
|
-
"ftp:*",
|
|
35901
|
-
// Process control and termination
|
|
35902
|
-
"kill",
|
|
35903
|
-
"kill:*",
|
|
35904
|
-
"killall",
|
|
35905
|
-
"killall:*",
|
|
35906
|
-
"pkill",
|
|
35907
|
-
"pkill:*",
|
|
35908
|
-
"nohup:*",
|
|
35909
|
-
"disown:*",
|
|
35910
|
-
// System control and shutdown
|
|
35911
|
-
"shutdown",
|
|
35912
|
-
"shutdown:*",
|
|
35913
|
-
"reboot",
|
|
35914
|
-
"halt",
|
|
35915
|
-
"poweroff",
|
|
35916
|
-
"init",
|
|
35917
|
-
"telinit",
|
|
35918
|
-
// Kernel and module operations
|
|
35919
|
-
"insmod",
|
|
35920
|
-
"insmod:*",
|
|
35921
|
-
"rmmod",
|
|
35922
|
-
"rmmod:*",
|
|
35923
|
-
"modprobe",
|
|
35924
|
-
"modprobe:*",
|
|
35925
|
-
"sysctl:-w:*",
|
|
35926
|
-
// Dangerous git operations
|
|
35927
|
-
"git:push",
|
|
35928
|
-
"git:push:*",
|
|
35929
|
-
"git:force",
|
|
35930
|
-
"git:reset:--hard:*",
|
|
35931
|
-
"git:clean:-fd",
|
|
35932
|
-
"git:rm:*",
|
|
35933
|
-
"git:commit",
|
|
35934
|
-
"git:merge",
|
|
35935
|
-
"git:rebase",
|
|
35936
|
-
"git:cherry-pick",
|
|
35937
|
-
"git:stash:drop",
|
|
35938
|
-
// File system mounting and partitioning
|
|
35939
|
-
"mount",
|
|
35940
|
-
"mount:*",
|
|
35941
|
-
"umount",
|
|
35942
|
-
"umount:*",
|
|
35943
|
-
"fdisk",
|
|
35944
|
-
"fdisk:*",
|
|
35945
|
-
"parted",
|
|
35946
|
-
"parted:*",
|
|
35947
|
-
"mkfs",
|
|
35948
|
-
"mkfs:*",
|
|
35949
|
-
"fsck",
|
|
35950
|
-
"fsck:*",
|
|
35951
|
-
// Cron and scheduling
|
|
35952
|
-
"crontab",
|
|
35953
|
-
"crontab:*",
|
|
35954
|
-
"at",
|
|
35955
|
-
"at:*",
|
|
35956
|
-
"batch",
|
|
35957
|
-
"batch:*",
|
|
35958
|
-
// Compression with potential overwrite
|
|
35959
|
-
"tar:-xf:*",
|
|
35960
|
-
"unzip",
|
|
35961
|
-
"unzip:*",
|
|
35962
|
-
"gzip:*",
|
|
35963
|
-
"gunzip:*",
|
|
35964
|
-
// Build and compilation that might modify files
|
|
35965
|
-
"make",
|
|
35966
|
-
"make:install",
|
|
35967
|
-
"make:clean",
|
|
35968
|
-
"cargo:build",
|
|
35969
|
-
"cargo:install",
|
|
35970
|
-
"npm:run:build",
|
|
35971
|
-
"yarn:build",
|
|
35972
|
-
"mvn:install",
|
|
35973
|
-
"gradle:build",
|
|
35974
|
-
// Docker operations that could modify state
|
|
35975
|
-
"docker:run",
|
|
35976
|
-
"docker:run:*",
|
|
35977
|
-
"docker:exec",
|
|
35978
|
-
"docker:exec:*",
|
|
35979
|
-
"docker:build",
|
|
35980
|
-
"docker:build:*",
|
|
35981
|
-
"docker:pull",
|
|
35982
|
-
"docker:push",
|
|
35983
|
-
"docker:rm",
|
|
35984
|
-
"docker:rmi",
|
|
35985
|
-
"docker:stop",
|
|
35986
|
-
"docker:start",
|
|
35987
|
-
// Database operations
|
|
35988
|
-
"mysql:-e:DROP",
|
|
35989
|
-
"psql:-c:DROP",
|
|
35990
|
-
"redis-cli:FLUSHALL",
|
|
35991
|
-
"mongo:--eval:*",
|
|
35992
|
-
// Text editors that could modify files
|
|
35993
|
-
"vi",
|
|
35994
|
-
"vi:*",
|
|
35995
|
-
"vim",
|
|
35996
|
-
"vim:*",
|
|
35997
|
-
"nano",
|
|
35998
|
-
"nano:*",
|
|
35999
|
-
"emacs",
|
|
36000
|
-
"emacs:*",
|
|
36001
|
-
"sed:-i:*",
|
|
36002
|
-
"perl:-i:*",
|
|
36003
|
-
// Potentially dangerous utilities
|
|
36004
|
-
"eval",
|
|
36005
|
-
"eval:*",
|
|
36006
|
-
"exec",
|
|
36007
|
-
"exec:*",
|
|
36008
|
-
"source",
|
|
36009
|
-
"source:*",
|
|
36010
|
-
"bash:-c:*",
|
|
36011
|
-
"sh:-c:*",
|
|
36012
|
-
"zsh:-c:*"
|
|
36013
|
-
];
|
|
36014
|
-
}
|
|
36015
|
-
});
|
|
36016
|
-
|
|
36017
|
-
// src/agent/bashCommandUtils.js
|
|
36018
|
-
function parseSimpleCommand(command) {
|
|
36019
|
-
if (!command || typeof command !== "string") {
|
|
36020
|
-
return {
|
|
36021
|
-
success: false,
|
|
36022
|
-
error: "Command must be a non-empty string",
|
|
36023
|
-
command: null,
|
|
36024
|
-
args: [],
|
|
36025
|
-
isComplex: false
|
|
36026
|
-
};
|
|
36027
|
-
}
|
|
36028
|
-
const trimmed = command.trim();
|
|
36029
|
-
if (!trimmed) {
|
|
36030
|
-
return {
|
|
36031
|
-
success: false,
|
|
36032
|
-
error: "Command cannot be empty",
|
|
36033
|
-
command: null,
|
|
36034
|
-
args: [],
|
|
36035
|
-
isComplex: false
|
|
36036
|
-
};
|
|
36037
|
-
}
|
|
36038
|
-
const complexPatterns = [
|
|
36039
|
-
/\|/,
|
|
36040
|
-
// Pipes
|
|
36041
|
-
/&&/,
|
|
36042
|
-
// Logical AND
|
|
36043
|
-
/\|\|/,
|
|
36044
|
-
// Logical OR
|
|
36045
|
-
/(?<!\\);/,
|
|
36046
|
-
// Command separator (but not escaped \;)
|
|
36047
|
-
/&$/,
|
|
36048
|
-
// Background execution
|
|
36049
|
-
/\$\(/,
|
|
36050
|
-
// Command substitution $()
|
|
36051
|
-
/`/,
|
|
36052
|
-
// Command substitution ``
|
|
36053
|
-
/>/,
|
|
36054
|
-
// Redirection >
|
|
36055
|
-
/</,
|
|
36056
|
-
// Redirection <
|
|
36057
|
-
/\*\*/,
|
|
36058
|
-
// Glob patterns (potentially dangerous)
|
|
36059
|
-
/^\s*\{.*,.*\}|\{.*\.\.\.*\}/
|
|
36060
|
-
// Brace expansion like {a,b} or {1..10} (but not find {} placeholders)
|
|
36061
|
-
];
|
|
36062
|
-
for (const pattern of complexPatterns) {
|
|
36063
|
-
if (pattern.test(trimmed)) {
|
|
36064
|
-
return {
|
|
36065
|
-
success: false,
|
|
36066
|
-
error: "Complex shell commands with pipes, operators, or redirections are not supported for security reasons",
|
|
36067
|
-
command: null,
|
|
36068
|
-
args: [],
|
|
36069
|
-
isComplex: true,
|
|
36070
|
-
detected: pattern.toString()
|
|
36071
|
-
};
|
|
36072
|
-
}
|
|
36073
|
-
}
|
|
36074
|
-
const args = [];
|
|
36075
|
-
let current = "";
|
|
36076
|
-
let inQuotes = false;
|
|
36077
|
-
let quoteChar = "";
|
|
36078
|
-
let escaped = false;
|
|
36079
|
-
for (let i4 = 0; i4 < trimmed.length; i4++) {
|
|
36080
|
-
const char = trimmed[i4];
|
|
36081
|
-
const nextChar = i4 + 1 < trimmed.length ? trimmed[i4 + 1] : "";
|
|
36082
|
-
if (escaped) {
|
|
36083
|
-
current += char;
|
|
36084
|
-
escaped = false;
|
|
36085
|
-
continue;
|
|
36086
|
-
}
|
|
36087
|
-
if (char === "\\" && !inQuotes) {
|
|
36088
|
-
escaped = true;
|
|
36089
|
-
continue;
|
|
36090
|
-
}
|
|
36091
|
-
if (!inQuotes && (char === '"' || char === "'")) {
|
|
36092
|
-
inQuotes = true;
|
|
36093
|
-
quoteChar = char;
|
|
36094
|
-
} else if (inQuotes && char === quoteChar) {
|
|
36095
|
-
inQuotes = false;
|
|
36096
|
-
quoteChar = "";
|
|
36097
|
-
} else if (!inQuotes && char === " ") {
|
|
36098
|
-
if (current.trim()) {
|
|
36099
|
-
args.push(current.trim());
|
|
36100
|
-
current = "";
|
|
36101
|
-
}
|
|
36102
|
-
} else {
|
|
36103
|
-
current += char;
|
|
36104
|
-
}
|
|
36105
|
-
}
|
|
36106
|
-
if (current.trim()) {
|
|
36107
|
-
args.push(current.trim());
|
|
35248
|
+
return null;
|
|
35249
|
+
}
|
|
35250
|
+
function createMessagePreview(message, charsPerSide = 200) {
|
|
35251
|
+
if (message === null || message === void 0) {
|
|
35252
|
+
return "null/undefined";
|
|
36108
35253
|
}
|
|
36109
|
-
if (
|
|
36110
|
-
return
|
|
36111
|
-
success: false,
|
|
36112
|
-
error: `Unclosed quote in command: ${quoteChar}`,
|
|
36113
|
-
command: null,
|
|
36114
|
-
args: [],
|
|
36115
|
-
isComplex: false
|
|
36116
|
-
};
|
|
35254
|
+
if (typeof message !== "string") {
|
|
35255
|
+
return "null/undefined";
|
|
36117
35256
|
}
|
|
36118
|
-
|
|
36119
|
-
|
|
36120
|
-
|
|
36121
|
-
error: "No command found after parsing",
|
|
36122
|
-
command: null,
|
|
36123
|
-
args: [],
|
|
36124
|
-
isComplex: false
|
|
36125
|
-
};
|
|
35257
|
+
const totalChars = charsPerSide * 2;
|
|
35258
|
+
if (message.length <= totalChars) {
|
|
35259
|
+
return message;
|
|
36126
35260
|
}
|
|
36127
|
-
const
|
|
36128
|
-
|
|
36129
|
-
|
|
36130
|
-
error: null,
|
|
36131
|
-
command: baseCommand,
|
|
36132
|
-
args: commandArgs,
|
|
36133
|
-
fullArgs: args,
|
|
36134
|
-
isComplex: false,
|
|
36135
|
-
original: command
|
|
36136
|
-
};
|
|
36137
|
-
}
|
|
36138
|
-
function isComplexCommand(command) {
|
|
36139
|
-
const result = parseSimpleCommand(command);
|
|
36140
|
-
return result.isComplex;
|
|
35261
|
+
const start = message.substring(0, charsPerSide);
|
|
35262
|
+
const end = message.substring(message.length - charsPerSide);
|
|
35263
|
+
return `${start}...${end}`;
|
|
36141
35264
|
}
|
|
36142
|
-
function
|
|
36143
|
-
if (!
|
|
36144
|
-
|
|
36145
|
-
|
|
36146
|
-
|
|
36147
|
-
/&&/,
|
|
36148
|
-
// Logical AND
|
|
36149
|
-
/\|\|/,
|
|
36150
|
-
// Logical OR
|
|
36151
|
-
/;/,
|
|
36152
|
-
// Command separator
|
|
36153
|
-
/&$/,
|
|
36154
|
-
// Background execution
|
|
36155
|
-
/\$\(/,
|
|
36156
|
-
// Command substitution $()
|
|
36157
|
-
/`/,
|
|
36158
|
-
// Command substitution ``
|
|
36159
|
-
/>/,
|
|
36160
|
-
// Redirection >
|
|
36161
|
-
/</
|
|
36162
|
-
// Redirection <
|
|
36163
|
-
];
|
|
36164
|
-
return operatorPatterns.some((p4) => p4.test(pattern));
|
|
35265
|
+
function parseTargets(targets) {
|
|
35266
|
+
if (!targets || typeof targets !== "string") {
|
|
35267
|
+
return [];
|
|
35268
|
+
}
|
|
35269
|
+
return targets.split(/[\s,]+/).filter((f4) => f4.length > 0);
|
|
36165
35270
|
}
|
|
36166
|
-
function
|
|
36167
|
-
|
|
36168
|
-
|
|
36169
|
-
return
|
|
35271
|
+
function parseAndResolvePaths(pathStr, cwd) {
|
|
35272
|
+
if (!pathStr) return [];
|
|
35273
|
+
const paths = pathStr.split(",").map((p4) => p4.trim()).filter((p4) => p4.length > 0);
|
|
35274
|
+
return paths.map((p4) => {
|
|
35275
|
+
if ((0, import_path6.isAbsolute)(p4)) {
|
|
35276
|
+
return p4;
|
|
35277
|
+
}
|
|
35278
|
+
return cwd ? (0, import_path6.resolve)(cwd, p4) : p4;
|
|
35279
|
+
});
|
|
36170
35280
|
}
|
|
36171
|
-
function
|
|
36172
|
-
|
|
36173
|
-
const
|
|
36174
|
-
const
|
|
36175
|
-
|
|
36176
|
-
|
|
36177
|
-
|
|
36178
|
-
|
|
36179
|
-
|
|
35281
|
+
function resolveTargetPath(target, cwd) {
|
|
35282
|
+
const searchStart = target.length > 2 && target[1] === ":" && /[a-zA-Z]/.test(target[0]) ? 2 : 0;
|
|
35283
|
+
const colonIdx = target.indexOf(":", searchStart);
|
|
35284
|
+
const hashIdx = target.indexOf("#");
|
|
35285
|
+
let filePart, suffix;
|
|
35286
|
+
if (colonIdx !== -1 && (hashIdx === -1 || colonIdx < hashIdx)) {
|
|
35287
|
+
filePart = target.substring(0, colonIdx);
|
|
35288
|
+
suffix = target.substring(colonIdx);
|
|
35289
|
+
} else if (hashIdx !== -1) {
|
|
35290
|
+
filePart = target.substring(0, hashIdx);
|
|
35291
|
+
suffix = target.substring(hashIdx);
|
|
35292
|
+
} else {
|
|
35293
|
+
filePart = target;
|
|
35294
|
+
suffix = "";
|
|
36180
35295
|
}
|
|
35296
|
+
if (!(0, import_path6.isAbsolute)(filePart) && cwd) {
|
|
35297
|
+
filePart = (0, import_path6.resolve)(cwd, filePart);
|
|
35298
|
+
}
|
|
35299
|
+
return filePart + suffix;
|
|
36181
35300
|
}
|
|
36182
|
-
|
|
36183
|
-
|
|
36184
|
-
|
|
36185
|
-
|
|
36186
|
-
|
|
36187
|
-
|
|
36188
|
-
|
|
36189
|
-
|
|
35301
|
+
var import_path6, searchSchema, querySchema, extractSchema, delegateSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, bashToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, DEFAULT_VALID_TOOLS;
|
|
35302
|
+
var init_common2 = __esm({
|
|
35303
|
+
"src/tools/common.js"() {
|
|
35304
|
+
"use strict";
|
|
35305
|
+
init_zod();
|
|
35306
|
+
import_path6 = require("path");
|
|
35307
|
+
init_edit();
|
|
35308
|
+
searchSchema = external_exports.object({
|
|
35309
|
+
query: external_exports.string().describe("Search query with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation."),
|
|
35310
|
+
path: external_exports.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.')
|
|
35311
|
+
});
|
|
35312
|
+
querySchema = external_exports.object({
|
|
35313
|
+
pattern: external_exports.string().describe("AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc."),
|
|
35314
|
+
path: external_exports.string().optional().default(".").describe("Path to search in"),
|
|
35315
|
+
language: external_exports.string().optional().default("rust").describe("Programming language to use for parsing"),
|
|
35316
|
+
allow_tests: external_exports.boolean().optional().default(true).describe("Allow test files in search results")
|
|
35317
|
+
});
|
|
35318
|
+
extractSchema = external_exports.object({
|
|
35319
|
+
targets: external_exports.string().optional().describe('File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (symbol). Multiple targets separated by spaces.'),
|
|
35320
|
+
input_content: external_exports.string().optional().describe("Text content to extract file paths from (alternative to targets)"),
|
|
35321
|
+
allow_tests: external_exports.boolean().optional().default(true).describe("Include test files in extraction results")
|
|
35322
|
+
});
|
|
35323
|
+
delegateSchema = external_exports.object({
|
|
35324
|
+
task: external_exports.string().describe("The task to delegate to a subagent. Be specific about what needs to be accomplished.")
|
|
35325
|
+
});
|
|
35326
|
+
bashSchema = external_exports.object({
|
|
35327
|
+
command: external_exports.string().describe("The bash command to execute"),
|
|
35328
|
+
workingDirectory: external_exports.string().optional().describe("Directory to execute the command in (optional)"),
|
|
35329
|
+
timeout: external_exports.number().optional().describe("Command timeout in milliseconds (optional)"),
|
|
35330
|
+
env: external_exports.record(external_exports.string()).optional().describe("Additional environment variables (optional)")
|
|
35331
|
+
});
|
|
35332
|
+
attemptCompletionSchema = {
|
|
35333
|
+
// Custom validation that requires result parameter but allows direct XML response
|
|
35334
|
+
safeParse: (params) => {
|
|
35335
|
+
if (!params || typeof params !== "object") {
|
|
35336
|
+
return {
|
|
35337
|
+
success: false,
|
|
35338
|
+
error: {
|
|
35339
|
+
issues: [{
|
|
35340
|
+
code: "invalid_type",
|
|
35341
|
+
expected: "object",
|
|
35342
|
+
received: typeof params,
|
|
35343
|
+
path: [],
|
|
35344
|
+
message: "Expected object"
|
|
35345
|
+
}]
|
|
35346
|
+
}
|
|
35347
|
+
};
|
|
35348
|
+
}
|
|
35349
|
+
if (!("result" in params)) {
|
|
35350
|
+
return {
|
|
35351
|
+
success: false,
|
|
35352
|
+
error: {
|
|
35353
|
+
issues: [{
|
|
35354
|
+
code: "invalid_type",
|
|
35355
|
+
expected: "string",
|
|
35356
|
+
received: "undefined",
|
|
35357
|
+
path: ["result"],
|
|
35358
|
+
message: "Required"
|
|
35359
|
+
}]
|
|
35360
|
+
}
|
|
35361
|
+
};
|
|
35362
|
+
}
|
|
35363
|
+
if (typeof params.result !== "string") {
|
|
35364
|
+
return {
|
|
35365
|
+
success: false,
|
|
35366
|
+
error: {
|
|
35367
|
+
issues: [{
|
|
35368
|
+
code: "invalid_type",
|
|
35369
|
+
expected: "string",
|
|
35370
|
+
received: typeof params.result,
|
|
35371
|
+
path: ["result"],
|
|
35372
|
+
message: "Expected string"
|
|
35373
|
+
}]
|
|
35374
|
+
}
|
|
35375
|
+
};
|
|
35376
|
+
}
|
|
35377
|
+
const filteredData = { result: params.result };
|
|
35378
|
+
return {
|
|
35379
|
+
success: true,
|
|
35380
|
+
data: filteredData
|
|
35381
|
+
};
|
|
35382
|
+
}
|
|
36190
35383
|
};
|
|
36191
|
-
|
|
36192
|
-
|
|
36193
|
-
|
|
36194
|
-
|
|
36195
|
-
|
|
36196
|
-
|
|
36197
|
-
|
|
36198
|
-
|
|
36199
|
-
|
|
36200
|
-
|
|
36201
|
-
|
|
36202
|
-
|
|
36203
|
-
|
|
36204
|
-
|
|
36205
|
-
|
|
36206
|
-
|
|
36207
|
-
|
|
36208
|
-
|
|
35384
|
+
searchToolDefinition = `
|
|
35385
|
+
## search
|
|
35386
|
+
Description: Search code in the repository using Elasticsearch query syntax (except field based queries, e.g. "filename:..." NOT supported).
|
|
35387
|
+
|
|
35388
|
+
You need to focus on main keywords when constructing the query, and always use elastic search syntax like OR AND and brackets to group keywords.
|
|
35389
|
+
|
|
35390
|
+
**Session Management & Caching:**
|
|
35391
|
+
- Ensure not to re-read the same symbols twice - reuse context from previous tool calls
|
|
35392
|
+
- Probe returns a session ID on first run - reuse it for subsequent calls to avoid redundant searches
|
|
35393
|
+
- Once data is returned, it's cached and won't return on next runs (this is expected behavior)
|
|
35394
|
+
|
|
35395
|
+
Parameters:
|
|
35396
|
+
- query: (required) Search query with Elasticsearch syntax. Use quotes for exact matches ("functionName"), AND/OR for boolean logic, - for negation, + for important terms.
|
|
35397
|
+
- path: (optional, default: '.') Path to search in. All dependencies located in /dep folder, under language sub folders, like this: "/dep/go/github.com/owner/repo", "/dep/js/package_name", or "/dep/rust/cargo_name" etc.
|
|
35398
|
+
|
|
35399
|
+
**Workflow:** Always start with search, then use extract for detailed context when needed.
|
|
35400
|
+
|
|
35401
|
+
Usage Example:
|
|
35402
|
+
|
|
35403
|
+
<examples>
|
|
35404
|
+
|
|
35405
|
+
User: Where is the login logic?
|
|
35406
|
+
Assistant workflow:
|
|
35407
|
+
1. <search>
|
|
35408
|
+
<query>login AND auth AND token</query>
|
|
35409
|
+
<path>.</path>
|
|
35410
|
+
</search>
|
|
35411
|
+
2. Now lets look closer: <extract>
|
|
35412
|
+
<targets>session.rs#AuthService.login auth.rs:2-100</targets>
|
|
35413
|
+
</extract>
|
|
35414
|
+
|
|
35415
|
+
User: How to calculate the total amount in the payments module?
|
|
35416
|
+
<search>
|
|
35417
|
+
<query>calculate AND payment</query>
|
|
35418
|
+
<path>src/utils</path>
|
|
35419
|
+
</search>
|
|
35420
|
+
|
|
35421
|
+
User: How do the user authentication and authorization work?
|
|
35422
|
+
<search>
|
|
35423
|
+
<query>+user AND (authentication OR authorization OR authz)</query>
|
|
35424
|
+
<path>.</path>
|
|
35425
|
+
</search>
|
|
35426
|
+
|
|
35427
|
+
User: Find all react imports in the project.
|
|
35428
|
+
<search>
|
|
35429
|
+
<query>"import" AND "react"</query>
|
|
35430
|
+
<path>.</path>
|
|
35431
|
+
</search>
|
|
35432
|
+
|
|
35433
|
+
User: Find how decompound library works?
|
|
35434
|
+
<search>
|
|
35435
|
+
<query>decompound</query>
|
|
35436
|
+
<path>/dep/rust/decompound</path>
|
|
35437
|
+
</search>
|
|
35438
|
+
|
|
35439
|
+
</examples>
|
|
35440
|
+
`;
|
|
35441
|
+
queryToolDefinition = `
|
|
35442
|
+
## query
|
|
35443
|
+
Description: Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.
|
|
35444
|
+
Parameters:
|
|
35445
|
+
- pattern: (required) AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc.
|
|
35446
|
+
- path: (optional, default: '.') Path to search in.
|
|
35447
|
+
- language: (optional, default: 'rust') Programming language to use for parsing.
|
|
35448
|
+
- allow_tests: (optional, default: true) Allow test files in search results (true/false).
|
|
35449
|
+
Usage Example:
|
|
35450
|
+
|
|
35451
|
+
<examples>
|
|
35452
|
+
|
|
35453
|
+
<query>
|
|
35454
|
+
<pattern>function $FUNC($$$PARAMS) { $$$BODY }</pattern>
|
|
35455
|
+
<path>src/parser</path>
|
|
35456
|
+
<language>js</language>
|
|
35457
|
+
</query>
|
|
35458
|
+
|
|
35459
|
+
</examples>
|
|
35460
|
+
`;
|
|
35461
|
+
extractToolDefinition = `
|
|
35462
|
+
## extract
|
|
35463
|
+
Description: Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. It can be used to read full files as well.
|
|
35464
|
+
Full file extraction should be the LAST RESORT! Always prefer search.
|
|
35465
|
+
|
|
35466
|
+
**Multiple Extraction:** You can extract multiple symbols/files in one call by providing multiple file paths separated by spaces.
|
|
35467
|
+
|
|
35468
|
+
**Session Awareness:** Reuse context from previous tool calls. Don't re-extract the same symbols you already have.
|
|
35469
|
+
|
|
35470
|
+
Parameters:
|
|
35471
|
+
- targets: (required) File paths or symbols to extract from. Formats: "file.js" (whole file), "file.js:42" (code block at line 42), "file.js:10-20" (lines 10-20), "file.js#funcName" (specific symbol). Multiple targets separated by spaces.
|
|
35472
|
+
- input_content: (optional) Text content to extract file paths from (alternative to targets for processing diffs/logs).
|
|
35473
|
+
- allow_tests: (optional, default: true) Include test files in extraction results.
|
|
35474
|
+
|
|
35475
|
+
Usage Example:
|
|
35476
|
+
|
|
35477
|
+
<examples>
|
|
35478
|
+
|
|
35479
|
+
User: Where is the login logic? (After search found relevant files)
|
|
35480
|
+
<extract>
|
|
35481
|
+
<targets>session.rs#AuthService.login auth.rs:2-100 config.rs#DatabaseConfig</targets>
|
|
35482
|
+
</extract>
|
|
35483
|
+
|
|
35484
|
+
User: How does error handling work? (After search identified files)
|
|
35485
|
+
<extract>
|
|
35486
|
+
<targets>error.rs#ErrorType utils.rs#handle_error src/main.rs:50-80</targets>
|
|
35487
|
+
</extract>
|
|
35488
|
+
|
|
35489
|
+
User: How RankManager works
|
|
35490
|
+
<extract>
|
|
35491
|
+
<targets>src/search/ranking.rs#RankManager</targets>
|
|
35492
|
+
</extract>
|
|
35493
|
+
|
|
35494
|
+
User: Lets read the whole file
|
|
35495
|
+
<extract>
|
|
35496
|
+
<targets>src/search/ranking.rs</targets>
|
|
35497
|
+
</extract>
|
|
35498
|
+
|
|
35499
|
+
User: Read the first 10 lines of the file
|
|
35500
|
+
<extract>
|
|
35501
|
+
<targets>src/search/ranking.rs:1-10</targets>
|
|
35502
|
+
</extract>
|
|
35503
|
+
|
|
35504
|
+
User: Read file inside the dependency
|
|
35505
|
+
<extract>
|
|
35506
|
+
<targets>/dep/go/github.com/gorilla/mux/router.go</targets>
|
|
35507
|
+
</extract>
|
|
35508
|
+
|
|
35509
|
+
</examples>
|
|
35510
|
+
`;
|
|
35511
|
+
delegateToolDefinition = `
|
|
35512
|
+
## delegate
|
|
35513
|
+
Description: Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Use this when you recognize that a user's request involves multiple large, distinct components that would benefit from parallel processing or specialized focus. The AI agent should automatically identify opportunities for task separation and use delegation without explicit user instruction.
|
|
35514
|
+
|
|
35515
|
+
Parameters:
|
|
35516
|
+
- task: (required) A complete, self-contained task that can be executed independently by a subagent. Should be specific and focused on one area of expertise.
|
|
35517
|
+
|
|
35518
|
+
Usage Pattern:
|
|
35519
|
+
When the AI agent encounters complex multi-part requests, it should automatically break them down and delegate:
|
|
35520
|
+
|
|
35521
|
+
<delegate>
|
|
35522
|
+
<task>Analyze all authentication and authorization code in the codebase for security vulnerabilities and provide specific remediation recommendations</task>
|
|
35523
|
+
</delegate>
|
|
35524
|
+
|
|
35525
|
+
<delegate>
|
|
35526
|
+
<task>Review database queries and API endpoints for performance bottlenecks and suggest optimization strategies</task>
|
|
35527
|
+
</delegate>
|
|
35528
|
+
|
|
35529
|
+
The agent uses this tool automatically when it identifies that work can be separated into distinct, parallel tasks for more efficient processing.
|
|
35530
|
+
`;
|
|
35531
|
+
attemptCompletionToolDefinition = `
|
|
35532
|
+
## attempt_completion
|
|
35533
|
+
Description: Use this tool ONLY when the task is fully complete and you have received confirmation of success for all previous tool uses. Presents the final result to the user. You can provide your response directly inside the XML tags without any parameter wrapper.
|
|
35534
|
+
Parameters:
|
|
35535
|
+
- No validation required - provide your complete answer directly inside the XML tags.
|
|
35536
|
+
Usage Example:
|
|
35537
|
+
<attempt_completion>
|
|
35538
|
+
I have refactored the search module according to the requirements and verified the tests pass. The module now uses the new BM25 ranking algorithm and has improved error handling.
|
|
35539
|
+
</attempt_completion>
|
|
35540
|
+
`;
|
|
35541
|
+
bashToolDefinition = `
|
|
35542
|
+
## bash
|
|
35543
|
+
Description: Execute bash commands for system exploration and development tasks. This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
|
|
35544
|
+
|
|
35545
|
+
Parameters:
|
|
35546
|
+
- command: (required) The bash command to execute
|
|
35547
|
+
- workingDirectory: (optional) Directory to execute the command in
|
|
35548
|
+
- timeout: (optional) Command timeout in milliseconds
|
|
35549
|
+
- env: (optional) Additional environment variables as an object
|
|
35550
|
+
|
|
35551
|
+
Security: Commands are filtered through allow/deny lists for safety:
|
|
35552
|
+
- Allowed by default: ls, cat, git status, npm list, find, grep, etc.
|
|
35553
|
+
- Denied by default: rm -rf, sudo, npm install, dangerous system commands
|
|
35554
|
+
|
|
35555
|
+
Usage Examples:
|
|
35556
|
+
|
|
35557
|
+
<examples>
|
|
35558
|
+
|
|
35559
|
+
User: What files are in the src directory?
|
|
35560
|
+
<bash>
|
|
35561
|
+
<command>ls -la src/</command>
|
|
35562
|
+
</bash>
|
|
35563
|
+
|
|
35564
|
+
User: Show me the git status
|
|
35565
|
+
<bash>
|
|
35566
|
+
<command>git status</command>
|
|
35567
|
+
</bash>
|
|
35568
|
+
|
|
35569
|
+
User: Find all TypeScript files
|
|
35570
|
+
<bash>
|
|
35571
|
+
<command>find . -name "*.ts" -type f</command>
|
|
35572
|
+
</bash>
|
|
35573
|
+
|
|
35574
|
+
User: Check installed npm packages
|
|
35575
|
+
<bash>
|
|
35576
|
+
<command>npm list --depth=0</command>
|
|
35577
|
+
</bash>
|
|
35578
|
+
|
|
35579
|
+
User: Search for TODO comments in code
|
|
35580
|
+
<bash>
|
|
35581
|
+
<command>grep -r "TODO" src/</command>
|
|
35582
|
+
</bash>
|
|
35583
|
+
|
|
35584
|
+
User: Show recent git commits
|
|
35585
|
+
<bash>
|
|
35586
|
+
<command>git log --oneline -10</command>
|
|
35587
|
+
</bash>
|
|
35588
|
+
|
|
35589
|
+
User: Check system info
|
|
35590
|
+
<bash>
|
|
35591
|
+
<command>uname -a</command>
|
|
35592
|
+
</bash>
|
|
35593
|
+
|
|
35594
|
+
</examples>
|
|
35595
|
+
`;
|
|
35596
|
+
searchDescription = "Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.";
|
|
35597
|
+
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
35598
|
+
extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.";
|
|
35599
|
+
delegateDescription = "Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.";
|
|
35600
|
+
DEFAULT_VALID_TOOLS = [
|
|
35601
|
+
"search",
|
|
35602
|
+
"query",
|
|
35603
|
+
"extract",
|
|
35604
|
+
"delegate",
|
|
35605
|
+
"listFiles",
|
|
35606
|
+
"searchFiles",
|
|
35607
|
+
"implement",
|
|
35608
|
+
"bash",
|
|
35609
|
+
"attempt_completion"
|
|
35610
|
+
];
|
|
36209
35611
|
}
|
|
36210
35612
|
});
|
|
36211
35613
|
|
|
36212
|
-
// src/
|
|
36213
|
-
|
|
36214
|
-
|
|
36215
|
-
|
|
36216
|
-
if (!command) return false;
|
|
36217
|
-
const patternParts = pattern.split(":");
|
|
36218
|
-
const commandName = patternParts[0];
|
|
36219
|
-
if (commandName === "*") {
|
|
36220
|
-
return true;
|
|
36221
|
-
} else if (commandName !== command) {
|
|
36222
|
-
return false;
|
|
36223
|
-
}
|
|
36224
|
-
if (patternParts.length === 1) {
|
|
36225
|
-
return true;
|
|
36226
|
-
}
|
|
36227
|
-
for (let i4 = 1; i4 < patternParts.length; i4++) {
|
|
36228
|
-
const patternArg = patternParts[i4];
|
|
36229
|
-
const argIndex = i4 - 1;
|
|
36230
|
-
if (patternArg === "*") {
|
|
36231
|
-
continue;
|
|
36232
|
-
}
|
|
36233
|
-
if (argIndex >= args.length) {
|
|
36234
|
-
return false;
|
|
36235
|
-
}
|
|
36236
|
-
const actualArg = args[argIndex];
|
|
36237
|
-
if (patternArg !== actualArg) {
|
|
36238
|
-
return false;
|
|
36239
|
-
}
|
|
36240
|
-
}
|
|
36241
|
-
return true;
|
|
36242
|
-
}
|
|
36243
|
-
function matchesAnyPattern(parsedCommand, patterns) {
|
|
36244
|
-
if (!patterns || patterns.length === 0) return false;
|
|
36245
|
-
return patterns.some((pattern) => matchesPattern(parsedCommand, pattern));
|
|
36246
|
-
}
|
|
36247
|
-
var BashPermissionChecker;
|
|
36248
|
-
var init_bashPermissions = __esm({
|
|
36249
|
-
"src/agent/bashPermissions.js"() {
|
|
35614
|
+
// src/tools/vercel.js
|
|
35615
|
+
var import_ai2, searchTool, queryTool, extractTool, delegateTool;
|
|
35616
|
+
var init_vercel = __esm({
|
|
35617
|
+
"src/tools/vercel.js"() {
|
|
36250
35618
|
"use strict";
|
|
36251
|
-
|
|
36252
|
-
|
|
36253
|
-
|
|
36254
|
-
|
|
36255
|
-
|
|
36256
|
-
|
|
36257
|
-
|
|
36258
|
-
|
|
36259
|
-
|
|
36260
|
-
|
|
36261
|
-
|
|
36262
|
-
|
|
36263
|
-
|
|
36264
|
-
|
|
36265
|
-
|
|
36266
|
-
|
|
36267
|
-
|
|
36268
|
-
|
|
36269
|
-
console.log(`[BashPermissions] Added ${DEFAULT_ALLOW_PATTERNS.length} default allow patterns`);
|
|
36270
|
-
}
|
|
36271
|
-
}
|
|
36272
|
-
if (config.allow && Array.isArray(config.allow)) {
|
|
36273
|
-
this.allowPatterns.push(...config.allow);
|
|
36274
|
-
if (this.debug) {
|
|
36275
|
-
console.log(`[BashPermissions] Added ${config.allow.length} custom allow patterns:`, config.allow);
|
|
36276
|
-
}
|
|
36277
|
-
}
|
|
36278
|
-
this.denyPatterns = [];
|
|
36279
|
-
if (!config.disableDefaultDeny) {
|
|
36280
|
-
this.denyPatterns.push(...DEFAULT_DENY_PATTERNS);
|
|
36281
|
-
if (this.debug) {
|
|
36282
|
-
console.log(`[BashPermissions] Added ${DEFAULT_DENY_PATTERNS.length} default deny patterns`);
|
|
36283
|
-
}
|
|
36284
|
-
}
|
|
36285
|
-
if (config.deny && Array.isArray(config.deny)) {
|
|
36286
|
-
this.denyPatterns.push(...config.deny);
|
|
36287
|
-
if (this.debug) {
|
|
36288
|
-
console.log(`[BashPermissions] Added ${config.deny.length} custom deny patterns:`, config.deny);
|
|
36289
|
-
}
|
|
36290
|
-
}
|
|
36291
|
-
if (this.debug) {
|
|
36292
|
-
console.log(`[BashPermissions] Total patterns - Allow: ${this.allowPatterns.length}, Deny: ${this.denyPatterns.length}`);
|
|
36293
|
-
}
|
|
36294
|
-
}
|
|
36295
|
-
/**
|
|
36296
|
-
* Check if a simple command is allowed (complex commands allowed if they match patterns)
|
|
36297
|
-
* @param {string} command - Command to check
|
|
36298
|
-
* @returns {Object} Permission result
|
|
36299
|
-
*/
|
|
36300
|
-
check(command) {
|
|
36301
|
-
if (!command || typeof command !== "string") {
|
|
36302
|
-
return {
|
|
36303
|
-
allowed: false,
|
|
36304
|
-
reason: "Invalid or empty command",
|
|
36305
|
-
command
|
|
36306
|
-
};
|
|
36307
|
-
}
|
|
36308
|
-
const commandIsComplex = isComplexCommand(command);
|
|
36309
|
-
if (commandIsComplex) {
|
|
36310
|
-
return this._checkComplexCommand(command);
|
|
36311
|
-
}
|
|
36312
|
-
const parsed = parseCommand(command);
|
|
36313
|
-
if (parsed.error) {
|
|
36314
|
-
return {
|
|
36315
|
-
allowed: false,
|
|
36316
|
-
reason: parsed.error,
|
|
36317
|
-
command
|
|
36318
|
-
};
|
|
36319
|
-
}
|
|
36320
|
-
if (!parsed.command) {
|
|
36321
|
-
return {
|
|
36322
|
-
allowed: false,
|
|
36323
|
-
reason: "No valid command found",
|
|
36324
|
-
command
|
|
36325
|
-
};
|
|
36326
|
-
}
|
|
36327
|
-
if (this.debug) {
|
|
36328
|
-
console.log(`[BashPermissions] Checking simple command: "${command}"`);
|
|
36329
|
-
console.log(`[BashPermissions] Parsed: ${parsed.command} with args: [${parsed.args.join(", ")}]`);
|
|
36330
|
-
}
|
|
36331
|
-
if (matchesAnyPattern(parsed, this.denyPatterns)) {
|
|
36332
|
-
const matchedPatterns = this.denyPatterns.filter((pattern) => matchesPattern(parsed, pattern));
|
|
36333
|
-
return {
|
|
36334
|
-
allowed: false,
|
|
36335
|
-
reason: `Command matches deny pattern: ${matchedPatterns[0]}`,
|
|
36336
|
-
command,
|
|
36337
|
-
parsed,
|
|
36338
|
-
matchedPatterns
|
|
36339
|
-
};
|
|
36340
|
-
}
|
|
36341
|
-
if (this.allowPatterns.length > 0) {
|
|
36342
|
-
if (!matchesAnyPattern(parsed, this.allowPatterns)) {
|
|
36343
|
-
return {
|
|
36344
|
-
allowed: false,
|
|
36345
|
-
reason: "Command not in allow list",
|
|
36346
|
-
command,
|
|
36347
|
-
parsed
|
|
36348
|
-
};
|
|
36349
|
-
}
|
|
36350
|
-
}
|
|
36351
|
-
const result = {
|
|
36352
|
-
allowed: true,
|
|
36353
|
-
command,
|
|
36354
|
-
parsed,
|
|
36355
|
-
isComplex: false
|
|
36356
|
-
};
|
|
36357
|
-
if (this.debug) {
|
|
36358
|
-
console.log(`[BashPermissions] ALLOWED - command passed all checks`);
|
|
36359
|
-
}
|
|
36360
|
-
return result;
|
|
36361
|
-
}
|
|
36362
|
-
/**
|
|
36363
|
-
* Check a complex command against complex patterns in allow/deny lists
|
|
36364
|
-
* @private
|
|
36365
|
-
* @param {string} command - Complex command to check
|
|
36366
|
-
* @returns {Object} Permission result
|
|
36367
|
-
*/
|
|
36368
|
-
_checkComplexCommand(command) {
|
|
36369
|
-
if (this.debug) {
|
|
36370
|
-
console.log(`[BashPermissions] Checking complex command: "${command}"`);
|
|
36371
|
-
}
|
|
36372
|
-
const complexAllowPatterns = this.allowPatterns.filter((p4) => isComplexPattern(p4));
|
|
36373
|
-
const complexDenyPatterns = this.denyPatterns.filter((p4) => isComplexPattern(p4));
|
|
36374
|
-
if (this.debug) {
|
|
36375
|
-
console.log(`[BashPermissions] Complex allow patterns: ${complexAllowPatterns.length}`);
|
|
36376
|
-
console.log(`[BashPermissions] Complex deny patterns: ${complexDenyPatterns.length}`);
|
|
36377
|
-
}
|
|
36378
|
-
for (const pattern of complexDenyPatterns) {
|
|
36379
|
-
if (matchesComplexPattern(command, pattern)) {
|
|
36380
|
-
if (this.debug) {
|
|
36381
|
-
console.log(`[BashPermissions] DENIED - matches complex deny pattern: ${pattern}`);
|
|
35619
|
+
import_ai2 = require("ai");
|
|
35620
|
+
init_search();
|
|
35621
|
+
init_query();
|
|
35622
|
+
init_extract();
|
|
35623
|
+
init_delegate();
|
|
35624
|
+
init_common2();
|
|
35625
|
+
searchTool = (options = {}) => {
|
|
35626
|
+
const { sessionId, maxTokens = 1e4, debug = false, outline = false } = options;
|
|
35627
|
+
return (0, import_ai2.tool)({
|
|
35628
|
+
name: "search",
|
|
35629
|
+
description: searchDescription,
|
|
35630
|
+
inputSchema: searchSchema,
|
|
35631
|
+
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language }) => {
|
|
35632
|
+
try {
|
|
35633
|
+
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
35634
|
+
let searchPaths;
|
|
35635
|
+
if (path9) {
|
|
35636
|
+
searchPaths = parseAndResolvePaths(path9, options.cwd);
|
|
36382
35637
|
}
|
|
36383
|
-
|
|
36384
|
-
|
|
36385
|
-
|
|
36386
|
-
|
|
36387
|
-
|
|
36388
|
-
|
|
35638
|
+
if (!searchPaths || searchPaths.length === 0) {
|
|
35639
|
+
searchPaths = [options.cwd || "."];
|
|
35640
|
+
}
|
|
35641
|
+
const searchPath = searchPaths.join(" ");
|
|
35642
|
+
if (debug) {
|
|
35643
|
+
console.error(`Executing search with query: "${searchQuery}", path: "${searchPath}", exact: ${exact ? "true" : "false"}, language: ${language || "all"}, session: ${sessionId || "none"}`);
|
|
35644
|
+
}
|
|
35645
|
+
const searchOptions = {
|
|
35646
|
+
query: searchQuery,
|
|
35647
|
+
path: searchPath,
|
|
35648
|
+
cwd: options.cwd,
|
|
35649
|
+
// Working directory for resolving relative paths
|
|
35650
|
+
allowTests: allow_tests ?? true,
|
|
35651
|
+
exact,
|
|
35652
|
+
json: false,
|
|
35653
|
+
maxTokens: effectiveMaxTokens,
|
|
35654
|
+
session: sessionId,
|
|
35655
|
+
// Pass session ID if provided
|
|
35656
|
+
language
|
|
35657
|
+
// Pass language parameter if provided
|
|
36389
35658
|
};
|
|
35659
|
+
if (outline) {
|
|
35660
|
+
searchOptions.format = "outline-xml";
|
|
35661
|
+
}
|
|
35662
|
+
const results = await search(searchOptions);
|
|
35663
|
+
return results;
|
|
35664
|
+
} catch (error2) {
|
|
35665
|
+
console.error("Error executing search command:", error2);
|
|
35666
|
+
return `Error executing search command: ${error2.message}`;
|
|
36390
35667
|
}
|
|
36391
35668
|
}
|
|
36392
|
-
|
|
36393
|
-
|
|
36394
|
-
|
|
36395
|
-
|
|
35669
|
+
});
|
|
35670
|
+
};
|
|
35671
|
+
queryTool = (options = {}) => {
|
|
35672
|
+
const { debug = false } = options;
|
|
35673
|
+
return (0, import_ai2.tool)({
|
|
35674
|
+
name: "query",
|
|
35675
|
+
description: queryDescription,
|
|
35676
|
+
inputSchema: querySchema,
|
|
35677
|
+
execute: async ({ pattern, path: path9, language, allow_tests }) => {
|
|
35678
|
+
try {
|
|
35679
|
+
let queryPaths;
|
|
35680
|
+
if (path9) {
|
|
35681
|
+
queryPaths = parseAndResolvePaths(path9, options.cwd);
|
|
36396
35682
|
}
|
|
36397
|
-
|
|
36398
|
-
|
|
36399
|
-
|
|
36400
|
-
|
|
36401
|
-
|
|
36402
|
-
|
|
35683
|
+
if (!queryPaths || queryPaths.length === 0) {
|
|
35684
|
+
queryPaths = [options.cwd || "."];
|
|
35685
|
+
}
|
|
35686
|
+
const queryPath = queryPaths.join(" ");
|
|
35687
|
+
if (debug) {
|
|
35688
|
+
console.error(`Executing query with pattern: "${pattern}", path: "${queryPath}", language: ${language || "auto"}`);
|
|
35689
|
+
}
|
|
35690
|
+
const results = await query({
|
|
35691
|
+
pattern,
|
|
35692
|
+
path: queryPath,
|
|
35693
|
+
cwd: options.cwd,
|
|
35694
|
+
// Working directory for resolving relative paths
|
|
35695
|
+
language,
|
|
35696
|
+
allowTests: allow_tests ?? true,
|
|
35697
|
+
json: false
|
|
35698
|
+
});
|
|
35699
|
+
return results;
|
|
35700
|
+
} catch (error2) {
|
|
35701
|
+
console.error("Error executing query command:", error2);
|
|
35702
|
+
return `Error executing query command: ${error2.message}`;
|
|
36403
35703
|
}
|
|
36404
35704
|
}
|
|
36405
|
-
|
|
36406
|
-
console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
|
|
36407
|
-
}
|
|
36408
|
-
return {
|
|
36409
|
-
allowed: false,
|
|
36410
|
-
reason: 'Complex shell commands require explicit allow patterns (e.g., "cd * && git *")',
|
|
36411
|
-
command,
|
|
36412
|
-
isComplex: true
|
|
36413
|
-
};
|
|
36414
|
-
}
|
|
36415
|
-
/**
|
|
36416
|
-
* Get configuration summary
|
|
36417
|
-
* @returns {Object} Configuration info
|
|
36418
|
-
*/
|
|
36419
|
-
getConfig() {
|
|
36420
|
-
return {
|
|
36421
|
-
allowPatterns: this.allowPatterns.length,
|
|
36422
|
-
denyPatterns: this.denyPatterns.length,
|
|
36423
|
-
totalPatterns: this.allowPatterns.length + this.denyPatterns.length
|
|
36424
|
-
};
|
|
36425
|
-
}
|
|
36426
|
-
};
|
|
36427
|
-
}
|
|
36428
|
-
});
|
|
36429
|
-
|
|
36430
|
-
// src/agent/bashExecutor.js
|
|
36431
|
-
async function executeBashCommand(command, options = {}) {
|
|
36432
|
-
const {
|
|
36433
|
-
workingDirectory = process.cwd(),
|
|
36434
|
-
timeout = 12e4,
|
|
36435
|
-
// 2 minutes default
|
|
36436
|
-
env = {},
|
|
36437
|
-
maxBuffer = 10 * 1024 * 1024,
|
|
36438
|
-
// 10MB
|
|
36439
|
-
debug = false
|
|
36440
|
-
} = options;
|
|
36441
|
-
let cwd = workingDirectory;
|
|
36442
|
-
try {
|
|
36443
|
-
cwd = (0, import_path6.resolve)(cwd);
|
|
36444
|
-
if (!(0, import_fs4.existsSync)(cwd)) {
|
|
36445
|
-
throw new Error(`Working directory does not exist: ${cwd}`);
|
|
36446
|
-
}
|
|
36447
|
-
} catch (error2) {
|
|
36448
|
-
return {
|
|
36449
|
-
success: false,
|
|
36450
|
-
error: `Invalid working directory: ${error2.message}`,
|
|
36451
|
-
stdout: "",
|
|
36452
|
-
stderr: "",
|
|
36453
|
-
exitCode: 1,
|
|
36454
|
-
command,
|
|
36455
|
-
workingDirectory: cwd,
|
|
36456
|
-
duration: 0
|
|
36457
|
-
};
|
|
36458
|
-
}
|
|
36459
|
-
const startTime = Date.now();
|
|
36460
|
-
if (debug) {
|
|
36461
|
-
console.log(`[BashExecutor] Executing command: "${command}"`);
|
|
36462
|
-
console.log(`[BashExecutor] Working directory: "${cwd}"`);
|
|
36463
|
-
console.log(`[BashExecutor] Timeout: ${timeout}ms`);
|
|
36464
|
-
}
|
|
36465
|
-
return new Promise((resolve6, reject2) => {
|
|
36466
|
-
const processEnv = {
|
|
36467
|
-
...process.env,
|
|
36468
|
-
...env
|
|
35705
|
+
});
|
|
36469
35706
|
};
|
|
36470
|
-
|
|
36471
|
-
|
|
36472
|
-
|
|
36473
|
-
|
|
36474
|
-
|
|
36475
|
-
|
|
36476
|
-
|
|
36477
|
-
|
|
36478
|
-
|
|
36479
|
-
|
|
36480
|
-
|
|
36481
|
-
|
|
36482
|
-
|
|
36483
|
-
|
|
36484
|
-
|
|
36485
|
-
stdout: "",
|
|
36486
|
-
stderr: "",
|
|
36487
|
-
exitCode: 1,
|
|
36488
|
-
command,
|
|
36489
|
-
workingDirectory: cwd,
|
|
36490
|
-
duration: Date.now() - startTime
|
|
36491
|
-
});
|
|
36492
|
-
return;
|
|
36493
|
-
}
|
|
36494
|
-
[cmd, ...cmdArgs] = args;
|
|
36495
|
-
useShell = false;
|
|
36496
|
-
}
|
|
36497
|
-
const child = (0, import_child_process6.spawn)(cmd, cmdArgs, {
|
|
36498
|
-
cwd,
|
|
36499
|
-
env: processEnv,
|
|
36500
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
36501
|
-
// stdin ignored, capture stdout/stderr
|
|
36502
|
-
shell: useShell,
|
|
36503
|
-
// false for security
|
|
36504
|
-
windowsHide: true
|
|
36505
|
-
});
|
|
36506
|
-
let stdout = "";
|
|
36507
|
-
let stderr = "";
|
|
36508
|
-
let killed = false;
|
|
36509
|
-
let timeoutHandle;
|
|
36510
|
-
if (timeout > 0) {
|
|
36511
|
-
timeoutHandle = setTimeout(() => {
|
|
36512
|
-
if (!killed) {
|
|
36513
|
-
killed = true;
|
|
36514
|
-
child.kill("SIGTERM");
|
|
36515
|
-
setTimeout(() => {
|
|
36516
|
-
if (child.exitCode === null) {
|
|
36517
|
-
child.kill("SIGKILL");
|
|
35707
|
+
extractTool = (options = {}) => {
|
|
35708
|
+
const { debug = false, outline = false } = options;
|
|
35709
|
+
return (0, import_ai2.tool)({
|
|
35710
|
+
name: "extract",
|
|
35711
|
+
description: extractDescription,
|
|
35712
|
+
inputSchema: extractSchema,
|
|
35713
|
+
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format: format2 }) => {
|
|
35714
|
+
try {
|
|
35715
|
+
const effectiveCwd = options.cwd || ".";
|
|
35716
|
+
if (debug) {
|
|
35717
|
+
if (targets) {
|
|
35718
|
+
console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
|
35719
|
+
} else if (input_content) {
|
|
35720
|
+
console.error(`Executing extract with input content, cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
|
35721
|
+
}
|
|
36518
35722
|
}
|
|
36519
|
-
|
|
36520
|
-
|
|
36521
|
-
|
|
36522
|
-
|
|
36523
|
-
|
|
36524
|
-
|
|
36525
|
-
|
|
36526
|
-
|
|
36527
|
-
|
|
36528
|
-
|
|
36529
|
-
|
|
36530
|
-
|
|
36531
|
-
|
|
36532
|
-
|
|
36533
|
-
|
|
36534
|
-
|
|
36535
|
-
|
|
36536
|
-
|
|
36537
|
-
|
|
36538
|
-
|
|
36539
|
-
|
|
36540
|
-
|
|
36541
|
-
|
|
36542
|
-
|
|
36543
|
-
|
|
36544
|
-
|
|
36545
|
-
|
|
36546
|
-
|
|
36547
|
-
|
|
36548
|
-
|
|
36549
|
-
|
|
36550
|
-
|
|
36551
|
-
|
|
36552
|
-
|
|
36553
|
-
|
|
36554
|
-
|
|
36555
|
-
|
|
36556
|
-
|
|
36557
|
-
|
|
36558
|
-
|
|
36559
|
-
|
|
36560
|
-
|
|
36561
|
-
|
|
35723
|
+
let tempFilePath = null;
|
|
35724
|
+
let extractOptions = { cwd: effectiveCwd };
|
|
35725
|
+
if (input_content) {
|
|
35726
|
+
const { writeFileSync: writeFileSync2, unlinkSync } = await import("fs");
|
|
35727
|
+
const { join: join3 } = await import("path");
|
|
35728
|
+
const { tmpdir } = await import("os");
|
|
35729
|
+
const { randomUUID: randomUUID6 } = await import("crypto");
|
|
35730
|
+
tempFilePath = join3(tmpdir(), `probe-extract-${randomUUID6()}.txt`);
|
|
35731
|
+
writeFileSync2(tempFilePath, input_content);
|
|
35732
|
+
if (debug) {
|
|
35733
|
+
console.error(`Created temporary file for input content: ${tempFilePath}`);
|
|
35734
|
+
}
|
|
35735
|
+
let effectiveFormat = format2;
|
|
35736
|
+
if (outline && format2 === "outline-xml") {
|
|
35737
|
+
effectiveFormat = "xml";
|
|
35738
|
+
}
|
|
35739
|
+
extractOptions = {
|
|
35740
|
+
inputFile: tempFilePath,
|
|
35741
|
+
cwd: effectiveCwd,
|
|
35742
|
+
allowTests: allow_tests ?? true,
|
|
35743
|
+
contextLines: context_lines,
|
|
35744
|
+
format: effectiveFormat
|
|
35745
|
+
};
|
|
35746
|
+
} else if (targets) {
|
|
35747
|
+
const parsedTargets = parseTargets(targets);
|
|
35748
|
+
const files = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
|
|
35749
|
+
let effectiveFormat = format2;
|
|
35750
|
+
if (outline && format2 === "outline-xml") {
|
|
35751
|
+
effectiveFormat = "xml";
|
|
35752
|
+
}
|
|
35753
|
+
extractOptions = {
|
|
35754
|
+
files,
|
|
35755
|
+
cwd: effectiveCwd,
|
|
35756
|
+
allowTests: allow_tests ?? true,
|
|
35757
|
+
contextLines: context_lines,
|
|
35758
|
+
format: effectiveFormat
|
|
35759
|
+
};
|
|
35760
|
+
} else {
|
|
35761
|
+
throw new Error("Either targets or input_content must be provided");
|
|
35762
|
+
}
|
|
35763
|
+
const results = await extract(extractOptions);
|
|
35764
|
+
if (tempFilePath) {
|
|
35765
|
+
const { unlinkSync } = await import("fs");
|
|
35766
|
+
try {
|
|
35767
|
+
unlinkSync(tempFilePath);
|
|
35768
|
+
if (debug) {
|
|
35769
|
+
console.error(`Removed temporary file: ${tempFilePath}`);
|
|
35770
|
+
}
|
|
35771
|
+
} catch (cleanupError) {
|
|
35772
|
+
console.error(`Warning: Failed to remove temporary file: ${cleanupError.message}`);
|
|
35773
|
+
}
|
|
35774
|
+
}
|
|
35775
|
+
return results;
|
|
35776
|
+
} catch (error2) {
|
|
35777
|
+
console.error("Error executing extract command:", error2);
|
|
35778
|
+
return `Error executing extract command: ${error2.message}`;
|
|
35779
|
+
}
|
|
36562
35780
|
}
|
|
36563
|
-
} else if (code !== 0) {
|
|
36564
|
-
success = false;
|
|
36565
|
-
error2 = `Command exited with code ${code}`;
|
|
36566
|
-
}
|
|
36567
|
-
resolve6({
|
|
36568
|
-
success,
|
|
36569
|
-
error: error2,
|
|
36570
|
-
stdout: stdout.trim(),
|
|
36571
|
-
stderr: stderr.trim(),
|
|
36572
|
-
exitCode: code,
|
|
36573
|
-
signal,
|
|
36574
|
-
command,
|
|
36575
|
-
workingDirectory: cwd,
|
|
36576
|
-
duration,
|
|
36577
|
-
killed
|
|
36578
35781
|
});
|
|
36579
|
-
}
|
|
36580
|
-
|
|
36581
|
-
|
|
36582
|
-
|
|
36583
|
-
|
|
36584
|
-
|
|
36585
|
-
|
|
36586
|
-
|
|
36587
|
-
|
|
36588
|
-
|
|
36589
|
-
|
|
36590
|
-
|
|
36591
|
-
|
|
36592
|
-
|
|
36593
|
-
|
|
36594
|
-
|
|
36595
|
-
|
|
35782
|
+
};
|
|
35783
|
+
delegateTool = (options = {}) => {
|
|
35784
|
+
const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig } = options;
|
|
35785
|
+
return (0, import_ai2.tool)({
|
|
35786
|
+
name: "delegate",
|
|
35787
|
+
description: delegateDescription,
|
|
35788
|
+
inputSchema: delegateSchema,
|
|
35789
|
+
execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer }) => {
|
|
35790
|
+
if (!task || typeof task !== "string") {
|
|
35791
|
+
throw new Error("Task parameter is required and must be a non-empty string");
|
|
35792
|
+
}
|
|
35793
|
+
if (task.trim().length === 0) {
|
|
35794
|
+
throw new Error("Task parameter cannot be empty or whitespace only");
|
|
35795
|
+
}
|
|
35796
|
+
if (currentIteration !== void 0 && (typeof currentIteration !== "number" || currentIteration < 0)) {
|
|
35797
|
+
throw new Error("currentIteration must be a non-negative number");
|
|
35798
|
+
}
|
|
35799
|
+
if (maxIterations !== void 0 && (typeof maxIterations !== "number" || maxIterations < 1)) {
|
|
35800
|
+
throw new Error("maxIterations must be a positive number");
|
|
35801
|
+
}
|
|
35802
|
+
if (parentSessionId !== void 0 && parentSessionId !== null && typeof parentSessionId !== "string") {
|
|
35803
|
+
throw new TypeError("parentSessionId must be a string, null, or undefined");
|
|
35804
|
+
}
|
|
35805
|
+
if (path9 !== void 0 && path9 !== null && typeof path9 !== "string") {
|
|
35806
|
+
throw new TypeError("path must be a string, null, or undefined");
|
|
35807
|
+
}
|
|
35808
|
+
if (provider !== void 0 && provider !== null && typeof provider !== "string") {
|
|
35809
|
+
throw new TypeError("provider must be a string, null, or undefined");
|
|
35810
|
+
}
|
|
35811
|
+
if (model !== void 0 && model !== null && typeof model !== "string") {
|
|
35812
|
+
throw new TypeError("model must be a string, null, or undefined");
|
|
35813
|
+
}
|
|
35814
|
+
const workspaceRoot = allowedFolders && allowedFolders[0];
|
|
35815
|
+
const effectivePath = path9 || workspaceRoot || cwd;
|
|
35816
|
+
if (debug) {
|
|
35817
|
+
console.error(`Executing delegate with task: "${task.substring(0, 100)}${task.length > 100 ? "..." : ""}"`);
|
|
35818
|
+
if (parentSessionId) {
|
|
35819
|
+
console.error(`Parent session: ${parentSessionId}`);
|
|
35820
|
+
}
|
|
35821
|
+
if (effectivePath && effectivePath !== path9) {
|
|
35822
|
+
console.error(`Using workspace root: ${effectivePath} (cwd was: ${cwd || "not set"})`);
|
|
35823
|
+
}
|
|
35824
|
+
}
|
|
35825
|
+
const result = await delegate({
|
|
35826
|
+
task,
|
|
35827
|
+
timeout,
|
|
35828
|
+
debug,
|
|
35829
|
+
currentIteration: currentIteration || 0,
|
|
35830
|
+
maxIterations: maxIterations || 30,
|
|
35831
|
+
parentSessionId,
|
|
35832
|
+
path: effectivePath,
|
|
35833
|
+
provider,
|
|
35834
|
+
model,
|
|
35835
|
+
tracer,
|
|
35836
|
+
enableBash,
|
|
35837
|
+
bashConfig
|
|
35838
|
+
});
|
|
35839
|
+
return result;
|
|
35840
|
+
}
|
|
36596
35841
|
});
|
|
36597
|
-
}
|
|
36598
|
-
}
|
|
36599
|
-
}
|
|
36600
|
-
|
|
36601
|
-
|
|
36602
|
-
|
|
35842
|
+
};
|
|
35843
|
+
}
|
|
35844
|
+
});
|
|
35845
|
+
|
|
35846
|
+
// src/agent/bashDefaults.js
|
|
35847
|
+
var DEFAULT_ALLOW_PATTERNS, DEFAULT_DENY_PATTERNS;
|
|
35848
|
+
var init_bashDefaults = __esm({
|
|
35849
|
+
"src/agent/bashDefaults.js"() {
|
|
35850
|
+
"use strict";
|
|
35851
|
+
DEFAULT_ALLOW_PATTERNS = [
|
|
35852
|
+
// Basic navigation and listing
|
|
35853
|
+
"ls",
|
|
35854
|
+
"dir",
|
|
35855
|
+
"pwd",
|
|
35856
|
+
"cd",
|
|
35857
|
+
"cd:*",
|
|
35858
|
+
// File reading commands
|
|
35859
|
+
"cat",
|
|
35860
|
+
"cat:*",
|
|
35861
|
+
"head",
|
|
35862
|
+
"head:*",
|
|
35863
|
+
"tail",
|
|
35864
|
+
"tail:*",
|
|
35865
|
+
"less",
|
|
35866
|
+
"more",
|
|
35867
|
+
"view",
|
|
35868
|
+
// File information and metadata
|
|
35869
|
+
"file",
|
|
35870
|
+
"file:*",
|
|
35871
|
+
"stat",
|
|
35872
|
+
"stat:*",
|
|
35873
|
+
"wc",
|
|
35874
|
+
"wc:*",
|
|
35875
|
+
"du",
|
|
35876
|
+
"du:*",
|
|
35877
|
+
"df",
|
|
35878
|
+
"df:*",
|
|
35879
|
+
"realpath",
|
|
35880
|
+
"realpath:*",
|
|
35881
|
+
// Search and find commands (read-only) - find restricted to safe operations
|
|
35882
|
+
"find",
|
|
35883
|
+
"find:-name:*",
|
|
35884
|
+
"find:-type:*",
|
|
35885
|
+
"find:-size:*",
|
|
35886
|
+
"find:-mtime:*",
|
|
35887
|
+
"find:-newer:*",
|
|
35888
|
+
"find:-path:*",
|
|
35889
|
+
"find:-iname:*",
|
|
35890
|
+
"find:-maxdepth:*",
|
|
35891
|
+
"find:-mindepth:*",
|
|
35892
|
+
"find:-print",
|
|
35893
|
+
"grep",
|
|
35894
|
+
"grep:*",
|
|
35895
|
+
"egrep",
|
|
35896
|
+
"egrep:*",
|
|
35897
|
+
"fgrep",
|
|
35898
|
+
"fgrep:*",
|
|
35899
|
+
"rg",
|
|
35900
|
+
"rg:*",
|
|
35901
|
+
"ag",
|
|
35902
|
+
"ag:*",
|
|
35903
|
+
"ack",
|
|
35904
|
+
"ack:*",
|
|
35905
|
+
"which",
|
|
35906
|
+
"which:*",
|
|
35907
|
+
"whereis",
|
|
35908
|
+
"whereis:*",
|
|
35909
|
+
"locate",
|
|
35910
|
+
"locate:*",
|
|
35911
|
+
"type",
|
|
35912
|
+
"type:*",
|
|
35913
|
+
"command",
|
|
35914
|
+
"command:*",
|
|
35915
|
+
// Tree and structure visualization
|
|
35916
|
+
"tree",
|
|
35917
|
+
"tree:*",
|
|
35918
|
+
// Git read-only operations
|
|
35919
|
+
"git:status",
|
|
35920
|
+
"git:log",
|
|
35921
|
+
"git:log:*",
|
|
35922
|
+
"git:diff",
|
|
35923
|
+
"git:diff:*",
|
|
35924
|
+
"git:show",
|
|
35925
|
+
"git:show:*",
|
|
35926
|
+
"git:branch",
|
|
35927
|
+
"git:branch:*",
|
|
35928
|
+
"git:tag",
|
|
35929
|
+
"git:tag:*",
|
|
35930
|
+
"git:describe",
|
|
35931
|
+
"git:describe:*",
|
|
35932
|
+
"git:remote",
|
|
35933
|
+
"git:remote:*",
|
|
35934
|
+
"git:config:*",
|
|
35935
|
+
"git:blame",
|
|
35936
|
+
"git:blame:*",
|
|
35937
|
+
"git:shortlog",
|
|
35938
|
+
"git:reflog",
|
|
35939
|
+
"git:ls-files",
|
|
35940
|
+
"git:ls-tree",
|
|
35941
|
+
"git:rev-parse",
|
|
35942
|
+
"git:rev-list",
|
|
35943
|
+
"git:--version",
|
|
35944
|
+
"git:help",
|
|
35945
|
+
"git:help:*",
|
|
35946
|
+
// Package managers (information only)
|
|
35947
|
+
"npm:list",
|
|
35948
|
+
"npm:ls",
|
|
35949
|
+
"npm:view",
|
|
35950
|
+
"npm:info",
|
|
35951
|
+
"npm:show",
|
|
35952
|
+
"npm:outdated",
|
|
35953
|
+
"npm:audit",
|
|
35954
|
+
"npm:--version",
|
|
35955
|
+
"yarn:list",
|
|
35956
|
+
"yarn:info",
|
|
35957
|
+
"yarn:--version",
|
|
35958
|
+
"pnpm:list",
|
|
35959
|
+
"pnpm:--version",
|
|
35960
|
+
"pip:list",
|
|
35961
|
+
"pip:show",
|
|
35962
|
+
"pip:--version",
|
|
35963
|
+
"pip3:list",
|
|
35964
|
+
"pip3:show",
|
|
35965
|
+
"pip3:--version",
|
|
35966
|
+
"gem:list",
|
|
35967
|
+
"gem:--version",
|
|
35968
|
+
"bundle:list",
|
|
35969
|
+
"bundle:show",
|
|
35970
|
+
"bundle:--version",
|
|
35971
|
+
"composer:show",
|
|
35972
|
+
"composer:--version",
|
|
35973
|
+
// Language and runtime versions
|
|
35974
|
+
"node:--version",
|
|
35975
|
+
"node:-v",
|
|
35976
|
+
"python:--version",
|
|
35977
|
+
"python:-V",
|
|
35978
|
+
"python3:--version",
|
|
35979
|
+
"python3:-V",
|
|
35980
|
+
"ruby:--version",
|
|
35981
|
+
"ruby:-v",
|
|
35982
|
+
"go:version",
|
|
35983
|
+
"go:env",
|
|
35984
|
+
"go:list",
|
|
35985
|
+
"go:mod:graph",
|
|
35986
|
+
"rustc:--version",
|
|
35987
|
+
"cargo:--version",
|
|
35988
|
+
"cargo:tree",
|
|
35989
|
+
"cargo:metadata",
|
|
35990
|
+
"java:--version",
|
|
35991
|
+
"java:-version",
|
|
35992
|
+
"javac:--version",
|
|
35993
|
+
"mvn:--version",
|
|
35994
|
+
"gradle:--version",
|
|
35995
|
+
"php:--version",
|
|
35996
|
+
"dotnet:--version",
|
|
35997
|
+
"dotnet:list",
|
|
35998
|
+
// Database client versions (connection info only)
|
|
35999
|
+
"psql:--version",
|
|
36000
|
+
"mysql:--version",
|
|
36001
|
+
"redis-cli:--version",
|
|
36002
|
+
"mongo:--version",
|
|
36003
|
+
"sqlite3:--version",
|
|
36004
|
+
// System information
|
|
36005
|
+
"uname",
|
|
36006
|
+
"uname:*",
|
|
36007
|
+
"hostname",
|
|
36008
|
+
"whoami",
|
|
36009
|
+
"id",
|
|
36010
|
+
"groups",
|
|
36011
|
+
"date",
|
|
36012
|
+
"cal",
|
|
36013
|
+
"uptime",
|
|
36014
|
+
"w",
|
|
36015
|
+
"users",
|
|
36016
|
+
"sleep",
|
|
36017
|
+
"sleep:*",
|
|
36018
|
+
// Environment and shell
|
|
36019
|
+
"env",
|
|
36020
|
+
"printenv",
|
|
36021
|
+
"echo",
|
|
36022
|
+
"echo:*",
|
|
36023
|
+
"printf",
|
|
36024
|
+
"printf:*",
|
|
36025
|
+
"export",
|
|
36026
|
+
"export:*",
|
|
36027
|
+
"set",
|
|
36028
|
+
"unset",
|
|
36029
|
+
// Process information (read-only)
|
|
36030
|
+
"ps",
|
|
36031
|
+
"ps:*",
|
|
36032
|
+
"pgrep",
|
|
36033
|
+
"pgrep:*",
|
|
36034
|
+
"jobs",
|
|
36035
|
+
"top:-n:1",
|
|
36036
|
+
// Network information (read-only)
|
|
36037
|
+
"ifconfig",
|
|
36038
|
+
"ip:addr",
|
|
36039
|
+
"ip:link",
|
|
36040
|
+
"hostname:-I",
|
|
36041
|
+
"ping:-c:*",
|
|
36042
|
+
"traceroute",
|
|
36043
|
+
"nslookup",
|
|
36044
|
+
"dig",
|
|
36045
|
+
// Text processing and utilities (awk removed - too powerful)
|
|
36046
|
+
"sed:-n:*",
|
|
36047
|
+
"cut",
|
|
36048
|
+
"cut:*",
|
|
36049
|
+
"sort",
|
|
36050
|
+
"sort:*",
|
|
36051
|
+
"uniq",
|
|
36052
|
+
"uniq:*",
|
|
36053
|
+
"tr",
|
|
36054
|
+
"tr:*",
|
|
36055
|
+
"column",
|
|
36056
|
+
"column:*",
|
|
36057
|
+
"paste",
|
|
36058
|
+
"paste:*",
|
|
36059
|
+
"join",
|
|
36060
|
+
"join:*",
|
|
36061
|
+
"comm",
|
|
36062
|
+
"comm:*",
|
|
36063
|
+
"diff",
|
|
36064
|
+
"diff:*",
|
|
36065
|
+
"cmp",
|
|
36066
|
+
"cmp:*",
|
|
36067
|
+
"patch:--dry-run:*",
|
|
36068
|
+
// Hashing and encoding (read-only)
|
|
36069
|
+
"md5sum",
|
|
36070
|
+
"md5sum:*",
|
|
36071
|
+
"sha1sum",
|
|
36072
|
+
"sha1sum:*",
|
|
36073
|
+
"sha256sum",
|
|
36074
|
+
"sha256sum:*",
|
|
36075
|
+
"base64",
|
|
36076
|
+
"base64:-d",
|
|
36077
|
+
"od",
|
|
36078
|
+
"od:*",
|
|
36079
|
+
"hexdump",
|
|
36080
|
+
"hexdump:*",
|
|
36081
|
+
// Archive and compression (list/view only)
|
|
36082
|
+
"tar:-tf:*",
|
|
36083
|
+
"tar:-tzf:*",
|
|
36084
|
+
"unzip:-l:*",
|
|
36085
|
+
"zip:-l:*",
|
|
36086
|
+
"gzip:-l:*",
|
|
36087
|
+
"gunzip:-l:*",
|
|
36088
|
+
// Help and documentation
|
|
36089
|
+
"man",
|
|
36090
|
+
"man:*",
|
|
36091
|
+
"--help",
|
|
36092
|
+
"help",
|
|
36093
|
+
"info",
|
|
36094
|
+
"info:*",
|
|
36095
|
+
"whatis",
|
|
36096
|
+
"whatis:*",
|
|
36097
|
+
"apropos",
|
|
36098
|
+
"apropos:*",
|
|
36099
|
+
// Make (dry run and info)
|
|
36100
|
+
"make:-n",
|
|
36101
|
+
"make:--dry-run",
|
|
36102
|
+
"make:-p",
|
|
36103
|
+
"make:--print-data-base",
|
|
36104
|
+
// Docker (read-only operations)
|
|
36105
|
+
"docker:ps",
|
|
36106
|
+
"docker:images",
|
|
36107
|
+
"docker:version",
|
|
36108
|
+
"docker:info",
|
|
36109
|
+
"docker:logs:*",
|
|
36110
|
+
"docker:inspect:*",
|
|
36111
|
+
// Test runners (list/info only)
|
|
36112
|
+
"jest:--listTests",
|
|
36113
|
+
"mocha:--help",
|
|
36114
|
+
"pytest:--collect-only"
|
|
36115
|
+
];
|
|
36116
|
+
DEFAULT_DENY_PATTERNS = [
|
|
36117
|
+
// Dangerous file operations
|
|
36118
|
+
"rm:-rf",
|
|
36119
|
+
"rm:-f:/",
|
|
36120
|
+
"rm:/",
|
|
36121
|
+
"rm:-rf:*",
|
|
36122
|
+
"rmdir",
|
|
36123
|
+
"chmod:777",
|
|
36124
|
+
"chmod:-R:777",
|
|
36125
|
+
"chown",
|
|
36126
|
+
"chgrp",
|
|
36127
|
+
"dd",
|
|
36128
|
+
"dd:*",
|
|
36129
|
+
"shred",
|
|
36130
|
+
"shred:*",
|
|
36131
|
+
// Dangerous find operations that can execute arbitrary commands
|
|
36132
|
+
"find:-exec:*",
|
|
36133
|
+
"find:*:-exec:*",
|
|
36134
|
+
"find:-execdir:*",
|
|
36135
|
+
"find:*:-execdir:*",
|
|
36136
|
+
"find:-ok:*",
|
|
36137
|
+
"find:*:-ok:*",
|
|
36138
|
+
"find:-okdir:*",
|
|
36139
|
+
"find:*:-okdir:*",
|
|
36140
|
+
// Powerful scripting tools that can execute arbitrary commands
|
|
36141
|
+
"awk",
|
|
36142
|
+
"awk:*",
|
|
36143
|
+
"perl",
|
|
36144
|
+
"perl:*",
|
|
36145
|
+
"python:-c:*",
|
|
36146
|
+
"node:-e:*",
|
|
36147
|
+
// System administration and modification
|
|
36148
|
+
"sudo:*",
|
|
36149
|
+
"su",
|
|
36150
|
+
"su:*",
|
|
36151
|
+
"passwd",
|
|
36152
|
+
"adduser",
|
|
36153
|
+
"useradd",
|
|
36154
|
+
"userdel",
|
|
36155
|
+
"usermod",
|
|
36156
|
+
"groupadd",
|
|
36157
|
+
"groupdel",
|
|
36158
|
+
"visudo",
|
|
36159
|
+
// Package installation and removal
|
|
36160
|
+
"npm:install",
|
|
36161
|
+
"npm:i",
|
|
36162
|
+
"npm:uninstall",
|
|
36163
|
+
"npm:publish",
|
|
36164
|
+
"npm:unpublish",
|
|
36165
|
+
"npm:link",
|
|
36166
|
+
"npm:update",
|
|
36167
|
+
"yarn:install",
|
|
36168
|
+
"yarn:add",
|
|
36169
|
+
"yarn:remove",
|
|
36170
|
+
"yarn:upgrade",
|
|
36171
|
+
"pnpm:install",
|
|
36172
|
+
"pnpm:add",
|
|
36173
|
+
"pnpm:remove",
|
|
36174
|
+
"pip:install",
|
|
36175
|
+
"pip:uninstall",
|
|
36176
|
+
"pip:upgrade",
|
|
36177
|
+
"pip3:install",
|
|
36178
|
+
"pip3:uninstall",
|
|
36179
|
+
"pip3:upgrade",
|
|
36180
|
+
"gem:install",
|
|
36181
|
+
"gem:uninstall",
|
|
36182
|
+
"gem:update",
|
|
36183
|
+
"bundle:install",
|
|
36184
|
+
"bundle:update",
|
|
36185
|
+
"composer:install",
|
|
36186
|
+
"composer:update",
|
|
36187
|
+
"composer:remove",
|
|
36188
|
+
"apt:*",
|
|
36189
|
+
"apt-get:*",
|
|
36190
|
+
"yum:*",
|
|
36191
|
+
"dnf:*",
|
|
36192
|
+
"zypper:*",
|
|
36193
|
+
"brew:install",
|
|
36194
|
+
"brew:uninstall",
|
|
36195
|
+
"brew:upgrade",
|
|
36196
|
+
"conda:install",
|
|
36197
|
+
"conda:remove",
|
|
36198
|
+
"conda:update",
|
|
36199
|
+
// Service and system control
|
|
36200
|
+
"systemctl:*",
|
|
36201
|
+
"service:*",
|
|
36202
|
+
"chkconfig:*",
|
|
36203
|
+
"initctl:*",
|
|
36204
|
+
"upstart:*",
|
|
36205
|
+
// Network operations that could be dangerous
|
|
36206
|
+
"curl:-d:*",
|
|
36207
|
+
"curl:--data:*",
|
|
36208
|
+
"curl:-X:POST:*",
|
|
36209
|
+
"curl:-X:PUT:*",
|
|
36210
|
+
"wget:-O:/",
|
|
36211
|
+
"wget:--post-data:*",
|
|
36212
|
+
"ssh",
|
|
36213
|
+
"ssh:*",
|
|
36214
|
+
"scp",
|
|
36215
|
+
"scp:*",
|
|
36216
|
+
"sftp",
|
|
36217
|
+
"sftp:*",
|
|
36218
|
+
"rsync:*",
|
|
36219
|
+
"nc",
|
|
36220
|
+
"nc:*",
|
|
36221
|
+
"netcat",
|
|
36222
|
+
"netcat:*",
|
|
36223
|
+
"telnet",
|
|
36224
|
+
"telnet:*",
|
|
36225
|
+
"ftp",
|
|
36226
|
+
"ftp:*",
|
|
36227
|
+
// Process control and termination
|
|
36228
|
+
"kill",
|
|
36229
|
+
"kill:*",
|
|
36230
|
+
"killall",
|
|
36231
|
+
"killall:*",
|
|
36232
|
+
"pkill",
|
|
36233
|
+
"pkill:*",
|
|
36234
|
+
"nohup:*",
|
|
36235
|
+
"disown:*",
|
|
36236
|
+
// System control and shutdown
|
|
36237
|
+
"shutdown",
|
|
36238
|
+
"shutdown:*",
|
|
36239
|
+
"reboot",
|
|
36240
|
+
"halt",
|
|
36241
|
+
"poweroff",
|
|
36242
|
+
"init",
|
|
36243
|
+
"telinit",
|
|
36244
|
+
// Kernel and module operations
|
|
36245
|
+
"insmod",
|
|
36246
|
+
"insmod:*",
|
|
36247
|
+
"rmmod",
|
|
36248
|
+
"rmmod:*",
|
|
36249
|
+
"modprobe",
|
|
36250
|
+
"modprobe:*",
|
|
36251
|
+
"sysctl:-w:*",
|
|
36252
|
+
// Dangerous git operations
|
|
36253
|
+
"git:push",
|
|
36254
|
+
"git:push:*",
|
|
36255
|
+
"git:force",
|
|
36256
|
+
"git:reset:--hard:*",
|
|
36257
|
+
"git:clean:-fd",
|
|
36258
|
+
"git:rm:*",
|
|
36259
|
+
"git:commit",
|
|
36260
|
+
"git:merge",
|
|
36261
|
+
"git:rebase",
|
|
36262
|
+
"git:cherry-pick",
|
|
36263
|
+
"git:stash:drop",
|
|
36264
|
+
// File system mounting and partitioning
|
|
36265
|
+
"mount",
|
|
36266
|
+
"mount:*",
|
|
36267
|
+
"umount",
|
|
36268
|
+
"umount:*",
|
|
36269
|
+
"fdisk",
|
|
36270
|
+
"fdisk:*",
|
|
36271
|
+
"parted",
|
|
36272
|
+
"parted:*",
|
|
36273
|
+
"mkfs",
|
|
36274
|
+
"mkfs:*",
|
|
36275
|
+
"fsck",
|
|
36276
|
+
"fsck:*",
|
|
36277
|
+
// Cron and scheduling
|
|
36278
|
+
"crontab",
|
|
36279
|
+
"crontab:*",
|
|
36280
|
+
"at",
|
|
36281
|
+
"at:*",
|
|
36282
|
+
"batch",
|
|
36283
|
+
"batch:*",
|
|
36284
|
+
// Compression with potential overwrite
|
|
36285
|
+
"tar:-xf:*",
|
|
36286
|
+
"unzip",
|
|
36287
|
+
"unzip:*",
|
|
36288
|
+
"gzip:*",
|
|
36289
|
+
"gunzip:*",
|
|
36290
|
+
// Build and compilation that might modify files
|
|
36291
|
+
"make",
|
|
36292
|
+
"make:install",
|
|
36293
|
+
"make:clean",
|
|
36294
|
+
"cargo:build",
|
|
36295
|
+
"cargo:install",
|
|
36296
|
+
"npm:run:build",
|
|
36297
|
+
"yarn:build",
|
|
36298
|
+
"mvn:install",
|
|
36299
|
+
"gradle:build",
|
|
36300
|
+
// Docker operations that could modify state
|
|
36301
|
+
"docker:run",
|
|
36302
|
+
"docker:run:*",
|
|
36303
|
+
"docker:exec",
|
|
36304
|
+
"docker:exec:*",
|
|
36305
|
+
"docker:build",
|
|
36306
|
+
"docker:build:*",
|
|
36307
|
+
"docker:pull",
|
|
36308
|
+
"docker:push",
|
|
36309
|
+
"docker:rm",
|
|
36310
|
+
"docker:rmi",
|
|
36311
|
+
"docker:stop",
|
|
36312
|
+
"docker:start",
|
|
36313
|
+
// Database operations
|
|
36314
|
+
"mysql:-e:DROP",
|
|
36315
|
+
"psql:-c:DROP",
|
|
36316
|
+
"redis-cli:FLUSHALL",
|
|
36317
|
+
"mongo:--eval:*",
|
|
36318
|
+
// Text editors that could modify files
|
|
36319
|
+
"vi",
|
|
36320
|
+
"vi:*",
|
|
36321
|
+
"vim",
|
|
36322
|
+
"vim:*",
|
|
36323
|
+
"nano",
|
|
36324
|
+
"nano:*",
|
|
36325
|
+
"emacs",
|
|
36326
|
+
"emacs:*",
|
|
36327
|
+
"sed:-i:*",
|
|
36328
|
+
"perl:-i:*",
|
|
36329
|
+
// Potentially dangerous utilities
|
|
36330
|
+
"eval",
|
|
36331
|
+
"eval:*",
|
|
36332
|
+
"exec",
|
|
36333
|
+
"exec:*",
|
|
36334
|
+
"source",
|
|
36335
|
+
"source:*",
|
|
36336
|
+
"bash:-c:*",
|
|
36337
|
+
"sh:-c:*",
|
|
36338
|
+
"zsh:-c:*"
|
|
36339
|
+
];
|
|
36603
36340
|
}
|
|
36604
|
-
|
|
36605
|
-
|
|
36606
|
-
|
|
36607
|
-
|
|
36608
|
-
|
|
36609
|
-
|
|
36610
|
-
|
|
36611
|
-
|
|
36612
|
-
|
|
36613
|
-
|
|
36614
|
-
|
|
36615
|
-
|
|
36616
|
-
|
|
36341
|
+
});
|
|
36342
|
+
|
|
36343
|
+
// src/agent/bashCommandUtils.js
|
|
36344
|
+
function parseSimpleCommand(command) {
|
|
36345
|
+
if (!command || typeof command !== "string") {
|
|
36346
|
+
return {
|
|
36347
|
+
success: false,
|
|
36348
|
+
error: "Command must be a non-empty string",
|
|
36349
|
+
command: null,
|
|
36350
|
+
args: [],
|
|
36351
|
+
isComplex: false
|
|
36352
|
+
};
|
|
36353
|
+
}
|
|
36354
|
+
const trimmed = command.trim();
|
|
36355
|
+
if (!trimmed) {
|
|
36356
|
+
return {
|
|
36357
|
+
success: false,
|
|
36358
|
+
error: "Command cannot be empty",
|
|
36359
|
+
command: null,
|
|
36360
|
+
args: [],
|
|
36361
|
+
isComplex: false
|
|
36362
|
+
};
|
|
36363
|
+
}
|
|
36364
|
+
const complexPatterns = [
|
|
36365
|
+
/\|/,
|
|
36366
|
+
// Pipes
|
|
36367
|
+
/&&/,
|
|
36368
|
+
// Logical AND
|
|
36369
|
+
/\|\|/,
|
|
36370
|
+
// Logical OR
|
|
36371
|
+
/(?<!\\);/,
|
|
36372
|
+
// Command separator (but not escaped \;)
|
|
36373
|
+
/&$/,
|
|
36374
|
+
// Background execution
|
|
36375
|
+
/\$\(/,
|
|
36376
|
+
// Command substitution $()
|
|
36377
|
+
/`/,
|
|
36378
|
+
// Command substitution ``
|
|
36379
|
+
/>/,
|
|
36380
|
+
// Redirection >
|
|
36381
|
+
/</,
|
|
36382
|
+
// Redirection <
|
|
36383
|
+
/\*\*/,
|
|
36384
|
+
// Glob patterns (potentially dangerous)
|
|
36385
|
+
/^\s*\{.*,.*\}|\{.*\.\.\.*\}/
|
|
36386
|
+
// Brace expansion like {a,b} or {1..10} (but not find {} placeholders)
|
|
36387
|
+
];
|
|
36388
|
+
for (const pattern of complexPatterns) {
|
|
36389
|
+
if (pattern.test(trimmed)) {
|
|
36390
|
+
return {
|
|
36391
|
+
success: false,
|
|
36392
|
+
error: "Complex shell commands with pipes, operators, or redirections are not supported for security reasons",
|
|
36393
|
+
command: null,
|
|
36394
|
+
args: [],
|
|
36395
|
+
isComplex: true,
|
|
36396
|
+
detected: pattern.toString()
|
|
36397
|
+
};
|
|
36617
36398
|
}
|
|
36618
|
-
output += "\n";
|
|
36619
36399
|
}
|
|
36620
|
-
|
|
36621
|
-
|
|
36622
|
-
|
|
36400
|
+
const args = [];
|
|
36401
|
+
let current = "";
|
|
36402
|
+
let inQuotes = false;
|
|
36403
|
+
let quoteChar = "";
|
|
36404
|
+
let escaped = false;
|
|
36405
|
+
for (let i4 = 0; i4 < trimmed.length; i4++) {
|
|
36406
|
+
const char = trimmed[i4];
|
|
36407
|
+
const nextChar = i4 + 1 < trimmed.length ? trimmed[i4 + 1] : "";
|
|
36408
|
+
if (escaped) {
|
|
36409
|
+
current += char;
|
|
36410
|
+
escaped = false;
|
|
36411
|
+
continue;
|
|
36623
36412
|
}
|
|
36624
|
-
|
|
36625
|
-
|
|
36626
|
-
|
|
36413
|
+
if (char === "\\" && !inQuotes) {
|
|
36414
|
+
escaped = true;
|
|
36415
|
+
continue;
|
|
36627
36416
|
}
|
|
36628
|
-
|
|
36629
|
-
|
|
36630
|
-
|
|
36631
|
-
|
|
36632
|
-
|
|
36633
|
-
|
|
36634
|
-
|
|
36417
|
+
if (!inQuotes && (char === '"' || char === "'")) {
|
|
36418
|
+
inQuotes = true;
|
|
36419
|
+
quoteChar = char;
|
|
36420
|
+
} else if (inQuotes && char === quoteChar) {
|
|
36421
|
+
inQuotes = false;
|
|
36422
|
+
quoteChar = "";
|
|
36423
|
+
} else if (!inQuotes && char === " ") {
|
|
36424
|
+
if (current.trim()) {
|
|
36425
|
+
args.push(current.trim());
|
|
36426
|
+
current = "";
|
|
36427
|
+
}
|
|
36428
|
+
} else {
|
|
36429
|
+
current += char;
|
|
36635
36430
|
}
|
|
36636
|
-
output += result.stderr;
|
|
36637
36431
|
}
|
|
36638
|
-
if (
|
|
36639
|
-
|
|
36640
|
-
output += `Error: ${result.error}`;
|
|
36432
|
+
if (current.trim()) {
|
|
36433
|
+
args.push(current.trim());
|
|
36641
36434
|
}
|
|
36642
|
-
if (
|
|
36643
|
-
|
|
36644
|
-
|
|
36435
|
+
if (inQuotes) {
|
|
36436
|
+
return {
|
|
36437
|
+
success: false,
|
|
36438
|
+
error: `Unclosed quote in command: ${quoteChar}`,
|
|
36439
|
+
command: null,
|
|
36440
|
+
args: [],
|
|
36441
|
+
isComplex: false
|
|
36442
|
+
};
|
|
36443
|
+
}
|
|
36444
|
+
if (args.length === 0) {
|
|
36445
|
+
return {
|
|
36446
|
+
success: false,
|
|
36447
|
+
error: "No command found after parsing",
|
|
36448
|
+
command: null,
|
|
36449
|
+
args: [],
|
|
36450
|
+
isComplex: false
|
|
36451
|
+
};
|
|
36452
|
+
}
|
|
36453
|
+
const [baseCommand, ...commandArgs] = args;
|
|
36454
|
+
return {
|
|
36455
|
+
success: true,
|
|
36456
|
+
error: null,
|
|
36457
|
+
command: baseCommand,
|
|
36458
|
+
args: commandArgs,
|
|
36459
|
+
fullArgs: args,
|
|
36460
|
+
isComplex: false,
|
|
36461
|
+
original: command
|
|
36462
|
+
};
|
|
36463
|
+
}
|
|
36464
|
+
function isComplexCommand(command) {
|
|
36465
|
+
const result = parseSimpleCommand(command);
|
|
36466
|
+
return result.isComplex;
|
|
36467
|
+
}
|
|
36468
|
+
function isComplexPattern(pattern) {
|
|
36469
|
+
if (!pattern || typeof pattern !== "string") return false;
|
|
36470
|
+
const operatorPatterns = [
|
|
36471
|
+
/\|/,
|
|
36472
|
+
// Pipes
|
|
36473
|
+
/&&/,
|
|
36474
|
+
// Logical AND
|
|
36475
|
+
/\|\|/,
|
|
36476
|
+
// Logical OR
|
|
36477
|
+
/;/,
|
|
36478
|
+
// Command separator
|
|
36479
|
+
/&$/,
|
|
36480
|
+
// Background execution
|
|
36481
|
+
/\$\(/,
|
|
36482
|
+
// Command substitution $()
|
|
36483
|
+
/`/,
|
|
36484
|
+
// Command substitution ``
|
|
36485
|
+
/>/,
|
|
36486
|
+
// Redirection >
|
|
36487
|
+
/</
|
|
36488
|
+
// Redirection <
|
|
36489
|
+
];
|
|
36490
|
+
return operatorPatterns.some((p4) => p4.test(pattern));
|
|
36491
|
+
}
|
|
36492
|
+
function globToRegex(pattern) {
|
|
36493
|
+
let escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
36494
|
+
escaped = escaped.replace(/\*/g, ".*?");
|
|
36495
|
+
return new RegExp("^" + escaped + "$", "i");
|
|
36496
|
+
}
|
|
36497
|
+
function matchesComplexPattern(command, pattern) {
|
|
36498
|
+
if (!command || !pattern) return false;
|
|
36499
|
+
const normalizedCommand = command.trim().replace(/\s+/g, " ");
|
|
36500
|
+
const normalizedPattern = pattern.trim().replace(/\s+/g, " ");
|
|
36501
|
+
try {
|
|
36502
|
+
const regex = globToRegex(normalizedPattern);
|
|
36503
|
+
return regex.test(normalizedCommand);
|
|
36504
|
+
} catch (e4) {
|
|
36505
|
+
return normalizedCommand === normalizedPattern;
|
|
36645
36506
|
}
|
|
36646
|
-
return output || (result.success ? "Command completed successfully (no output)" : "Command failed (no output)");
|
|
36647
36507
|
}
|
|
36648
|
-
function
|
|
36649
|
-
const
|
|
36650
|
-
|
|
36651
|
-
|
|
36652
|
-
|
|
36653
|
-
|
|
36654
|
-
|
|
36655
|
-
|
|
36656
|
-
}
|
|
36657
|
-
}
|
|
36658
|
-
if (options.maxBuffer !== void 0) {
|
|
36659
|
-
if (typeof options.maxBuffer !== "number" || options.maxBuffer < 1024) {
|
|
36660
|
-
errors.push("maxBuffer must be at least 1024 bytes");
|
|
36661
|
-
} else if (options.maxBuffer > 100 * 1024 * 1024) {
|
|
36662
|
-
warnings.push("maxBuffer is very high (>100MB)");
|
|
36663
|
-
}
|
|
36664
|
-
}
|
|
36665
|
-
if (options.workingDirectory) {
|
|
36666
|
-
if (typeof options.workingDirectory !== "string") {
|
|
36667
|
-
errors.push("workingDirectory must be a string");
|
|
36668
|
-
} else if (!(0, import_fs4.existsSync)(options.workingDirectory)) {
|
|
36669
|
-
errors.push(`workingDirectory does not exist: ${options.workingDirectory}`);
|
|
36670
|
-
}
|
|
36671
|
-
}
|
|
36672
|
-
if (options.env && typeof options.env !== "object") {
|
|
36673
|
-
errors.push("env must be an object");
|
|
36508
|
+
function parseCommand(command) {
|
|
36509
|
+
const result = parseSimpleCommand(command);
|
|
36510
|
+
if (!result.success) {
|
|
36511
|
+
return {
|
|
36512
|
+
command: "",
|
|
36513
|
+
args: [],
|
|
36514
|
+
error: result.error,
|
|
36515
|
+
isComplex: result.isComplex
|
|
36516
|
+
};
|
|
36674
36517
|
}
|
|
36675
36518
|
return {
|
|
36676
|
-
|
|
36677
|
-
|
|
36678
|
-
|
|
36519
|
+
command: result.command,
|
|
36520
|
+
args: result.args,
|
|
36521
|
+
error: null,
|
|
36522
|
+
isComplex: result.isComplex
|
|
36679
36523
|
};
|
|
36680
36524
|
}
|
|
36681
|
-
|
|
36682
|
-
|
|
36683
|
-
|
|
36525
|
+
function parseCommandForExecution(command) {
|
|
36526
|
+
const result = parseSimpleCommand(command);
|
|
36527
|
+
if (!result.success) {
|
|
36528
|
+
return null;
|
|
36529
|
+
}
|
|
36530
|
+
return result.fullArgs;
|
|
36531
|
+
}
|
|
36532
|
+
var init_bashCommandUtils = __esm({
|
|
36533
|
+
"src/agent/bashCommandUtils.js"() {
|
|
36684
36534
|
"use strict";
|
|
36685
|
-
import_child_process6 = require("child_process");
|
|
36686
|
-
import_path6 = require("path");
|
|
36687
|
-
import_fs4 = require("fs");
|
|
36688
|
-
init_bashCommandUtils();
|
|
36689
36535
|
}
|
|
36690
36536
|
});
|
|
36691
36537
|
|
|
36692
|
-
// src/
|
|
36693
|
-
|
|
36694
|
-
|
|
36695
|
-
|
|
36538
|
+
// src/agent/bashPermissions.js
|
|
36539
|
+
function matchesPattern(parsedCommand, pattern) {
|
|
36540
|
+
if (!parsedCommand || !pattern) return false;
|
|
36541
|
+
const { command, args } = parsedCommand;
|
|
36542
|
+
if (!command) return false;
|
|
36543
|
+
const patternParts = pattern.split(":");
|
|
36544
|
+
const commandName = patternParts[0];
|
|
36545
|
+
if (commandName === "*") {
|
|
36546
|
+
return true;
|
|
36547
|
+
} else if (commandName !== command) {
|
|
36548
|
+
return false;
|
|
36549
|
+
}
|
|
36550
|
+
if (patternParts.length === 1) {
|
|
36551
|
+
return true;
|
|
36552
|
+
}
|
|
36553
|
+
for (let i4 = 1; i4 < patternParts.length; i4++) {
|
|
36554
|
+
const patternArg = patternParts[i4];
|
|
36555
|
+
const argIndex = i4 - 1;
|
|
36556
|
+
if (patternArg === "*") {
|
|
36557
|
+
continue;
|
|
36558
|
+
}
|
|
36559
|
+
if (argIndex >= args.length) {
|
|
36560
|
+
return false;
|
|
36561
|
+
}
|
|
36562
|
+
const actualArg = args[argIndex];
|
|
36563
|
+
if (patternArg !== actualArg) {
|
|
36564
|
+
return false;
|
|
36565
|
+
}
|
|
36566
|
+
}
|
|
36567
|
+
return true;
|
|
36568
|
+
}
|
|
36569
|
+
function matchesAnyPattern(parsedCommand, patterns) {
|
|
36570
|
+
if (!patterns || patterns.length === 0) return false;
|
|
36571
|
+
return patterns.some((pattern) => matchesPattern(parsedCommand, pattern));
|
|
36572
|
+
}
|
|
36573
|
+
var BashPermissionChecker;
|
|
36574
|
+
var init_bashPermissions = __esm({
|
|
36575
|
+
"src/agent/bashPermissions.js"() {
|
|
36696
36576
|
"use strict";
|
|
36697
|
-
|
|
36698
|
-
|
|
36699
|
-
|
|
36700
|
-
|
|
36701
|
-
|
|
36702
|
-
|
|
36703
|
-
|
|
36704
|
-
|
|
36705
|
-
|
|
36706
|
-
|
|
36707
|
-
|
|
36708
|
-
|
|
36709
|
-
|
|
36710
|
-
|
|
36711
|
-
|
|
36712
|
-
|
|
36713
|
-
|
|
36714
|
-
|
|
36715
|
-
|
|
36716
|
-
|
|
36717
|
-
return bashConfig.workingDirectory;
|
|
36577
|
+
init_bashDefaults();
|
|
36578
|
+
init_bashCommandUtils();
|
|
36579
|
+
BashPermissionChecker = class {
|
|
36580
|
+
/**
|
|
36581
|
+
* Create a permission checker
|
|
36582
|
+
* @param {Object} config - Configuration options
|
|
36583
|
+
* @param {string[]} [config.allow] - Additional allow patterns
|
|
36584
|
+
* @param {string[]} [config.deny] - Additional deny patterns
|
|
36585
|
+
* @param {boolean} [config.disableDefaultAllow] - Disable default allow list
|
|
36586
|
+
* @param {boolean} [config.disableDefaultDeny] - Disable default deny list
|
|
36587
|
+
* @param {boolean} [config.debug] - Enable debug logging
|
|
36588
|
+
*/
|
|
36589
|
+
constructor(config = {}) {
|
|
36590
|
+
this.debug = config.debug || false;
|
|
36591
|
+
this.allowPatterns = [];
|
|
36592
|
+
if (!config.disableDefaultAllow) {
|
|
36593
|
+
this.allowPatterns.push(...DEFAULT_ALLOW_PATTERNS);
|
|
36594
|
+
if (this.debug) {
|
|
36595
|
+
console.log(`[BashPermissions] Added ${DEFAULT_ALLOW_PATTERNS.length} default allow patterns`);
|
|
36596
|
+
}
|
|
36718
36597
|
}
|
|
36719
|
-
if (
|
|
36720
|
-
|
|
36598
|
+
if (config.allow && Array.isArray(config.allow)) {
|
|
36599
|
+
this.allowPatterns.push(...config.allow);
|
|
36600
|
+
if (this.debug) {
|
|
36601
|
+
console.log(`[BashPermissions] Added ${config.allow.length} custom allow patterns:`, config.allow);
|
|
36602
|
+
}
|
|
36721
36603
|
}
|
|
36722
|
-
|
|
36723
|
-
|
|
36604
|
+
this.denyPatterns = [];
|
|
36605
|
+
if (!config.disableDefaultDeny) {
|
|
36606
|
+
this.denyPatterns.push(...DEFAULT_DENY_PATTERNS);
|
|
36607
|
+
if (this.debug) {
|
|
36608
|
+
console.log(`[BashPermissions] Added ${DEFAULT_DENY_PATTERNS.length} default deny patterns`);
|
|
36609
|
+
}
|
|
36724
36610
|
}
|
|
36725
|
-
|
|
36726
|
-
|
|
36727
|
-
|
|
36728
|
-
|
|
36729
|
-
|
|
36730
|
-
|
|
36731
|
-
|
|
36732
|
-
|
|
36733
|
-
|
|
36734
|
-
|
|
36735
|
-
|
|
36736
|
-
|
|
36737
|
-
|
|
36738
|
-
|
|
36739
|
-
|
|
36740
|
-
|
|
36741
|
-
|
|
36742
|
-
|
|
36743
|
-
|
|
36744
|
-
|
|
36745
|
-
|
|
36746
|
-
|
|
36747
|
-
|
|
36748
|
-
|
|
36749
|
-
|
|
36750
|
-
|
|
36751
|
-
|
|
36752
|
-
|
|
36753
|
-
|
|
36754
|
-
|
|
36755
|
-
|
|
36756
|
-
|
|
36757
|
-
|
|
36758
|
-
|
|
36759
|
-
|
|
36760
|
-
|
|
36761
|
-
|
|
36762
|
-
|
|
36763
|
-
|
|
36764
|
-
|
|
36765
|
-
|
|
36766
|
-
|
|
36767
|
-
|
|
36768
|
-
|
|
36769
|
-
|
|
36770
|
-
|
|
36771
|
-
|
|
36772
|
-
|
|
36773
|
-
|
|
36774
|
-
|
|
36775
|
-
|
|
36776
|
-
|
|
36777
|
-
|
|
36778
|
-
|
|
36779
|
-
|
|
36780
|
-
|
|
36781
|
-
|
|
36782
|
-
|
|
36783
|
-
|
|
36784
|
-
|
|
36785
|
-
|
|
36786
|
-
|
|
36787
|
-
|
|
36788
|
-
return `Permission denied: ${permissionResult.reason}
|
|
36789
|
-
|
|
36790
|
-
This command is not allowed by the current security policy.
|
|
36791
|
-
|
|
36792
|
-
Common reasons:
|
|
36793
|
-
1. The command is in the deny list (potentially dangerous)
|
|
36794
|
-
2. The command is not in the allow list (not a recognized safe command)
|
|
36795
|
-
|
|
36796
|
-
If you believe this command should be allowed, you can:
|
|
36797
|
-
- Use the --bash-allow option to add specific patterns
|
|
36798
|
-
- Use the --no-default-bash-deny flag to remove default restrictions (not recommended)
|
|
36799
|
-
|
|
36800
|
-
For code exploration, try these safe alternatives:
|
|
36801
|
-
- ls, cat, head, tail for file operations
|
|
36802
|
-
- find, grep, rg for searching
|
|
36803
|
-
- git status, git log, git show for git operations
|
|
36804
|
-
- npm list, pip list for package information`;
|
|
36805
|
-
}
|
|
36806
|
-
const defaultDir = getDefaultWorkingDirectory();
|
|
36807
|
-
const workingDir = workingDirectory ? (0, import_path7.isAbsolute)(workingDirectory) ? (0, import_path7.resolve)(workingDirectory) : (0, import_path7.resolve)(defaultDir, workingDirectory) : defaultDir;
|
|
36808
|
-
if (allowedFolders && allowedFolders.length > 0) {
|
|
36809
|
-
const resolvedWorkingDir = (0, import_path7.resolve)(workingDir);
|
|
36810
|
-
const isAllowed = allowedFolders.some((folder) => {
|
|
36811
|
-
const resolvedFolder = (0, import_path7.resolve)(folder);
|
|
36812
|
-
return resolvedWorkingDir === resolvedFolder || resolvedWorkingDir.startsWith(resolvedFolder + import_path7.sep);
|
|
36813
|
-
});
|
|
36814
|
-
if (!isAllowed) {
|
|
36815
|
-
return `Error: Working directory "${workingDir}" is not within allowed folders: ${allowedFolders.join(", ")}`;
|
|
36816
|
-
}
|
|
36817
|
-
}
|
|
36818
|
-
const executionOptions = {
|
|
36819
|
-
workingDirectory: workingDir,
|
|
36820
|
-
timeout: timeout || bashConfig.timeout || 12e4,
|
|
36821
|
-
env: { ...bashConfig.env, ...env },
|
|
36822
|
-
maxBuffer: bashConfig.maxBuffer,
|
|
36823
|
-
debug
|
|
36611
|
+
if (config.deny && Array.isArray(config.deny)) {
|
|
36612
|
+
this.denyPatterns.push(...config.deny);
|
|
36613
|
+
if (this.debug) {
|
|
36614
|
+
console.log(`[BashPermissions] Added ${config.deny.length} custom deny patterns:`, config.deny);
|
|
36615
|
+
}
|
|
36616
|
+
}
|
|
36617
|
+
if (this.debug) {
|
|
36618
|
+
console.log(`[BashPermissions] Total patterns - Allow: ${this.allowPatterns.length}, Deny: ${this.denyPatterns.length}`);
|
|
36619
|
+
}
|
|
36620
|
+
}
|
|
36621
|
+
/**
|
|
36622
|
+
* Check if a simple command is allowed (complex commands allowed if they match patterns)
|
|
36623
|
+
* @param {string} command - Command to check
|
|
36624
|
+
* @returns {Object} Permission result
|
|
36625
|
+
*/
|
|
36626
|
+
check(command) {
|
|
36627
|
+
if (!command || typeof command !== "string") {
|
|
36628
|
+
return {
|
|
36629
|
+
allowed: false,
|
|
36630
|
+
reason: "Invalid or empty command",
|
|
36631
|
+
command
|
|
36632
|
+
};
|
|
36633
|
+
}
|
|
36634
|
+
const commandIsComplex = isComplexCommand(command);
|
|
36635
|
+
if (commandIsComplex) {
|
|
36636
|
+
return this._checkComplexCommand(command);
|
|
36637
|
+
}
|
|
36638
|
+
const parsed = parseCommand(command);
|
|
36639
|
+
if (parsed.error) {
|
|
36640
|
+
return {
|
|
36641
|
+
allowed: false,
|
|
36642
|
+
reason: parsed.error,
|
|
36643
|
+
command
|
|
36644
|
+
};
|
|
36645
|
+
}
|
|
36646
|
+
if (!parsed.command) {
|
|
36647
|
+
return {
|
|
36648
|
+
allowed: false,
|
|
36649
|
+
reason: "No valid command found",
|
|
36650
|
+
command
|
|
36651
|
+
};
|
|
36652
|
+
}
|
|
36653
|
+
if (this.debug) {
|
|
36654
|
+
console.log(`[BashPermissions] Checking simple command: "${command}"`);
|
|
36655
|
+
console.log(`[BashPermissions] Parsed: ${parsed.command} with args: [${parsed.args.join(", ")}]`);
|
|
36656
|
+
}
|
|
36657
|
+
if (matchesAnyPattern(parsed, this.denyPatterns)) {
|
|
36658
|
+
const matchedPatterns = this.denyPatterns.filter((pattern) => matchesPattern(parsed, pattern));
|
|
36659
|
+
return {
|
|
36660
|
+
allowed: false,
|
|
36661
|
+
reason: `Command matches deny pattern: ${matchedPatterns[0]}`,
|
|
36662
|
+
command,
|
|
36663
|
+
parsed,
|
|
36664
|
+
matchedPatterns
|
|
36665
|
+
};
|
|
36666
|
+
}
|
|
36667
|
+
if (this.allowPatterns.length > 0) {
|
|
36668
|
+
if (!matchesAnyPattern(parsed, this.allowPatterns)) {
|
|
36669
|
+
return {
|
|
36670
|
+
allowed: false,
|
|
36671
|
+
reason: "Command not in allow list",
|
|
36672
|
+
command,
|
|
36673
|
+
parsed
|
|
36824
36674
|
};
|
|
36825
|
-
|
|
36826
|
-
|
|
36827
|
-
|
|
36828
|
-
|
|
36829
|
-
|
|
36830
|
-
|
|
36831
|
-
|
|
36832
|
-
|
|
36833
|
-
|
|
36834
|
-
|
|
36835
|
-
|
|
36675
|
+
}
|
|
36676
|
+
}
|
|
36677
|
+
const result = {
|
|
36678
|
+
allowed: true,
|
|
36679
|
+
command,
|
|
36680
|
+
parsed,
|
|
36681
|
+
isComplex: false
|
|
36682
|
+
};
|
|
36683
|
+
if (this.debug) {
|
|
36684
|
+
console.log(`[BashPermissions] ALLOWED - command passed all checks`);
|
|
36685
|
+
}
|
|
36686
|
+
return result;
|
|
36687
|
+
}
|
|
36688
|
+
/**
|
|
36689
|
+
* Check a complex command against complex patterns in allow/deny lists
|
|
36690
|
+
* @private
|
|
36691
|
+
* @param {string} command - Complex command to check
|
|
36692
|
+
* @returns {Object} Permission result
|
|
36693
|
+
*/
|
|
36694
|
+
_checkComplexCommand(command) {
|
|
36695
|
+
if (this.debug) {
|
|
36696
|
+
console.log(`[BashPermissions] Checking complex command: "${command}"`);
|
|
36697
|
+
}
|
|
36698
|
+
const complexAllowPatterns = this.allowPatterns.filter((p4) => isComplexPattern(p4));
|
|
36699
|
+
const complexDenyPatterns = this.denyPatterns.filter((p4) => isComplexPattern(p4));
|
|
36700
|
+
if (this.debug) {
|
|
36701
|
+
console.log(`[BashPermissions] Complex allow patterns: ${complexAllowPatterns.length}`);
|
|
36702
|
+
console.log(`[BashPermissions] Complex deny patterns: ${complexDenyPatterns.length}`);
|
|
36703
|
+
}
|
|
36704
|
+
for (const pattern of complexDenyPatterns) {
|
|
36705
|
+
if (matchesComplexPattern(command, pattern)) {
|
|
36706
|
+
if (this.debug) {
|
|
36707
|
+
console.log(`[BashPermissions] DENIED - matches complex deny pattern: ${pattern}`);
|
|
36836
36708
|
}
|
|
36837
|
-
|
|
36838
|
-
|
|
36839
|
-
|
|
36709
|
+
return {
|
|
36710
|
+
allowed: false,
|
|
36711
|
+
reason: `Command matches deny pattern: ${pattern}`,
|
|
36712
|
+
command,
|
|
36713
|
+
isComplex: true,
|
|
36714
|
+
matchedPatterns: [pattern]
|
|
36715
|
+
};
|
|
36716
|
+
}
|
|
36717
|
+
}
|
|
36718
|
+
for (const pattern of complexAllowPatterns) {
|
|
36719
|
+
if (matchesComplexPattern(command, pattern)) {
|
|
36720
|
+
if (this.debug) {
|
|
36721
|
+
console.log(`[BashPermissions] ALLOWED - matches complex allow pattern: ${pattern}`);
|
|
36840
36722
|
}
|
|
36841
|
-
|
|
36842
|
-
|
|
36843
|
-
|
|
36723
|
+
return {
|
|
36724
|
+
allowed: true,
|
|
36725
|
+
command,
|
|
36726
|
+
isComplex: true,
|
|
36727
|
+
matchedPattern: pattern
|
|
36728
|
+
};
|
|
36729
|
+
}
|
|
36730
|
+
}
|
|
36731
|
+
if (this.debug) {
|
|
36732
|
+
console.log(`[BashPermissions] DENIED - no matching complex pattern found`);
|
|
36733
|
+
}
|
|
36734
|
+
return {
|
|
36735
|
+
allowed: false,
|
|
36736
|
+
reason: 'Complex shell commands require explicit allow patterns (e.g., "cd * && git *")',
|
|
36737
|
+
command,
|
|
36738
|
+
isComplex: true
|
|
36739
|
+
};
|
|
36740
|
+
}
|
|
36741
|
+
/**
|
|
36742
|
+
* Get configuration summary
|
|
36743
|
+
* @returns {Object} Configuration info
|
|
36744
|
+
*/
|
|
36745
|
+
getConfig() {
|
|
36746
|
+
return {
|
|
36747
|
+
allowPatterns: this.allowPatterns.length,
|
|
36748
|
+
denyPatterns: this.denyPatterns.length,
|
|
36749
|
+
totalPatterns: this.allowPatterns.length + this.denyPatterns.length
|
|
36750
|
+
};
|
|
36751
|
+
}
|
|
36752
|
+
};
|
|
36753
|
+
}
|
|
36754
|
+
});
|
|
36844
36755
|
|
|
36845
|
-
|
|
36846
|
-
|
|
36847
|
-
|
|
36848
|
-
|
|
36849
|
-
|
|
36850
|
-
|
|
36851
|
-
|
|
36852
|
-
|
|
36853
|
-
|
|
36854
|
-
|
|
36756
|
+
// src/agent/bashExecutor.js
|
|
36757
|
+
async function executeBashCommand(command, options = {}) {
|
|
36758
|
+
const {
|
|
36759
|
+
workingDirectory = process.cwd(),
|
|
36760
|
+
timeout = 12e4,
|
|
36761
|
+
// 2 minutes default
|
|
36762
|
+
env = {},
|
|
36763
|
+
maxBuffer = 10 * 1024 * 1024,
|
|
36764
|
+
// 10MB
|
|
36765
|
+
debug = false
|
|
36766
|
+
} = options;
|
|
36767
|
+
let cwd = workingDirectory;
|
|
36768
|
+
try {
|
|
36769
|
+
cwd = (0, import_path7.resolve)(cwd);
|
|
36770
|
+
if (!(0, import_fs6.existsSync)(cwd)) {
|
|
36771
|
+
throw new Error(`Working directory does not exist: ${cwd}`);
|
|
36772
|
+
}
|
|
36773
|
+
} catch (error2) {
|
|
36774
|
+
return {
|
|
36775
|
+
success: false,
|
|
36776
|
+
error: `Invalid working directory: ${error2.message}`,
|
|
36777
|
+
stdout: "",
|
|
36778
|
+
stderr: "",
|
|
36779
|
+
exitCode: 1,
|
|
36780
|
+
command,
|
|
36781
|
+
workingDirectory: cwd,
|
|
36782
|
+
duration: 0
|
|
36783
|
+
};
|
|
36784
|
+
}
|
|
36785
|
+
const startTime = Date.now();
|
|
36786
|
+
if (debug) {
|
|
36787
|
+
console.log(`[BashExecutor] Executing command: "${command}"`);
|
|
36788
|
+
console.log(`[BashExecutor] Working directory: "${cwd}"`);
|
|
36789
|
+
console.log(`[BashExecutor] Timeout: ${timeout}ms`);
|
|
36790
|
+
}
|
|
36791
|
+
return new Promise((resolve6, reject2) => {
|
|
36792
|
+
const processEnv = {
|
|
36793
|
+
...process.env,
|
|
36794
|
+
...env
|
|
36795
|
+
};
|
|
36796
|
+
const isComplex = isComplexCommand(command);
|
|
36797
|
+
let cmd, cmdArgs, useShell;
|
|
36798
|
+
if (isComplex) {
|
|
36799
|
+
cmd = "sh";
|
|
36800
|
+
cmdArgs = ["-c", command];
|
|
36801
|
+
useShell = false;
|
|
36802
|
+
if (debug) {
|
|
36803
|
+
console.log(`[BashExecutor] Complex command - using sh -c`);
|
|
36804
|
+
}
|
|
36805
|
+
} else {
|
|
36806
|
+
const args = parseCommandForExecution(command);
|
|
36807
|
+
if (!args || args.length === 0) {
|
|
36808
|
+
resolve6({
|
|
36809
|
+
success: false,
|
|
36810
|
+
error: "Failed to parse command",
|
|
36811
|
+
stdout: "",
|
|
36812
|
+
stderr: "",
|
|
36813
|
+
exitCode: 1,
|
|
36814
|
+
command,
|
|
36815
|
+
workingDirectory: cwd,
|
|
36816
|
+
duration: Date.now() - startTime
|
|
36817
|
+
});
|
|
36818
|
+
return;
|
|
36819
|
+
}
|
|
36820
|
+
[cmd, ...cmdArgs] = args;
|
|
36821
|
+
useShell = false;
|
|
36822
|
+
}
|
|
36823
|
+
const child = (0, import_child_process6.spawn)(cmd, cmdArgs, {
|
|
36824
|
+
cwd,
|
|
36825
|
+
env: processEnv,
|
|
36826
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
36827
|
+
// stdin ignored, capture stdout/stderr
|
|
36828
|
+
shell: useShell,
|
|
36829
|
+
// false for security
|
|
36830
|
+
windowsHide: true
|
|
36831
|
+
});
|
|
36832
|
+
let stdout = "";
|
|
36833
|
+
let stderr = "";
|
|
36834
|
+
let killed = false;
|
|
36835
|
+
let timeoutHandle;
|
|
36836
|
+
if (timeout > 0) {
|
|
36837
|
+
timeoutHandle = setTimeout(() => {
|
|
36838
|
+
if (!killed) {
|
|
36839
|
+
killed = true;
|
|
36840
|
+
child.kill("SIGTERM");
|
|
36841
|
+
setTimeout(() => {
|
|
36842
|
+
if (child.exitCode === null) {
|
|
36843
|
+
child.kill("SIGKILL");
|
|
36855
36844
|
}
|
|
36856
|
-
|
|
36857
|
-
|
|
36845
|
+
}, 5e3);
|
|
36846
|
+
}
|
|
36847
|
+
}, timeout);
|
|
36848
|
+
}
|
|
36849
|
+
child.stdout.on("data", (data2) => {
|
|
36850
|
+
const chunk = data2.toString();
|
|
36851
|
+
if (stdout.length + chunk.length <= maxBuffer) {
|
|
36852
|
+
stdout += chunk;
|
|
36853
|
+
} else {
|
|
36854
|
+
if (!killed) {
|
|
36855
|
+
killed = true;
|
|
36856
|
+
child.kill("SIGTERM");
|
|
36857
|
+
}
|
|
36858
|
+
}
|
|
36859
|
+
});
|
|
36860
|
+
child.stderr.on("data", (data2) => {
|
|
36861
|
+
const chunk = data2.toString();
|
|
36862
|
+
if (stderr.length + chunk.length <= maxBuffer) {
|
|
36863
|
+
stderr += chunk;
|
|
36864
|
+
} else {
|
|
36865
|
+
if (!killed) {
|
|
36866
|
+
killed = true;
|
|
36867
|
+
child.kill("SIGTERM");
|
|
36868
|
+
}
|
|
36869
|
+
}
|
|
36870
|
+
});
|
|
36871
|
+
child.on("close", (code, signal) => {
|
|
36872
|
+
if (timeoutHandle) {
|
|
36873
|
+
clearTimeout(timeoutHandle);
|
|
36874
|
+
}
|
|
36875
|
+
const duration = Date.now() - startTime;
|
|
36876
|
+
if (debug) {
|
|
36877
|
+
console.log(`[BashExecutor] Command completed - Code: ${code}, Signal: ${signal}, Duration: ${duration}ms`);
|
|
36878
|
+
console.log(`[BashExecutor] Stdout length: ${stdout.length}, Stderr length: ${stderr.length}`);
|
|
36879
|
+
}
|
|
36880
|
+
let success = true;
|
|
36881
|
+
let error2 = "";
|
|
36882
|
+
if (killed) {
|
|
36883
|
+
success = false;
|
|
36884
|
+
if (stdout.length + stderr.length > maxBuffer) {
|
|
36885
|
+
error2 = `Command output exceeded maximum buffer size (${maxBuffer} bytes)`;
|
|
36886
|
+
} else {
|
|
36887
|
+
error2 = `Command timed out after ${timeout}ms`;
|
|
36858
36888
|
}
|
|
36889
|
+
} else if (code !== 0) {
|
|
36890
|
+
success = false;
|
|
36891
|
+
error2 = `Command exited with code ${code}`;
|
|
36892
|
+
}
|
|
36893
|
+
resolve6({
|
|
36894
|
+
success,
|
|
36895
|
+
error: error2,
|
|
36896
|
+
stdout: stdout.trim(),
|
|
36897
|
+
stderr: stderr.trim(),
|
|
36898
|
+
exitCode: code,
|
|
36899
|
+
signal,
|
|
36900
|
+
command,
|
|
36901
|
+
workingDirectory: cwd,
|
|
36902
|
+
duration,
|
|
36903
|
+
killed
|
|
36859
36904
|
});
|
|
36860
|
-
};
|
|
36905
|
+
});
|
|
36906
|
+
child.on("error", (error2) => {
|
|
36907
|
+
if (timeoutHandle) {
|
|
36908
|
+
clearTimeout(timeoutHandle);
|
|
36909
|
+
}
|
|
36910
|
+
if (debug) {
|
|
36911
|
+
console.log(`[BashExecutor] Spawn error:`, error2);
|
|
36912
|
+
}
|
|
36913
|
+
resolve6({
|
|
36914
|
+
success: false,
|
|
36915
|
+
error: `Failed to execute command: ${error2.message}`,
|
|
36916
|
+
stdout: "",
|
|
36917
|
+
stderr: "",
|
|
36918
|
+
exitCode: 1,
|
|
36919
|
+
command,
|
|
36920
|
+
workingDirectory: cwd,
|
|
36921
|
+
duration: Date.now() - startTime
|
|
36922
|
+
});
|
|
36923
|
+
});
|
|
36924
|
+
});
|
|
36925
|
+
}
|
|
36926
|
+
function formatExecutionResult(result, includeMetadata = false) {
|
|
36927
|
+
if (!result) {
|
|
36928
|
+
return "No result available";
|
|
36861
36929
|
}
|
|
36862
|
-
|
|
36863
|
-
|
|
36864
|
-
|
|
36865
|
-
|
|
36866
|
-
|
|
36867
|
-
|
|
36868
|
-
|
|
36869
|
-
|
|
36930
|
+
let output = "";
|
|
36931
|
+
if (includeMetadata) {
|
|
36932
|
+
output += `Command: ${result.command}
|
|
36933
|
+
`;
|
|
36934
|
+
output += `Working directory: ${result.workingDirectory}
|
|
36935
|
+
`;
|
|
36936
|
+
output += `Duration: ${result.duration}ms
|
|
36937
|
+
`;
|
|
36938
|
+
output += `Exit Code: ${result.exitCode}
|
|
36939
|
+
`;
|
|
36940
|
+
if (result.signal) {
|
|
36941
|
+
output += `Signal: ${result.signal}
|
|
36942
|
+
`;
|
|
36943
|
+
}
|
|
36944
|
+
output += "\n";
|
|
36870
36945
|
}
|
|
36871
|
-
|
|
36872
|
-
|
|
36873
|
-
|
|
36874
|
-
|
|
36875
|
-
|
|
36946
|
+
if (result.stdout) {
|
|
36947
|
+
if (includeMetadata) {
|
|
36948
|
+
output += "--- STDOUT ---\n";
|
|
36949
|
+
}
|
|
36950
|
+
output += result.stdout;
|
|
36951
|
+
if (includeMetadata && result.stderr) {
|
|
36952
|
+
output += "\n";
|
|
36953
|
+
}
|
|
36954
|
+
}
|
|
36955
|
+
if (result.stderr) {
|
|
36956
|
+
if (includeMetadata) {
|
|
36957
|
+
if (result.stdout) output += "\n";
|
|
36958
|
+
output += "--- STDERR ---\n";
|
|
36959
|
+
} else if (result.stdout) {
|
|
36960
|
+
output += "\n--- STDERR ---\n";
|
|
36961
|
+
}
|
|
36962
|
+
output += result.stderr;
|
|
36963
|
+
}
|
|
36964
|
+
if (!result.success && result.error && !result.stderr) {
|
|
36965
|
+
if (output) output += "\n";
|
|
36966
|
+
output += `Error: ${result.error}`;
|
|
36967
|
+
}
|
|
36968
|
+
if (!result.success && result.exitCode !== void 0 && result.exitCode !== 0) {
|
|
36969
|
+
if (output) output += "\n";
|
|
36970
|
+
output += `Exit code: ${result.exitCode}`;
|
|
36971
|
+
}
|
|
36972
|
+
return output || (result.success ? "Command completed successfully (no output)" : "Command failed (no output)");
|
|
36876
36973
|
}
|
|
36877
|
-
function
|
|
36974
|
+
function validateExecutionOptions(options = {}) {
|
|
36975
|
+
const errors = [];
|
|
36976
|
+
const warnings = [];
|
|
36977
|
+
if (options.timeout !== void 0) {
|
|
36978
|
+
if (typeof options.timeout !== "number" || options.timeout < 0) {
|
|
36979
|
+
errors.push("timeout must be a non-negative number");
|
|
36980
|
+
} else if (options.timeout > 6e5) {
|
|
36981
|
+
warnings.push("timeout is very high (>10 minutes)");
|
|
36982
|
+
}
|
|
36983
|
+
}
|
|
36984
|
+
if (options.maxBuffer !== void 0) {
|
|
36985
|
+
if (typeof options.maxBuffer !== "number" || options.maxBuffer < 1024) {
|
|
36986
|
+
errors.push("maxBuffer must be at least 1024 bytes");
|
|
36987
|
+
} else if (options.maxBuffer > 100 * 1024 * 1024) {
|
|
36988
|
+
warnings.push("maxBuffer is very high (>100MB)");
|
|
36989
|
+
}
|
|
36990
|
+
}
|
|
36991
|
+
if (options.workingDirectory) {
|
|
36992
|
+
if (typeof options.workingDirectory !== "string") {
|
|
36993
|
+
errors.push("workingDirectory must be a string");
|
|
36994
|
+
} else if (!(0, import_fs6.existsSync)(options.workingDirectory)) {
|
|
36995
|
+
errors.push(`workingDirectory does not exist: ${options.workingDirectory}`);
|
|
36996
|
+
}
|
|
36997
|
+
}
|
|
36998
|
+
if (options.env && typeof options.env !== "object") {
|
|
36999
|
+
errors.push("env must be an object");
|
|
37000
|
+
}
|
|
36878
37001
|
return {
|
|
36879
|
-
|
|
36880
|
-
|
|
36881
|
-
|
|
37002
|
+
valid: errors.length === 0,
|
|
37003
|
+
errors,
|
|
37004
|
+
warnings
|
|
36882
37005
|
};
|
|
36883
37006
|
}
|
|
36884
|
-
var
|
|
36885
|
-
var
|
|
36886
|
-
"src/
|
|
37007
|
+
var import_child_process6, import_path7, import_fs6;
|
|
37008
|
+
var init_bashExecutor = __esm({
|
|
37009
|
+
"src/agent/bashExecutor.js"() {
|
|
36887
37010
|
"use strict";
|
|
36888
|
-
|
|
36889
|
-
|
|
36890
|
-
import_path8 = require("path");
|
|
37011
|
+
import_child_process6 = require("child_process");
|
|
37012
|
+
import_path7 = require("path");
|
|
36891
37013
|
import_fs6 = require("fs");
|
|
36892
|
-
|
|
36893
|
-
|
|
36894
|
-
|
|
36895
|
-
name: "edit",
|
|
36896
|
-
description: `Edit files using exact string replacement (Claude Code style).
|
|
36897
|
-
|
|
36898
|
-
This tool performs exact string replacements in files. It requires the old_string to match exactly what's in the file, including all whitespace and indentation.
|
|
36899
|
-
|
|
36900
|
-
Parameters:
|
|
36901
|
-
- file_path: Path to the file to edit (absolute or relative)
|
|
36902
|
-
- old_string: Exact text to find and replace (must be unique in the file unless replace_all is true)
|
|
36903
|
-
- new_string: Text to replace with
|
|
36904
|
-
- replace_all: (optional) Replace all occurrences instead of requiring uniqueness
|
|
37014
|
+
init_bashCommandUtils();
|
|
37015
|
+
}
|
|
37016
|
+
});
|
|
36905
37017
|
|
|
36906
|
-
|
|
36907
|
-
|
|
36908
|
-
|
|
36909
|
-
|
|
36910
|
-
|
|
36911
|
-
|
|
36912
|
-
|
|
36913
|
-
|
|
36914
|
-
|
|
36915
|
-
|
|
36916
|
-
|
|
36917
|
-
|
|
36918
|
-
|
|
36919
|
-
|
|
36920
|
-
|
|
36921
|
-
|
|
36922
|
-
|
|
36923
|
-
|
|
36924
|
-
|
|
36925
|
-
|
|
36926
|
-
|
|
36927
|
-
|
|
36928
|
-
default: false
|
|
36929
|
-
}
|
|
36930
|
-
},
|
|
36931
|
-
required: ["file_path", "old_string", "new_string"]
|
|
36932
|
-
},
|
|
36933
|
-
execute: async ({ file_path, old_string, new_string, replace_all = false }) => {
|
|
36934
|
-
try {
|
|
36935
|
-
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
36936
|
-
return `Error editing file: Invalid file_path - must be a non-empty string`;
|
|
36937
|
-
}
|
|
36938
|
-
if (old_string === void 0 || old_string === null || typeof old_string !== "string") {
|
|
36939
|
-
return `Error editing file: Invalid old_string - must be a string`;
|
|
36940
|
-
}
|
|
36941
|
-
if (new_string === void 0 || new_string === null || typeof new_string !== "string") {
|
|
36942
|
-
return `Error editing file: Invalid new_string - must be a string`;
|
|
36943
|
-
}
|
|
36944
|
-
const resolvedPath2 = (0, import_path8.isAbsolute)(file_path) ? file_path : (0, import_path8.resolve)(cwd || process.cwd(), file_path);
|
|
36945
|
-
if (debug) {
|
|
36946
|
-
console.error(`[Edit] Attempting to edit file: ${resolvedPath2}`);
|
|
36947
|
-
}
|
|
36948
|
-
if (!isPathAllowed(resolvedPath2, allowedFolders)) {
|
|
36949
|
-
return `Error editing file: Permission denied - ${file_path} is outside allowed directories`;
|
|
36950
|
-
}
|
|
36951
|
-
if (!(0, import_fs6.existsSync)(resolvedPath2)) {
|
|
36952
|
-
return `Error editing file: File not found - ${file_path}`;
|
|
36953
|
-
}
|
|
36954
|
-
const content = await import_fs5.promises.readFile(resolvedPath2, "utf-8");
|
|
36955
|
-
if (!content.includes(old_string)) {
|
|
36956
|
-
return `Error editing file: String not found - the specified old_string was not found in ${file_path}`;
|
|
36957
|
-
}
|
|
36958
|
-
const occurrences = content.split(old_string).length - 1;
|
|
36959
|
-
if (!replace_all && occurrences > 1) {
|
|
36960
|
-
return `Error editing file: Multiple occurrences found - the old_string appears ${occurrences} times. Use replace_all: true to replace all occurrences, or provide more context to make the string unique.`;
|
|
36961
|
-
}
|
|
36962
|
-
let newContent;
|
|
36963
|
-
if (replace_all) {
|
|
36964
|
-
newContent = content.replaceAll(old_string, new_string);
|
|
36965
|
-
} else {
|
|
36966
|
-
newContent = content.replace(old_string, new_string);
|
|
36967
|
-
}
|
|
36968
|
-
if (newContent === content) {
|
|
36969
|
-
return `Error editing file: No changes made - old_string and new_string might be the same`;
|
|
36970
|
-
}
|
|
36971
|
-
await import_fs5.promises.writeFile(resolvedPath2, newContent, "utf-8");
|
|
36972
|
-
const replacedCount = replace_all ? occurrences : 1;
|
|
36973
|
-
if (debug) {
|
|
36974
|
-
console.error(`[Edit] Successfully edited ${resolvedPath2}, replaced ${replacedCount} occurrence(s)`);
|
|
36975
|
-
}
|
|
36976
|
-
return `Successfully edited ${file_path} (${replacedCount} replacement${replacedCount !== 1 ? "s" : ""})`;
|
|
36977
|
-
} catch (error2) {
|
|
36978
|
-
console.error("[Edit] Error:", error2);
|
|
36979
|
-
return `Error editing file: ${error2.message}`;
|
|
36980
|
-
}
|
|
36981
|
-
}
|
|
37018
|
+
// src/tools/bash.js
|
|
37019
|
+
var import_ai3, import_path8, bashTool;
|
|
37020
|
+
var init_bash = __esm({
|
|
37021
|
+
"src/tools/bash.js"() {
|
|
37022
|
+
"use strict";
|
|
37023
|
+
import_ai3 = require("ai");
|
|
37024
|
+
import_path8 = require("path");
|
|
37025
|
+
init_bashPermissions();
|
|
37026
|
+
init_bashExecutor();
|
|
37027
|
+
bashTool = (options = {}) => {
|
|
37028
|
+
const {
|
|
37029
|
+
bashConfig = {},
|
|
37030
|
+
debug = false,
|
|
37031
|
+
cwd,
|
|
37032
|
+
allowedFolders = []
|
|
37033
|
+
} = options;
|
|
37034
|
+
const permissionChecker = new BashPermissionChecker({
|
|
37035
|
+
allow: bashConfig.allow,
|
|
37036
|
+
deny: bashConfig.deny,
|
|
37037
|
+
disableDefaultAllow: bashConfig.disableDefaultAllow,
|
|
37038
|
+
disableDefaultDeny: bashConfig.disableDefaultDeny,
|
|
37039
|
+
debug
|
|
36982
37040
|
});
|
|
36983
|
-
|
|
36984
|
-
|
|
36985
|
-
|
|
37041
|
+
const getDefaultWorkingDirectory = () => {
|
|
37042
|
+
if (bashConfig.workingDirectory) {
|
|
37043
|
+
return bashConfig.workingDirectory;
|
|
37044
|
+
}
|
|
37045
|
+
if (cwd) {
|
|
37046
|
+
return cwd;
|
|
37047
|
+
}
|
|
37048
|
+
if (allowedFolders && allowedFolders.length > 0) {
|
|
37049
|
+
return allowedFolders[0];
|
|
37050
|
+
}
|
|
37051
|
+
return process.cwd();
|
|
37052
|
+
};
|
|
36986
37053
|
return (0, import_ai3.tool)({
|
|
36987
|
-
name: "
|
|
36988
|
-
description: `
|
|
37054
|
+
name: "bash",
|
|
37055
|
+
description: `Execute bash commands for system exploration and development tasks.
|
|
36989
37056
|
|
|
36990
|
-
This tool
|
|
37057
|
+
Security: This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
|
|
36991
37058
|
|
|
36992
37059
|
Parameters:
|
|
36993
|
-
-
|
|
36994
|
-
-
|
|
36995
|
-
-
|
|
37060
|
+
- command: (required) The bash command to execute
|
|
37061
|
+
- workingDirectory: (optional) Directory to execute command in
|
|
37062
|
+
- timeout: (optional) Command timeout in milliseconds
|
|
37063
|
+
- env: (optional) Additional environment variables
|
|
36996
37064
|
|
|
36997
|
-
|
|
36998
|
-
-
|
|
36999
|
-
-
|
|
37000
|
-
-
|
|
37065
|
+
Examples of allowed commands by default:
|
|
37066
|
+
- File exploration: ls, cat, head, tail, find, grep
|
|
37067
|
+
- Git operations: git status, git log, git diff, git show
|
|
37068
|
+
- Package info: npm list, pip list, cargo --version
|
|
37069
|
+
- System info: whoami, pwd, uname, date
|
|
37070
|
+
|
|
37071
|
+
Dangerous commands are blocked by default (rm -rf, sudo, npm install, etc.)`,
|
|
37001
37072
|
inputSchema: {
|
|
37002
37073
|
type: "object",
|
|
37003
37074
|
properties: {
|
|
37004
|
-
|
|
37075
|
+
command: {
|
|
37005
37076
|
type: "string",
|
|
37006
|
-
description: "
|
|
37077
|
+
description: "The bash command to execute"
|
|
37007
37078
|
},
|
|
37008
|
-
|
|
37079
|
+
workingDirectory: {
|
|
37009
37080
|
type: "string",
|
|
37010
|
-
description: "
|
|
37081
|
+
description: "Directory to execute the command in (optional)"
|
|
37011
37082
|
},
|
|
37012
|
-
|
|
37013
|
-
type: "
|
|
37014
|
-
description: "
|
|
37015
|
-
|
|
37083
|
+
timeout: {
|
|
37084
|
+
type: "number",
|
|
37085
|
+
description: "Command timeout in milliseconds (optional)",
|
|
37086
|
+
minimum: 1e3,
|
|
37087
|
+
maximum: 6e5
|
|
37088
|
+
},
|
|
37089
|
+
env: {
|
|
37090
|
+
type: "object",
|
|
37091
|
+
description: "Additional environment variables (optional)",
|
|
37092
|
+
additionalProperties: {
|
|
37093
|
+
type: "string"
|
|
37094
|
+
}
|
|
37016
37095
|
}
|
|
37017
37096
|
},
|
|
37018
|
-
required: ["
|
|
37097
|
+
required: ["command"],
|
|
37098
|
+
additionalProperties: false
|
|
37019
37099
|
},
|
|
37020
|
-
execute: async ({
|
|
37100
|
+
execute: async ({ command, workingDirectory, timeout, env }) => {
|
|
37021
37101
|
try {
|
|
37022
|
-
if (
|
|
37023
|
-
return
|
|
37102
|
+
if (command === null || command === void 0 || typeof command !== "string") {
|
|
37103
|
+
return "Error: Command is required and must be a string";
|
|
37024
37104
|
}
|
|
37025
|
-
if (
|
|
37026
|
-
return
|
|
37105
|
+
if (command.trim().length === 0) {
|
|
37106
|
+
return "Error: Command cannot be empty";
|
|
37027
37107
|
}
|
|
37028
|
-
const
|
|
37029
|
-
if (
|
|
37030
|
-
|
|
37108
|
+
const permissionResult = permissionChecker.check(command.trim());
|
|
37109
|
+
if (!permissionResult.allowed) {
|
|
37110
|
+
if (debug) {
|
|
37111
|
+
console.log(`[BashTool] Permission denied for command: "${command}"`);
|
|
37112
|
+
console.log(`[BashTool] Reason: ${permissionResult.reason}`);
|
|
37113
|
+
}
|
|
37114
|
+
return `Permission denied: ${permissionResult.reason}
|
|
37115
|
+
|
|
37116
|
+
This command is not allowed by the current security policy.
|
|
37117
|
+
|
|
37118
|
+
Common reasons:
|
|
37119
|
+
1. The command is in the deny list (potentially dangerous)
|
|
37120
|
+
2. The command is not in the allow list (not a recognized safe command)
|
|
37121
|
+
|
|
37122
|
+
If you believe this command should be allowed, you can:
|
|
37123
|
+
- Use the --bash-allow option to add specific patterns
|
|
37124
|
+
- Use the --no-default-bash-deny flag to remove default restrictions (not recommended)
|
|
37125
|
+
|
|
37126
|
+
For code exploration, try these safe alternatives:
|
|
37127
|
+
- ls, cat, head, tail for file operations
|
|
37128
|
+
- find, grep, rg for searching
|
|
37129
|
+
- git status, git log, git show for git operations
|
|
37130
|
+
- npm list, pip list for package information`;
|
|
37031
37131
|
}
|
|
37032
|
-
|
|
37033
|
-
|
|
37132
|
+
const defaultDir = getDefaultWorkingDirectory();
|
|
37133
|
+
const workingDir = workingDirectory ? (0, import_path8.isAbsolute)(workingDirectory) ? (0, import_path8.resolve)(workingDirectory) : (0, import_path8.resolve)(defaultDir, workingDirectory) : defaultDir;
|
|
37134
|
+
if (allowedFolders && allowedFolders.length > 0) {
|
|
37135
|
+
const resolvedWorkingDir = (0, import_path8.resolve)(workingDir);
|
|
37136
|
+
const isAllowed = allowedFolders.some((folder) => {
|
|
37137
|
+
const resolvedFolder = (0, import_path8.resolve)(folder);
|
|
37138
|
+
return resolvedWorkingDir === resolvedFolder || resolvedWorkingDir.startsWith(resolvedFolder + import_path8.sep);
|
|
37139
|
+
});
|
|
37140
|
+
if (!isAllowed) {
|
|
37141
|
+
return `Error: Working directory "${workingDir}" is not within allowed folders: ${allowedFolders.join(", ")}`;
|
|
37142
|
+
}
|
|
37034
37143
|
}
|
|
37035
|
-
|
|
37036
|
-
|
|
37144
|
+
const executionOptions = {
|
|
37145
|
+
workingDirectory: workingDir,
|
|
37146
|
+
timeout: timeout || bashConfig.timeout || 12e4,
|
|
37147
|
+
env: { ...bashConfig.env, ...env },
|
|
37148
|
+
maxBuffer: bashConfig.maxBuffer,
|
|
37149
|
+
debug
|
|
37150
|
+
};
|
|
37151
|
+
const validation = validateExecutionOptions(executionOptions);
|
|
37152
|
+
if (!validation.valid) {
|
|
37153
|
+
return `Error: Invalid execution options: ${validation.errors.join(", ")}`;
|
|
37154
|
+
}
|
|
37155
|
+
if (validation.warnings.length > 0 && debug) {
|
|
37156
|
+
console.log("[BashTool] Warnings:", validation.warnings);
|
|
37037
37157
|
}
|
|
37038
|
-
const dir = (0, import_path8.dirname)(resolvedPath2);
|
|
37039
|
-
await import_fs5.promises.mkdir(dir, { recursive: true });
|
|
37040
|
-
await import_fs5.promises.writeFile(resolvedPath2, content, "utf-8");
|
|
37041
|
-
const action = (0, import_fs6.existsSync)(resolvedPath2) && overwrite ? "overwrote" : "created";
|
|
37042
|
-
const bytes = Buffer.byteLength(content, "utf-8");
|
|
37043
37158
|
if (debug) {
|
|
37044
|
-
console.
|
|
37159
|
+
console.log(`[BashTool] Executing command: "${command}"`);
|
|
37160
|
+
console.log(`[BashTool] Working directory: "${workingDir}"`);
|
|
37161
|
+
console.log(`[BashTool] Timeout: ${executionOptions.timeout}ms`);
|
|
37045
37162
|
}
|
|
37046
|
-
|
|
37163
|
+
const result = await executeBashCommand(command.trim(), executionOptions);
|
|
37164
|
+
if (debug) {
|
|
37165
|
+
console.log(`[BashTool] Command completed - Success: ${result.success}, Duration: ${result.duration}ms`);
|
|
37166
|
+
}
|
|
37167
|
+
const formattedResult = formatExecutionResult(result, debug);
|
|
37168
|
+
if (!result.success) {
|
|
37169
|
+
let errorInfo = `
|
|
37170
|
+
|
|
37171
|
+
Command failed with exit code ${result.exitCode}`;
|
|
37172
|
+
if (result.killed) {
|
|
37173
|
+
errorInfo += ` (${result.error})`;
|
|
37174
|
+
}
|
|
37175
|
+
return formattedResult + errorInfo;
|
|
37176
|
+
}
|
|
37177
|
+
return formattedResult;
|
|
37047
37178
|
} catch (error2) {
|
|
37048
|
-
|
|
37049
|
-
|
|
37179
|
+
if (debug) {
|
|
37180
|
+
console.error("[BashTool] Execution error:", error2);
|
|
37181
|
+
}
|
|
37182
|
+
return `Error executing bash command: ${error2.message}`;
|
|
37050
37183
|
}
|
|
37051
37184
|
}
|
|
37052
37185
|
});
|
|
37053
37186
|
};
|
|
37054
|
-
editDescription = "Edit files using exact string replacement. Requires exact match including whitespace.";
|
|
37055
|
-
createDescription = "Create new files with specified content. Will create parent directories if needed.";
|
|
37056
|
-
editToolDefinition = `
|
|
37057
|
-
## edit
|
|
37058
|
-
Description: ${editDescription}
|
|
37059
|
-
|
|
37060
|
-
When to use:
|
|
37061
|
-
- For precise, surgical edits to existing files
|
|
37062
|
-
- When you need to change specific lines or blocks of code
|
|
37063
|
-
- For renaming functions, variables, or updating configuration values
|
|
37064
|
-
- When the exact text to replace is known and unique (or use replace_all for multiple occurrences)
|
|
37065
|
-
|
|
37066
|
-
When NOT to use:
|
|
37067
|
-
- For creating new files (use 'create' tool instead)
|
|
37068
|
-
- When you cannot determine the exact text to replace
|
|
37069
|
-
- When changes span multiple locations that would be better handled together
|
|
37070
|
-
|
|
37071
|
-
Parameters:
|
|
37072
|
-
- file_path: (required) Path to the file to edit
|
|
37073
|
-
- old_string: (required) Exact text to find and replace (must match including whitespace, newlines, and indentation)
|
|
37074
|
-
- new_string: (required) Text to replace with
|
|
37075
|
-
- replace_all: (optional, default: false) Replace all occurrences if the string appears multiple times
|
|
37076
|
-
|
|
37077
|
-
Important notes:
|
|
37078
|
-
- The old_string MUST match EXACTLY, including all whitespace, indentation, and line breaks
|
|
37079
|
-
- If old_string appears multiple times and replace_all is false, the tool will fail
|
|
37080
|
-
- Always verify the exact formatting of the text you want to replace
|
|
37081
|
-
|
|
37082
|
-
Examples:
|
|
37083
|
-
<edit>
|
|
37084
|
-
<file_path>src/main.js</file_path>
|
|
37085
|
-
<old_string>function oldName() {
|
|
37086
|
-
return 42;
|
|
37087
|
-
}</old_string>
|
|
37088
|
-
<new_string>function newName() {
|
|
37089
|
-
return 42;
|
|
37090
|
-
}</new_string>
|
|
37091
|
-
</edit>
|
|
37092
|
-
|
|
37093
|
-
<edit>
|
|
37094
|
-
<file_path>config.json</file_path>
|
|
37095
|
-
<old_string>"debug": false</old_string>
|
|
37096
|
-
<new_string>"debug": true</new_string>
|
|
37097
|
-
<replace_all>true</replace_all>
|
|
37098
|
-
</edit>`;
|
|
37099
|
-
createToolDefinition = `
|
|
37100
|
-
## create
|
|
37101
|
-
Description: ${createDescription}
|
|
37102
|
-
|
|
37103
|
-
When to use:
|
|
37104
|
-
- For creating brand new files from scratch
|
|
37105
|
-
- When you need to add configuration files, documentation, or new modules
|
|
37106
|
-
- For generating boilerplate code or templates
|
|
37107
|
-
- When you have the complete content ready to write
|
|
37108
|
-
|
|
37109
|
-
When NOT to use:
|
|
37110
|
-
- For editing existing files (use 'edit' tool instead)
|
|
37111
|
-
- When a file already exists unless you explicitly want to overwrite it
|
|
37112
|
-
|
|
37113
|
-
Parameters:
|
|
37114
|
-
- file_path: (required) Path where the file should be created
|
|
37115
|
-
- content: (required) Complete content to write to the file
|
|
37116
|
-
- overwrite: (optional, default: false) Whether to overwrite if file already exists
|
|
37117
|
-
|
|
37118
|
-
Important notes:
|
|
37119
|
-
- Parent directories will be created automatically if they don't exist
|
|
37120
|
-
- The tool will fail if the file already exists and overwrite is false
|
|
37121
|
-
- Be careful with the overwrite option as it completely replaces existing files
|
|
37122
|
-
|
|
37123
|
-
Examples:
|
|
37124
|
-
<create>
|
|
37125
|
-
<file_path>src/newFile.js</file_path>
|
|
37126
|
-
<content>export function hello() {
|
|
37127
|
-
return "Hello, world!";
|
|
37128
|
-
}</content>
|
|
37129
|
-
</create>
|
|
37130
|
-
|
|
37131
|
-
<create>
|
|
37132
|
-
<file_path>README.md</file_path>
|
|
37133
|
-
<content># My Project
|
|
37134
|
-
|
|
37135
|
-
This is a new project.</content>
|
|
37136
|
-
<overwrite>true</overwrite>
|
|
37137
|
-
</create>`;
|
|
37138
37187
|
}
|
|
37139
37188
|
});
|
|
37140
37189
|
|