@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.
@@ -30505,7 +30505,9 @@ async function delegate({
30505
30505
  debug,
30506
30506
  tracer,
30507
30507
  path: path9,
30508
- // Inherit from parent
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/common.js
34825
- function buildToolTagPattern(tools2 = DEFAULT_VALID_TOOLS) {
34826
- const allTools = [...tools2];
34827
- if (allTools.includes("attempt_completion") && !allTools.includes("attempt_complete")) {
34828
- allTools.push("attempt_complete");
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
- return targets.split(/[\s,]+/).filter((f4) => f4.length > 0);
34946
- }
34947
- function parseAndResolvePaths(pathStr, cwd) {
34948
- if (!pathStr) return [];
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 resolveTargetPath(target, cwd) {
34958
- const searchStart = target.length > 2 && target[1] === ":" && /[a-zA-Z]/.test(target[0]) ? 2 : 0;
34959
- const colonIdx = target.indexOf(":", searchStart);
34960
- const hashIdx = target.indexOf("#");
34961
- let filePart, suffix;
34962
- if (colonIdx !== -1 && (hashIdx === -1 || colonIdx < hashIdx)) {
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 import_path5, searchSchema, querySchema, extractSchema, delegateSchema, bashSchema, attemptCompletionSchema, searchToolDefinition, queryToolDefinition, extractToolDefinition, delegateToolDefinition, attemptCompletionToolDefinition, bashToolDefinition, searchDescription, queryDescription, extractDescription, delegateDescription, DEFAULT_VALID_TOOLS;
34978
- var init_common2 = __esm({
34979
- "src/tools/common.js"() {
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
- init_zod();
34850
+ import_ai = require("ai");
34851
+ import_fs4 = require("fs");
34982
34852
  import_path5 = require("path");
34983
- searchSchema = external_exports.object({
34984
- query: external_exports.string().describe("Search query with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation."),
34985
- 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.')
34986
- });
34987
- querySchema = external_exports.object({
34988
- pattern: external_exports.string().describe("AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc."),
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
- **Session Management & Caching:**
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
- - query: (required) Search query with Elasticsearch syntax. Use quotes for exact matches ("functionName"), AND/OR for boolean logic, - for negation, + for important terms.
35072
- - 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.
35073
-
35074
- **Workflow:** Always start with search, then use extract for detailed context when needed.
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
- User: Find all react imports in the project.
35103
- <search>
35104
- <query>"import" AND "react"</query>
35105
- <path>.</path>
35106
- </search>
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
- User: Find how decompound library works?
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
- - pattern: (required) AST pattern to search for. Use $NAME for variable names, $$$PARAMS for parameter lists, etc.
35121
- - path: (optional, default: '.') Path to search in.
35122
- - language: (optional, default: 'rust') Programming language to use for parsing.
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
- </examples>
35135
- `;
35136
- extractToolDefinition = `
35137
- ## extract
35138
- 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.
35139
- Full file extraction should be the LAST RESORT! Always prefer search.
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
- **Multiple Extraction:** You can extract multiple symbols/files in one call by providing multiple file paths separated by spaces.
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
- **Session Awareness:** Reuse context from previous tool calls. Don't re-extract the same symbols you already have.
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
- - 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.
35147
- - input_content: (optional) Text content to extract file paths from (alternative to targets for processing diffs/logs).
35148
- - allow_tests: (optional, default: true) Include test files in extraction results.
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
- User: Where is the login logic? (After search found relevant files)
35155
- <extract>
35156
- <targets>session.rs#AuthService.login auth.rs:2-100 config.rs#DatabaseConfig</targets>
35157
- </extract>
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
- User: How does error handling work? (After search identified files)
35160
- <extract>
35161
- <targets>error.rs#ErrorType utils.rs#handle_error src/main.rs:50-80</targets>
35162
- </extract>
35163
-
35164
- User: How RankManager works
35165
- <extract>
35166
- <targets>src/search/ranking.rs#RankManager</targets>
35167
- </extract>
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
- <delegate>
35197
- <task>Analyze all authentication and authorization code in the codebase for security vulnerabilities and provide specific remediation recommendations</task>
35198
- </delegate>
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
- <delegate>
35201
- <task>Review database queries and API endpoints for performance bottlenecks and suggest optimization strategies</task>
35202
- </delegate>
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
- The agent uses this tool automatically when it identifies that work can be separated into distinct, parallel tasks for more efficient processing.
35205
- `;
35206
- attemptCompletionToolDefinition = `
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
- - command: (required) The bash command to execute
35222
- - workingDirectory: (optional) Directory to execute the command in
35223
- - timeout: (optional) Command timeout in milliseconds
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
- User: Search for TODO comments in code
35255
- <bash>
35256
- <command>grep -r "TODO" src/</command>
35257
- </bash>
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
- User: Show recent git commits
35260
- <bash>
35261
- <command>git log --oneline -10</command>
35262
- </bash>
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
- User: Check system info
35265
- <bash>
35266
- <command>uname -a</command>
35267
- </bash>
35133
+ <create>
35134
+ <file_path>README.md</file_path>
35135
+ <content># My Project
35268
35136
 
35269
- </examples>
35270
- `;
35271
- searchDescription = "Search code in the repository using Elasticsearch-like query syntax. Use this tool first for any code-related questions.";
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/vercel.js
35290
- var import_ai, searchTool, queryTool, extractTool, delegateTool;
35291
- var init_vercel = __esm({
35292
- "src/tools/vercel.js"() {
35293
- "use strict";
35294
- import_ai = require("ai");
35295
- init_search();
35296
- init_query();
35297
- init_extract();
35298
- init_delegate();
35299
- init_common2();
35300
- searchTool = (options = {}) => {
35301
- const { sessionId, maxTokens = 1e4, debug = false, outline = false } = options;
35302
- return (0, import_ai.tool)({
35303
- name: "search",
35304
- description: searchDescription,
35305
- inputSchema: searchSchema,
35306
- execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language }) => {
35307
- try {
35308
- const effectiveMaxTokens = paramMaxTokens || maxTokens;
35309
- let searchPaths;
35310
- if (path9) {
35311
- searchPaths = parseAndResolvePaths(path9, options.cwd);
35312
- }
35313
- if (!searchPaths || searchPaths.length === 0) {
35314
- searchPaths = [options.cwd || "."];
35315
- }
35316
- const searchPath = searchPaths.join(" ");
35317
- if (debug) {
35318
- console.error(`Executing search with query: "${searchQuery}", path: "${searchPath}", exact: ${exact ? "true" : "false"}, language: ${language || "all"}, session: ${sessionId || "none"}`);
35319
- }
35320
- const searchOptions = {
35321
- query: searchQuery,
35322
- path: searchPath,
35323
- cwd: options.cwd,
35324
- // Working directory for resolving relative paths
35325
- allowTests: allow_tests ?? true,
35326
- exact,
35327
- json: false,
35328
- maxTokens: effectiveMaxTokens,
35329
- session: sessionId,
35330
- // Pass session ID if provided
35331
- language
35332
- // Pass language parameter if provided
35333
- };
35334
- if (outline) {
35335
- searchOptions.format = "outline-xml";
35336
- }
35337
- const results = await search(searchOptions);
35338
- return results;
35339
- } catch (error2) {
35340
- console.error("Error executing search command:", error2);
35341
- return `Error executing search command: ${error2.message}`;
35342
- }
35343
- }
35344
- });
35345
- };
35346
- queryTool = (options = {}) => {
35347
- const { debug = false } = options;
35348
- return (0, import_ai.tool)({
35349
- name: "query",
35350
- description: queryDescription,
35351
- inputSchema: querySchema,
35352
- execute: async ({ pattern, path: path9, language, allow_tests }) => {
35353
- try {
35354
- let queryPaths;
35355
- if (path9) {
35356
- queryPaths = parseAndResolvePaths(path9, options.cwd);
35357
- }
35358
- if (!queryPaths || queryPaths.length === 0) {
35359
- queryPaths = [options.cwd || "."];
35360
- }
35361
- const queryPath = queryPaths.join(" ");
35362
- if (debug) {
35363
- console.error(`Executing query with pattern: "${pattern}", path: "${queryPath}", language: ${language || "auto"}`);
35364
- }
35365
- const results = await query({
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
- delegateTool = (options = {}) => {
35459
- const { debug = false, timeout = 300, cwd, allowedFolders, enableBash = false, bashConfig } = options;
35460
- return (0, import_ai.tool)({
35461
- name: "delegate",
35462
- description: delegateDescription,
35463
- inputSchema: delegateSchema,
35464
- execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer }) => {
35465
- if (!task || typeof task !== "string") {
35466
- throw new Error("Task parameter is required and must be a non-empty string");
35467
- }
35468
- if (task.trim().length === 0) {
35469
- throw new Error("Task parameter cannot be empty or whitespace only");
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
- // src/agent/bashDefaults.js
35521
- var DEFAULT_ALLOW_PATTERNS, DEFAULT_DENY_PATTERNS;
35522
- var init_bashDefaults = __esm({
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 (inQuotes) {
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
- if (args.length === 0) {
36119
- return {
36120
- success: false,
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 [baseCommand, ...commandArgs] = args;
36128
- return {
36129
- success: true,
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 isComplexPattern(pattern) {
36143
- if (!pattern || typeof pattern !== "string") return false;
36144
- const operatorPatterns = [
36145
- /\|/,
36146
- // Pipes
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 globToRegex(pattern) {
36167
- let escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
36168
- escaped = escaped.replace(/\*/g, ".*?");
36169
- return new RegExp("^" + escaped + "$", "i");
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 matchesComplexPattern(command, pattern) {
36172
- if (!command || !pattern) return false;
36173
- const normalizedCommand = command.trim().replace(/\s+/g, " ");
36174
- const normalizedPattern = pattern.trim().replace(/\s+/g, " ");
36175
- try {
36176
- const regex = globToRegex(normalizedPattern);
36177
- return regex.test(normalizedCommand);
36178
- } catch (e4) {
36179
- return normalizedCommand === normalizedPattern;
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
- function parseCommand(command) {
36183
- const result = parseSimpleCommand(command);
36184
- if (!result.success) {
36185
- return {
36186
- command: "",
36187
- args: [],
36188
- error: result.error,
36189
- isComplex: result.isComplex
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
- return {
36193
- command: result.command,
36194
- args: result.args,
36195
- error: null,
36196
- isComplex: result.isComplex
36197
- };
36198
- }
36199
- function parseCommandForExecution(command) {
36200
- const result = parseSimpleCommand(command);
36201
- if (!result.success) {
36202
- return null;
36203
- }
36204
- return result.fullArgs;
36205
- }
36206
- var init_bashCommandUtils = __esm({
36207
- "src/agent/bashCommandUtils.js"() {
36208
- "use strict";
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/agent/bashPermissions.js
36213
- function matchesPattern(parsedCommand, pattern) {
36214
- if (!parsedCommand || !pattern) return false;
36215
- const { command, args } = parsedCommand;
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
- init_bashDefaults();
36252
- init_bashCommandUtils();
36253
- BashPermissionChecker = class {
36254
- /**
36255
- * Create a permission checker
36256
- * @param {Object} config - Configuration options
36257
- * @param {string[]} [config.allow] - Additional allow patterns
36258
- * @param {string[]} [config.deny] - Additional deny patterns
36259
- * @param {boolean} [config.disableDefaultAllow] - Disable default allow list
36260
- * @param {boolean} [config.disableDefaultDeny] - Disable default deny list
36261
- * @param {boolean} [config.debug] - Enable debug logging
36262
- */
36263
- constructor(config = {}) {
36264
- this.debug = config.debug || false;
36265
- this.allowPatterns = [];
36266
- if (!config.disableDefaultAllow) {
36267
- this.allowPatterns.push(...DEFAULT_ALLOW_PATTERNS);
36268
- if (this.debug) {
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
- return {
36384
- allowed: false,
36385
- reason: `Command matches deny pattern: ${pattern}`,
36386
- command,
36387
- isComplex: true,
36388
- matchedPatterns: [pattern]
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
- for (const pattern of complexAllowPatterns) {
36393
- if (matchesComplexPattern(command, pattern)) {
36394
- if (this.debug) {
36395
- console.log(`[BashPermissions] ALLOWED - matches complex allow pattern: ${pattern}`);
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
- return {
36398
- allowed: true,
36399
- command,
36400
- isComplex: true,
36401
- matchedPattern: pattern
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
- if (this.debug) {
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
- const isComplex = isComplexCommand(command);
36471
- let cmd, cmdArgs, useShell;
36472
- if (isComplex) {
36473
- cmd = "sh";
36474
- cmdArgs = ["-c", command];
36475
- useShell = false;
36476
- if (debug) {
36477
- console.log(`[BashExecutor] Complex command - using sh -c`);
36478
- }
36479
- } else {
36480
- const args = parseCommandForExecution(command);
36481
- if (!args || args.length === 0) {
36482
- resolve6({
36483
- success: false,
36484
- error: "Failed to parse command",
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
- }, 5e3);
36520
- }
36521
- }, timeout);
36522
- }
36523
- child.stdout.on("data", (data2) => {
36524
- const chunk = data2.toString();
36525
- if (stdout.length + chunk.length <= maxBuffer) {
36526
- stdout += chunk;
36527
- } else {
36528
- if (!killed) {
36529
- killed = true;
36530
- child.kill("SIGTERM");
36531
- }
36532
- }
36533
- });
36534
- child.stderr.on("data", (data2) => {
36535
- const chunk = data2.toString();
36536
- if (stderr.length + chunk.length <= maxBuffer) {
36537
- stderr += chunk;
36538
- } else {
36539
- if (!killed) {
36540
- killed = true;
36541
- child.kill("SIGTERM");
36542
- }
36543
- }
36544
- });
36545
- child.on("close", (code, signal) => {
36546
- if (timeoutHandle) {
36547
- clearTimeout(timeoutHandle);
36548
- }
36549
- const duration = Date.now() - startTime;
36550
- if (debug) {
36551
- console.log(`[BashExecutor] Command completed - Code: ${code}, Signal: ${signal}, Duration: ${duration}ms`);
36552
- console.log(`[BashExecutor] Stdout length: ${stdout.length}, Stderr length: ${stderr.length}`);
36553
- }
36554
- let success = true;
36555
- let error2 = "";
36556
- if (killed) {
36557
- success = false;
36558
- if (stdout.length + stderr.length > maxBuffer) {
36559
- error2 = `Command output exceeded maximum buffer size (${maxBuffer} bytes)`;
36560
- } else {
36561
- error2 = `Command timed out after ${timeout}ms`;
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
- child.on("error", (error2) => {
36581
- if (timeoutHandle) {
36582
- clearTimeout(timeoutHandle);
36583
- }
36584
- if (debug) {
36585
- console.log(`[BashExecutor] Spawn error:`, error2);
36586
- }
36587
- resolve6({
36588
- success: false,
36589
- error: `Failed to execute command: ${error2.message}`,
36590
- stdout: "",
36591
- stderr: "",
36592
- exitCode: 1,
36593
- command,
36594
- workingDirectory: cwd,
36595
- duration: Date.now() - startTime
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
- function formatExecutionResult(result, includeMetadata = false) {
36601
- if (!result) {
36602
- return "No result available";
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
- let output = "";
36605
- if (includeMetadata) {
36606
- output += `Command: ${result.command}
36607
- `;
36608
- output += `Working directory: ${result.workingDirectory}
36609
- `;
36610
- output += `Duration: ${result.duration}ms
36611
- `;
36612
- output += `Exit Code: ${result.exitCode}
36613
- `;
36614
- if (result.signal) {
36615
- output += `Signal: ${result.signal}
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
- if (result.stdout) {
36621
- if (includeMetadata) {
36622
- output += "--- STDOUT ---\n";
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
- output += result.stdout;
36625
- if (includeMetadata && result.stderr) {
36626
- output += "\n";
36413
+ if (char === "\\" && !inQuotes) {
36414
+ escaped = true;
36415
+ continue;
36627
36416
  }
36628
- }
36629
- if (result.stderr) {
36630
- if (includeMetadata) {
36631
- if (result.stdout) output += "\n";
36632
- output += "--- STDERR ---\n";
36633
- } else if (result.stdout) {
36634
- output += "\n--- STDERR ---\n";
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 (!result.success && result.error && !result.stderr) {
36639
- if (output) output += "\n";
36640
- output += `Error: ${result.error}`;
36432
+ if (current.trim()) {
36433
+ args.push(current.trim());
36641
36434
  }
36642
- if (!result.success && result.exitCode !== void 0 && result.exitCode !== 0) {
36643
- if (output) output += "\n";
36644
- output += `Exit code: ${result.exitCode}`;
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 validateExecutionOptions(options = {}) {
36649
- const errors = [];
36650
- const warnings = [];
36651
- if (options.timeout !== void 0) {
36652
- if (typeof options.timeout !== "number" || options.timeout < 0) {
36653
- errors.push("timeout must be a non-negative number");
36654
- } else if (options.timeout > 6e5) {
36655
- warnings.push("timeout is very high (>10 minutes)");
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
- valid: errors.length === 0,
36677
- errors,
36678
- warnings
36519
+ command: result.command,
36520
+ args: result.args,
36521
+ error: null,
36522
+ isComplex: result.isComplex
36679
36523
  };
36680
36524
  }
36681
- var import_child_process6, import_path6, import_fs4;
36682
- var init_bashExecutor = __esm({
36683
- "src/agent/bashExecutor.js"() {
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/tools/bash.js
36693
- var import_ai2, import_path7, bashTool;
36694
- var init_bash = __esm({
36695
- "src/tools/bash.js"() {
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
- import_ai2 = require("ai");
36698
- import_path7 = require("path");
36699
- init_bashPermissions();
36700
- init_bashExecutor();
36701
- bashTool = (options = {}) => {
36702
- const {
36703
- bashConfig = {},
36704
- debug = false,
36705
- cwd,
36706
- allowedFolders = []
36707
- } = options;
36708
- const permissionChecker = new BashPermissionChecker({
36709
- allow: bashConfig.allow,
36710
- deny: bashConfig.deny,
36711
- disableDefaultAllow: bashConfig.disableDefaultAllow,
36712
- disableDefaultDeny: bashConfig.disableDefaultDeny,
36713
- debug
36714
- });
36715
- const getDefaultWorkingDirectory = () => {
36716
- if (bashConfig.workingDirectory) {
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 (cwd) {
36720
- return cwd;
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
- if (allowedFolders && allowedFolders.length > 0) {
36723
- return allowedFolders[0];
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
- return process.cwd();
36726
- };
36727
- return (0, import_ai2.tool)({
36728
- name: "bash",
36729
- description: `Execute bash commands for system exploration and development tasks.
36730
-
36731
- Security: This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
36732
-
36733
- Parameters:
36734
- - command: (required) The bash command to execute
36735
- - workingDirectory: (optional) Directory to execute command in
36736
- - timeout: (optional) Command timeout in milliseconds
36737
- - env: (optional) Additional environment variables
36738
-
36739
- Examples of allowed commands by default:
36740
- - File exploration: ls, cat, head, tail, find, grep
36741
- - Git operations: git status, git log, git diff, git show
36742
- - Package info: npm list, pip list, cargo --version
36743
- - System info: whoami, pwd, uname, date
36744
-
36745
- Dangerous commands are blocked by default (rm -rf, sudo, npm install, etc.)`,
36746
- inputSchema: {
36747
- type: "object",
36748
- properties: {
36749
- command: {
36750
- type: "string",
36751
- description: "The bash command to execute"
36752
- },
36753
- workingDirectory: {
36754
- type: "string",
36755
- description: "Directory to execute the command in (optional)"
36756
- },
36757
- timeout: {
36758
- type: "number",
36759
- description: "Command timeout in milliseconds (optional)",
36760
- minimum: 1e3,
36761
- maximum: 6e5
36762
- },
36763
- env: {
36764
- type: "object",
36765
- description: "Additional environment variables (optional)",
36766
- additionalProperties: {
36767
- type: "string"
36768
- }
36769
- }
36770
- },
36771
- required: ["command"],
36772
- additionalProperties: false
36773
- },
36774
- execute: async ({ command, workingDirectory, timeout, env }) => {
36775
- try {
36776
- if (command === null || command === void 0 || typeof command !== "string") {
36777
- return "Error: Command is required and must be a string";
36778
- }
36779
- if (command.trim().length === 0) {
36780
- return "Error: Command cannot be empty";
36781
- }
36782
- const permissionResult = permissionChecker.check(command.trim());
36783
- if (!permissionResult.allowed) {
36784
- if (debug) {
36785
- console.log(`[BashTool] Permission denied for command: "${command}"`);
36786
- console.log(`[BashTool] Reason: ${permissionResult.reason}`);
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
- const validation = validateExecutionOptions(executionOptions);
36826
- if (!validation.valid) {
36827
- return `Error: Invalid execution options: ${validation.errors.join(", ")}`;
36828
- }
36829
- if (validation.warnings.length > 0 && debug) {
36830
- console.log("[BashTool] Warnings:", validation.warnings);
36831
- }
36832
- if (debug) {
36833
- console.log(`[BashTool] Executing command: "${command}"`);
36834
- console.log(`[BashTool] Working directory: "${workingDir}"`);
36835
- console.log(`[BashTool] Timeout: ${executionOptions.timeout}ms`);
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
- const result = await executeBashCommand(command.trim(), executionOptions);
36838
- if (debug) {
36839
- console.log(`[BashTool] Command completed - Success: ${result.success}, Duration: ${result.duration}ms`);
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
- const formattedResult = formatExecutionResult(result, debug);
36842
- if (!result.success) {
36843
- let errorInfo = `
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
- Command failed with exit code ${result.exitCode}`;
36846
- if (result.killed) {
36847
- errorInfo += ` (${result.error})`;
36848
- }
36849
- return formattedResult + errorInfo;
36850
- }
36851
- return formattedResult;
36852
- } catch (error2) {
36853
- if (debug) {
36854
- console.error("[BashTool] Execution error:", error2);
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
- return `Error executing bash command: ${error2.message}`;
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
- // src/tools/edit.js
36865
- function isPathAllowed(filePath, allowedFolders) {
36866
- if (!allowedFolders || allowedFolders.length === 0) {
36867
- const resolvedPath3 = (0, import_path8.resolve)(filePath);
36868
- const cwd = (0, import_path8.resolve)(process.cwd());
36869
- return resolvedPath3 === cwd || resolvedPath3.startsWith(cwd + import_path8.sep);
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
- const resolvedPath2 = (0, import_path8.resolve)(filePath);
36872
- return allowedFolders.some((folder) => {
36873
- const allowedPath = (0, import_path8.resolve)(folder);
36874
- return resolvedPath2 === allowedPath || resolvedPath2.startsWith(allowedPath + import_path8.sep);
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 parseFileToolOptions(options = {}) {
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
- debug: options.debug || false,
36880
- allowedFolders: options.allowedFolders || [],
36881
- cwd: options.cwd
37002
+ valid: errors.length === 0,
37003
+ errors,
37004
+ warnings
36882
37005
  };
36883
37006
  }
36884
- var import_ai3, import_fs5, import_path8, import_fs6, editTool, createTool, editDescription, createDescription, editToolDefinition, createToolDefinition;
36885
- var init_edit = __esm({
36886
- "src/tools/edit.js"() {
37007
+ var import_child_process6, import_path7, import_fs6;
37008
+ var init_bashExecutor = __esm({
37009
+ "src/agent/bashExecutor.js"() {
36887
37010
  "use strict";
36888
- import_ai3 = require("ai");
36889
- import_fs5 = require("fs");
36890
- import_path8 = require("path");
37011
+ import_child_process6 = require("child_process");
37012
+ import_path7 = require("path");
36891
37013
  import_fs6 = require("fs");
36892
- editTool = (options = {}) => {
36893
- const { debug, allowedFolders, cwd } = parseFileToolOptions(options);
36894
- return (0, import_ai3.tool)({
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
- Important:
36907
- - The old_string must match EXACTLY including whitespace
36908
- - If old_string appears multiple times and replace_all is false, the edit will fail
36909
- - Use larger context around the string to ensure uniqueness when needed`,
36910
- inputSchema: {
36911
- type: "object",
36912
- properties: {
36913
- file_path: {
36914
- type: "string",
36915
- description: "Path to the file to edit"
36916
- },
36917
- old_string: {
36918
- type: "string",
36919
- description: "Exact text to find and replace"
36920
- },
36921
- new_string: {
36922
- type: "string",
36923
- description: "Text to replace with"
36924
- },
36925
- replace_all: {
36926
- type: "boolean",
36927
- description: "Replace all occurrences (default: false)",
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
- createTool = (options = {}) => {
36985
- const { debug, allowedFolders, cwd } = parseFileToolOptions(options);
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: "create",
36988
- description: `Create new files with specified content.
37054
+ name: "bash",
37055
+ description: `Execute bash commands for system exploration and development tasks.
36989
37056
 
36990
- This tool creates new files in the filesystem. It will create parent directories if they don't exist.
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
- - file_path: Path where the file should be created (absolute or relative)
36994
- - content: Content to write to the file
36995
- - overwrite: (optional) Whether to overwrite if file exists (default: false)
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
- Important:
36998
- - By default, will fail if the file already exists
36999
- - Set overwrite: true to replace existing files
37000
- - Parent directories will be created automatically if needed`,
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
- file_path: {
37075
+ command: {
37005
37076
  type: "string",
37006
- description: "Path where the file should be created"
37077
+ description: "The bash command to execute"
37007
37078
  },
37008
- content: {
37079
+ workingDirectory: {
37009
37080
  type: "string",
37010
- description: "Content to write to the file"
37081
+ description: "Directory to execute the command in (optional)"
37011
37082
  },
37012
- overwrite: {
37013
- type: "boolean",
37014
- description: "Overwrite if file exists (default: false)",
37015
- default: false
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: ["file_path", "content"]
37097
+ required: ["command"],
37098
+ additionalProperties: false
37019
37099
  },
37020
- execute: async ({ file_path, content, overwrite = false }) => {
37100
+ execute: async ({ command, workingDirectory, timeout, env }) => {
37021
37101
  try {
37022
- if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
37023
- return `Error creating file: Invalid file_path - must be a non-empty string`;
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 (content === void 0 || content === null || typeof content !== "string") {
37026
- return `Error creating file: Invalid content - must be a string`;
37105
+ if (command.trim().length === 0) {
37106
+ return "Error: Command cannot be empty";
37027
37107
  }
37028
- const resolvedPath2 = (0, import_path8.isAbsolute)(file_path) ? file_path : (0, import_path8.resolve)(cwd || process.cwd(), file_path);
37029
- if (debug) {
37030
- console.error(`[Create] Attempting to create file: ${resolvedPath2}`);
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
- if (!isPathAllowed(resolvedPath2, allowedFolders)) {
37033
- return `Error creating file: Permission denied - ${file_path} is outside allowed directories`;
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
- if ((0, import_fs6.existsSync)(resolvedPath2) && !overwrite) {
37036
- return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
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.error(`[Create] Successfully ${action} ${resolvedPath2}`);
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
- return `Successfully ${action} ${file_path} (${bytes} bytes)`;
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
- console.error("[Create] Error:", error2);
37049
- return `Error creating file: ${error2.message}`;
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