deepagents 0.0.0-rc.2 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/graph.js CHANGED
@@ -11,7 +11,6 @@ import { createReactAgent } from "@langchain/langgraph/prebuilt";
11
11
  import { createTaskTool } from "./subAgent.js";
12
12
  import { getDefaultModel } from "./model.js";
13
13
  import { writeTodos, readFile, writeFile, editFile, ls } from "./tools.js";
14
- import { localLsTool, localReadFileTool, localWriteFileTool, localGlobTool, localGrepTool, strReplaceBasedEditTool, } from "./local_tools.js";
15
14
  import { DeepAgentState } from "./state.js";
16
15
  /**
17
16
  * Base prompt that provides instructions about available tools
@@ -29,7 +28,7 @@ It is critical that you mark todos as completed as soon as you are done with a t
29
28
 
30
29
  - When doing web search, prefer to use the \`task\` tool in order to reduce context usage.`;
31
30
  /**
32
- * Built-in tools that are always available in Deep Agents (mock filesystem)
31
+ * Built-in tools that are always available in Deep Agents
33
32
  */
34
33
  const BUILTIN_TOOLS = [
35
34
  writeTodos,
@@ -38,18 +37,6 @@ const BUILTIN_TOOLS = [
38
37
  editFile,
39
38
  ls,
40
39
  ];
41
- /**
42
- * Local filesystem tools
43
- */
44
- const LOCAL_BUILTIN_TOOLS = [
45
- writeTodos,
46
- localReadFileTool,
47
- localWriteFileTool,
48
- strReplaceBasedEditTool,
49
- localLsTool,
50
- localGlobTool,
51
- localGrepTool,
52
- ];
53
40
  /**
54
41
  * Create a Deep Agent with TypeScript types for all parameters.
55
42
  * Combines built-in tools with provided tools, creates task tool using createTaskTool(),
@@ -57,14 +44,12 @@ const LOCAL_BUILTIN_TOOLS = [
57
44
  * Ensures exact parameter matching and behavior with Python version.
58
45
  */
59
46
  export function createDeepAgent(params = {}) {
60
- const { tools = [], instructions, model = getDefaultModel(), subagents = [], isLocalFileSystem = false, postModelHook, } = params;
47
+ const { tools = [], instructions, model = getDefaultModel(), subagents = [], } = params;
61
48
  const stateSchema = params.stateSchema
62
49
  ? DeepAgentState.extend(params.stateSchema.shape)
63
50
  : DeepAgentState;
64
- // Choose tools based on filesystem type
65
- const builtinTools = isLocalFileSystem ? LOCAL_BUILTIN_TOOLS : BUILTIN_TOOLS;
66
51
  // Combine built-in tools with provided tools
67
- const allTools = [...builtinTools, ...tools];
52
+ const allTools = [...BUILTIN_TOOLS, ...tools];
68
53
  // Create task tool using createTaskTool() if subagents are provided
69
54
  if (subagents.length > 0) {
70
55
  // Create tools map for task tool creation
@@ -92,6 +77,5 @@ export function createDeepAgent(params = {}) {
92
77
  tools: allTools,
93
78
  stateSchema,
94
79
  messageModifier: finalInstructions,
95
- postModelHook,
96
80
  });
97
81
  }
package/dist/tools.d.ts CHANGED
@@ -43,13 +43,15 @@ export declare const writeTodos: import("@langchain/core/tools").DynamicStructur
43
43
  status: "pending" | "in_progress" | "completed";
44
44
  content: string;
45
45
  }[];
46
- }, Command<unknown, {
47
- todos: {
48
- status: "pending" | "in_progress" | "completed";
49
- content: string;
50
- }[];
51
- messages: ToolMessage[];
52
- }, string>>;
46
+ }, {
47
+ update: {
48
+ todos: {
49
+ status: "pending" | "in_progress" | "completed";
50
+ content: string;
51
+ }[];
52
+ messages: ToolMessage[];
53
+ };
54
+ }>;
53
55
  /**
54
56
  * List files tool - returns list of files from state.files
55
57
  * Equivalent to Python's ls function
package/dist/tools.js CHANGED
@@ -15,7 +15,7 @@ import { WRITE_TODOS_DESCRIPTION, EDIT_DESCRIPTION, TOOL_DESCRIPTION, } from "./
15
15
  * Uses getCurrentTaskInput() instead of Python's InjectedState
16
16
  */
17
17
  export const writeTodos = tool((input, config) => {
18
- return new Command({
18
+ return {
19
19
  update: {
20
20
  todos: input.todos,
21
21
  messages: [
@@ -25,7 +25,7 @@ export const writeTodos = tool((input, config) => {
25
25
  }),
26
26
  ],
27
27
  },
28
- });
28
+ };
29
29
  }, {
30
30
  name: "write_todos",
31
31
  description: WRITE_TODOS_DESCRIPTION,
package/dist/types.d.ts CHANGED
@@ -37,8 +37,6 @@ export interface CreateDeepAgentParams<StateSchema extends z.ZodObject<any, any,
37
37
  model?: LanguageModelLike;
38
38
  subagents?: SubAgent[];
39
39
  stateSchema?: StateSchema;
40
- isLocalFileSystem?: boolean;
41
- postModelHook?: (state: DeepAgentStateType, model: LanguageModelLike) => Promise<DeepAgentStateType>;
42
40
  }
43
41
  /**
44
42
  * Parameters for createTaskTool function
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepagents",
3
- "version": "0.0.0-rc.2",
3
+ "version": "0.0.1",
4
4
  "description": "Deep Agents - a library for building controllable AI agents with LangGraph",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -42,7 +42,6 @@
42
42
  "@langchain/anthropic": "^0.3.25",
43
43
  "@langchain/core": "^0.3.66",
44
44
  "@langchain/langgraph": "^0.4.2",
45
- "glob": "^11.0.3",
46
45
  "zod": "^3.25.32"
47
46
  },
48
47
  "devDependencies": {
@@ -1,9 +0,0 @@
1
- export declare const WRITE_TODOS_DESCRIPTION = "\nYou have access to this tool to help you manage and plan tasks. Use this tool VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress.\nThis tool is also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.\n\nIt is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.\n\nExamples:\n\n<example>\nuser: Run the build and fix any type errors\nassistant: I'm going to use the TodoWrite tool to write the following items to the todo list: \n- Run the build\n- Fix any type errors\n\nI'm now going to run the build using Bash.\n\nLooks like I found 10 type errors. I'm going to use the TodoWrite tool to write 10 items to the todo list.\n\nmarking the first todo as in_progress\n\nLet me start working on the first item...\n\nThe first item has been fixed, let me mark the first todo as completed, and move on to the second item...\n..\n..\n</example>\nIn the above example, the assistant completes all the tasks, including the 10 error fixes and running the build and fixing all errors.\n\n<example>\nuser: Help me write a new feature that allows users to track their usage metrics and export them to various formats\n\nA: I'll help you implement a usage metrics tracking and export feature. Let me first use the TodoWrite tool to plan this task.\nAdding the following todos to the todo list:\n1. Research existing metrics tracking in the codebase\n2. Design the metrics collection system\n3. Implement core metrics tracking functionality\n4. Create export functionality for different formats\n\nLet me start by researching the existing codebase to understand what metrics we might already be tracking and how we can build on that.\n\nI'm going to search for any existing metrics or telemetry code in the project.\n\nI've found some existing telemetry code. Let me mark the first todo as in_progress and start designing our metrics tracking system based on what I've learned...\n\n[Assistant continues implementing the feature step by step, marking todos as in_progress and completed as they go]\n</example>\n";
2
- export declare const EDIT_DESCRIPTION = "This is a tool for editing files. For moving or renaming files, you should generally use the Bash tool with the 'mv' command instead. For larger edits, use the Write tool to overwrite files.\n\nBefore using this tool:\n\nUse the Read tool to understand the file's contents and context\n\nVerify the directory path is correct (only applicable when creating new files):\n\nUse the LS tool to verify the parent directory exists and is the correct location\n\nTo make a file edit, provide the following:\n\nfile_path: The absolute path to the file to modify (must be absolute, not relative)\nold_string: The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)\nnew_string: The edited text to replace the old_string\n\nThe tool will replace ONE occurrence of old_string with new_string in the specified file.\n\nCRITICAL REQUIREMENTS FOR USING THIS TOOL:\n\nUNIQUENESS: The old_string MUST uniquely identify the specific instance you want to change. This means:\n\nInclude AT LEAST 3-5 lines of context BEFORE the change point\nInclude AT LEAST 3-5 lines of context AFTER the change point\nInclude all whitespace, indentation, and surrounding code exactly as it appears in the file\n\nSINGLE INSTANCE: This tool can only change ONE instance at a time. If you need to change multiple instances:\n\nMake separate calls to this tool for each instance\nEach call must uniquely identify its specific instance using extensive context\n\nVERIFICATION: Before using this tool:\n\nCheck how many instances of the target text exist in the file\nIf multiple instances exist, gather enough context to uniquely identify each one\nPlan separate tool calls for each instance\n\nWARNING: If you do not follow these requirements:\n\nThe tool will fail if old_string matches multiple locations\nThe tool will fail if old_string doesn't match exactly (including whitespace)\nYou may change the wrong instance if you don't include enough context\n\nWhen making edits:\n\nEnsure the edit results in idiomatic, correct code\nDo not leave the code in a broken state\nAlways use absolute file paths (starting with /)\n\nIf you want to create a new file, use:\n\nA new file path, including dir name if needed\nAn empty old_string\nThe new file's contents as new_string";
3
- export declare const TOOL_DESCRIPTION = "Reads a file from the local filesystem. You can access any file directly by using this tool.\nAssume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.\n\nUsage:\n- The file_path parameter must be an absolute path, not a relative path\n- By default, it reads up to 2000 lines starting from the beginning of the file\n- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters\n- Any lines longer than 2000 characters will be truncated\n- Results are returned using cat -n format, with line numbers starting at 1\n- You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful. \n- If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents\n\nCRITICAL: Always use absolute paths (starting with /)";
4
- export declare const GLOB_DESCRIPTION = "Find files and directories using glob patterns (similar to Unix glob/find commands).\n\nThis tool searches for files and directories that match specified patterns. It's ideal for finding files by name, extension, or path patterns.\n\nUsage:\n- pattern: Glob pattern to match (e.g., \"*.py\", \"**/*.js\", \"src/**/test_*.py\")\n- path: Directory to start search from (defaults to current directory \".\")\n- max_results: Maximum number of results to return (defaults to 100)\n- include_dirs: Include directories in results (defaults to False, files only)\n- recursive: Enable recursive search (defaults to True)\n\nGlob Pattern Examples:\n- \"*.py\" - All Python files in current directory\n- \"**/*.py\" - All Python files recursively\n- \"src/**/*.js\" - All JS files under src/ directory recursively \n- \"test_*.py\" - Files starting with \"test_\" and ending with \".py\"\n- \"**/node_modules\" - All node_modules directories\n- \"*.{py,js,ts}\" - Files with .py, .js, or .ts extensions\n\nReturns: List of matching file/directory paths, one per line\n\nCRITICAL: Always use absolute paths for the path parameter";
5
- export declare const GREP_DESCRIPTION = "A powerful search tool that uses ripgrep (rg) for fast text pattern matching.\n\nThis tool searches file contents for specified patterns and returns matching lines with context. It's ideal for finding specific content, functions, variables, or text across your codebase.\n\nUsage:\n- pattern: Text pattern to search for (supports regular expressions if regex=True)\n- files: List of file paths to search in, or single file path string\n- path: Directory to search in (alternative to files parameter)\n- file_pattern: Glob pattern for files to search (e.g., \"*.py\") when using path\n- max_results: Maximum number of matching lines to return (defaults to 50)\n- case_sensitive: Whether search should be case-sensitive (defaults to False)\n- context_lines: Number of lines to show before/after each match (defaults to 0)\n- regex: Treat pattern as regular expression (defaults to False)\n\nExamples:\n- Search for \"TODO\" in specific files: pattern=\"TODO\", files=[\"main.py\", \"utils.py\"]\n- Search in all Python files: pattern=\"def main\", path=\".\", file_pattern=\"*.py\"\n- Regex search: pattern=r\"function\\s+\\w+\", regex=True, file_pattern=\"*.js\"\n- Case-sensitive search: pattern=\"ClassName\", case_sensitive=True\n- With context: pattern=\"import\", context_lines=2\n\nReturns: File paths with line numbers and matching lines, plus context if requested\n\nCRITICAL: Always use absolute paths for files and path parameters";
6
- export declare const WRITE_DESCRIPTION = "Write a file to the local filesystem. Overwrites the existing file if there is one.\n\nBefore using this tool:\n\n- Use the Read tool to understand the file's contents and context.\n- ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly instructed to do so.\n\nDirectory Verification (only applicable when creating new files):\n\nUse the LS tool to verify the parent directory exists and is the correct location\n\nUsage:\n- file_path: Path to the file to write (absolute or relative path)\n- content: The content to write to the file\n\nThe tool will automatically create parent directories if they don't exist.\n\nCRITICAL: Always use absolute paths (starting with /)";
7
- export declare const STR_REPLACE_EDIT_DESCRIPTION = "A versatile text editor tool for viewing, editing, creating, and inserting content in files.\n\nThis tool provides multiple commands for different file operations:\n\nCommands:\n- view: Display file contents with line numbers or list directory contents\n- str_replace: Replace exact text matches in files (safer than edit_file for single replacements)\n- create: Create new files with specified content\n- insert: Insert text at specific line numbers\n\nUsage:\n- command: The operation to perform (view, str_replace, create, insert)\n- path: File or directory path (use absolute paths)\n- old_str: Exact string to replace (for str_replace)\n- new_str: Replacement string (for str_replace/insert)\n- view_range: [start_line, end_line] for viewing specific lines (1-indexed)\n- file_text: Content for new file (for create)\n- insert_line: Line number after which to insert (for insert, 0-indexed)\n\nExamples:\n- View file: command=\"view\", path=\"/path/to/file.py\"\n- View specific lines: command=\"view\", path=\"/path/to/file.py\", view_range=[10, 20]\n- Replace text: command=\"str_replace\", path=\"/path/to/file.py\", old_str=\"old text\", new_str=\"new text\"\n- Create file: command=\"create\", path=\"/path/to/new.py\", file_text=\"print('hello')\"\n- Insert line: command=\"insert\", path=\"/path/to/file.py\", insert_line=5, new_str=\"new line content\"\n\nCRITICAL: Always use absolute paths (starting with /)";
8
- export declare const TASK_DESCRIPTION_PREFIX = "Launch a new agent to handle complex, multi-step tasks autonomously. \n\nAvailable agent types and the tools they have access to:\n- general-purpose: General-purpose agent for researching complex questions, searching for files and content, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)\n{other_agents}\n";
9
- export declare const TASK_DESCRIPTION_SUFFIX = "When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.\n\nWhen to use the Agent tool:\n- When you are instructed to execute custom slash commands. Use the Agent tool with the slash command invocation as the entire prompt. The slash command can take arguments. For example: Task(description=\"Check the file\", prompt=\"/check-file path/to/file.py\")\n\nWhen NOT to use the Agent tool:\n- If you want to read a specific file path, use the Read or Glob tool instead of the Agent tool, to find the match more quickly\n- If you are searching for a specific term or definition within a known location, use the Glob tool instead, to find the match more quickly\n- If you are searching for content within a specific file or set of 2-3 files, use the Read tool instead of the Agent tool, to find the match more quickly\n- Other tasks that are not related to the agent descriptions above\n\nUsage notes:\n1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses\n2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.\n3. Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.\n4. The agent's outputs should generally be trusted\n5. Clearly tell the agent whether you expect it to create content, perform analysis, or just do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent\n6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.";
@@ -1,229 +0,0 @@
1
- export const WRITE_TODOS_DESCRIPTION = `
2
- You have access to this tool to help you manage and plan tasks. Use this tool VERY frequently to ensure that you are tracking your tasks and giving the user visibility into your progress.
3
- This tool is also EXTREMELY helpful for planning tasks, and for breaking down larger complex tasks into smaller steps. If you do not use this tool when planning, you may forget to do important tasks - and that is unacceptable.
4
-
5
- It is critical that you mark todos as completed as soon as you are done with a task. Do not batch up multiple tasks before marking them as completed.
6
-
7
- Examples:
8
-
9
- <example>
10
- user: Run the build and fix any type errors
11
- assistant: I'm going to use the TodoWrite tool to write the following items to the todo list:
12
- - Run the build
13
- - Fix any type errors
14
-
15
- I'm now going to run the build using Bash.
16
-
17
- Looks like I found 10 type errors. I'm going to use the TodoWrite tool to write 10 items to the todo list.
18
-
19
- marking the first todo as in_progress
20
-
21
- Let me start working on the first item...
22
-
23
- The first item has been fixed, let me mark the first todo as completed, and move on to the second item...
24
- ..
25
- ..
26
- </example>
27
- In the above example, the assistant completes all the tasks, including the 10 error fixes and running the build and fixing all errors.
28
-
29
- <example>
30
- user: Help me write a new feature that allows users to track their usage metrics and export them to various formats
31
-
32
- A: I'll help you implement a usage metrics tracking and export feature. Let me first use the TodoWrite tool to plan this task.
33
- Adding the following todos to the todo list:
34
- 1. Research existing metrics tracking in the codebase
35
- 2. Design the metrics collection system
36
- 3. Implement core metrics tracking functionality
37
- 4. Create export functionality for different formats
38
-
39
- Let me start by researching the existing codebase to understand what metrics we might already be tracking and how we can build on that.
40
-
41
- I'm going to search for any existing metrics or telemetry code in the project.
42
-
43
- I've found some existing telemetry code. Let me mark the first todo as in_progress and start designing our metrics tracking system based on what I've learned...
44
-
45
- [Assistant continues implementing the feature step by step, marking todos as in_progress and completed as they go]
46
- </example>
47
- `;
48
- export const EDIT_DESCRIPTION = `This is a tool for editing files. For moving or renaming files, you should generally use the Bash tool with the 'mv' command instead. For larger edits, use the Write tool to overwrite files.
49
-
50
- Before using this tool:
51
-
52
- Use the Read tool to understand the file's contents and context
53
-
54
- Verify the directory path is correct (only applicable when creating new files):
55
-
56
- Use the LS tool to verify the parent directory exists and is the correct location
57
-
58
- To make a file edit, provide the following:
59
-
60
- file_path: The absolute path to the file to modify (must be absolute, not relative)
61
- old_string: The text to replace (must be unique within the file, and must match the file contents exactly, including all whitespace and indentation)
62
- new_string: The edited text to replace the old_string
63
-
64
- The tool will replace ONE occurrence of old_string with new_string in the specified file.
65
-
66
- CRITICAL REQUIREMENTS FOR USING THIS TOOL:
67
-
68
- UNIQUENESS: The old_string MUST uniquely identify the specific instance you want to change. This means:
69
-
70
- Include AT LEAST 3-5 lines of context BEFORE the change point
71
- Include AT LEAST 3-5 lines of context AFTER the change point
72
- Include all whitespace, indentation, and surrounding code exactly as it appears in the file
73
-
74
- SINGLE INSTANCE: This tool can only change ONE instance at a time. If you need to change multiple instances:
75
-
76
- Make separate calls to this tool for each instance
77
- Each call must uniquely identify its specific instance using extensive context
78
-
79
- VERIFICATION: Before using this tool:
80
-
81
- Check how many instances of the target text exist in the file
82
- If multiple instances exist, gather enough context to uniquely identify each one
83
- Plan separate tool calls for each instance
84
-
85
- WARNING: If you do not follow these requirements:
86
-
87
- The tool will fail if old_string matches multiple locations
88
- The tool will fail if old_string doesn't match exactly (including whitespace)
89
- You may change the wrong instance if you don't include enough context
90
-
91
- When making edits:
92
-
93
- Ensure the edit results in idiomatic, correct code
94
- Do not leave the code in a broken state
95
- Always use absolute file paths (starting with /)
96
-
97
- If you want to create a new file, use:
98
-
99
- A new file path, including dir name if needed
100
- An empty old_string
101
- The new file's contents as new_string`;
102
- export const TOOL_DESCRIPTION = `Reads a file from the local filesystem. You can access any file directly by using this tool.
103
- Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that path is valid. It is okay to read a file that does not exist; an error will be returned.
104
-
105
- Usage:
106
- - The file_path parameter must be an absolute path, not a relative path
107
- - By default, it reads up to 2000 lines starting from the beginning of the file
108
- - You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to read the whole file by not providing these parameters
109
- - Any lines longer than 2000 characters will be truncated
110
- - Results are returned using cat -n format, with line numbers starting at 1
111
- - You have the capability to call multiple tools in a single response. It is always better to speculatively read multiple files as a batch that are potentially useful.
112
- - If you read a file that exists but has empty contents you will receive a system reminder warning in place of file contents
113
-
114
- CRITICAL: Always use absolute paths (starting with /)`;
115
- export const GLOB_DESCRIPTION = `Find files and directories using glob patterns (similar to Unix glob/find commands).
116
-
117
- This tool searches for files and directories that match specified patterns. It's ideal for finding files by name, extension, or path patterns.
118
-
119
- Usage:
120
- - pattern: Glob pattern to match (e.g., "*.py", "**/*.js", "src/**/test_*.py")
121
- - path: Directory to start search from (defaults to current directory ".")
122
- - max_results: Maximum number of results to return (defaults to 100)
123
- - include_dirs: Include directories in results (defaults to False, files only)
124
- - recursive: Enable recursive search (defaults to True)
125
-
126
- Glob Pattern Examples:
127
- - "*.py" - All Python files in current directory
128
- - "**/*.py" - All Python files recursively
129
- - "src/**/*.js" - All JS files under src/ directory recursively
130
- - "test_*.py" - Files starting with "test_" and ending with ".py"
131
- - "**/node_modules" - All node_modules directories
132
- - "*.{py,js,ts}" - Files with .py, .js, or .ts extensions
133
-
134
- Returns: List of matching file/directory paths, one per line
135
-
136
- CRITICAL: Always use absolute paths for the path parameter`;
137
- export const GREP_DESCRIPTION = `A powerful search tool that uses ripgrep (rg) for fast text pattern matching.
138
-
139
- This tool searches file contents for specified patterns and returns matching lines with context. It's ideal for finding specific content, functions, variables, or text across your codebase.
140
-
141
- Usage:
142
- - pattern: Text pattern to search for (supports regular expressions if regex=True)
143
- - files: List of file paths to search in, or single file path string
144
- - path: Directory to search in (alternative to files parameter)
145
- - file_pattern: Glob pattern for files to search (e.g., "*.py") when using path
146
- - max_results: Maximum number of matching lines to return (defaults to 50)
147
- - case_sensitive: Whether search should be case-sensitive (defaults to False)
148
- - context_lines: Number of lines to show before/after each match (defaults to 0)
149
- - regex: Treat pattern as regular expression (defaults to False)
150
-
151
- Examples:
152
- - Search for "TODO" in specific files: pattern="TODO", files=["main.py", "utils.py"]
153
- - Search in all Python files: pattern="def main", path=".", file_pattern="*.py"
154
- - Regex search: pattern=r"function\\s+\\w+", regex=True, file_pattern="*.js"
155
- - Case-sensitive search: pattern="ClassName", case_sensitive=True
156
- - With context: pattern="import", context_lines=2
157
-
158
- Returns: File paths with line numbers and matching lines, plus context if requested
159
-
160
- CRITICAL: Always use absolute paths for files and path parameters`;
161
- export const WRITE_DESCRIPTION = `Write a file to the local filesystem. Overwrites the existing file if there is one.
162
-
163
- Before using this tool:
164
-
165
- - Use the Read tool to understand the file's contents and context.
166
- - ALWAYS prefer editing existing files in the codebase. NEVER write new files unless explicitly instructed to do so.
167
-
168
- Directory Verification (only applicable when creating new files):
169
-
170
- Use the LS tool to verify the parent directory exists and is the correct location
171
-
172
- Usage:
173
- - file_path: Path to the file to write (absolute or relative path)
174
- - content: The content to write to the file
175
-
176
- The tool will automatically create parent directories if they don't exist.
177
-
178
- CRITICAL: Always use absolute paths (starting with /)`;
179
- export const STR_REPLACE_EDIT_DESCRIPTION = `A versatile text editor tool for viewing, editing, creating, and inserting content in files.
180
-
181
- This tool provides multiple commands for different file operations:
182
-
183
- Commands:
184
- - view: Display file contents with line numbers or list directory contents
185
- - str_replace: Replace exact text matches in files (safer than edit_file for single replacements)
186
- - create: Create new files with specified content
187
- - insert: Insert text at specific line numbers
188
-
189
- Usage:
190
- - command: The operation to perform (view, str_replace, create, insert)
191
- - path: File or directory path (use absolute paths)
192
- - old_str: Exact string to replace (for str_replace)
193
- - new_str: Replacement string (for str_replace/insert)
194
- - view_range: [start_line, end_line] for viewing specific lines (1-indexed)
195
- - file_text: Content for new file (for create)
196
- - insert_line: Line number after which to insert (for insert, 0-indexed)
197
-
198
- Examples:
199
- - View file: command="view", path="/path/to/file.py"
200
- - View specific lines: command="view", path="/path/to/file.py", view_range=[10, 20]
201
- - Replace text: command="str_replace", path="/path/to/file.py", old_str="old text", new_str="new text"
202
- - Create file: command="create", path="/path/to/new.py", file_text="print('hello')"
203
- - Insert line: command="insert", path="/path/to/file.py", insert_line=5, new_str="new line content"
204
-
205
- CRITICAL: Always use absolute paths (starting with /)`;
206
- export const TASK_DESCRIPTION_PREFIX = `Launch a new agent to handle complex, multi-step tasks autonomously.
207
-
208
- Available agent types and the tools they have access to:
209
- - general-purpose: General-purpose agent for researching complex questions, searching for files and content, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)
210
- {other_agents}
211
- `;
212
- export const TASK_DESCRIPTION_SUFFIX = `When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.
213
-
214
- When to use the Agent tool:
215
- - When you are instructed to execute custom slash commands. Use the Agent tool with the slash command invocation as the entire prompt. The slash command can take arguments. For example: Task(description="Check the file", prompt="/check-file path/to/file.py")
216
-
217
- When NOT to use the Agent tool:
218
- - If you want to read a specific file path, use the Read or Glob tool instead of the Agent tool, to find the match more quickly
219
- - If you are searching for a specific term or definition within a known location, use the Glob tool instead, to find the match more quickly
220
- - If you are searching for content within a specific file or set of 2-3 files, use the Read tool instead of the Agent tool, to find the match more quickly
221
- - Other tasks that are not related to the agent descriptions above
222
-
223
- Usage notes:
224
- 1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
225
- 2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
226
- 3. Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
227
- 4. The agent's outputs should generally be trusted
228
- 5. Clearly tell the agent whether you expect it to create content, perform analysis, or just do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent
229
- 6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.`;
@@ -1,177 +0,0 @@
1
- import { ToolMessage } from "@langchain/core/messages";
2
- import { z } from "zod";
3
- export interface ToolState {
4
- [key: string]: any;
5
- }
6
- export declare function ls(dirPath?: string, _state?: ToolState): string[];
7
- export declare function readFile(filePath: string, offset?: number, limit?: number, _state?: ToolState): string;
8
- export declare function writeFile(filePath: string, content: string, _state?: ToolState): string;
9
- export declare function glob(pattern: string, basePath?: string, maxResults?: number, includeDirs?: boolean, recursive?: boolean, _state?: ToolState): Promise<string>;
10
- export declare function grep(pattern: string, files?: string | string[], searchPath?: string, filePattern?: string, maxResults?: number, caseSensitive?: boolean, contextLines?: number, regex?: boolean, _state?: ToolState): Promise<string>;
11
- export declare function strReplaceBasedEditToolFunction(command: "view" | "str_replace" | "create" | "insert", filePath: string, oldStr?: string, newStr?: string, viewRange?: [number, number], fileText?: string, insertLine?: number, _state?: ToolState): string;
12
- export declare const localLsTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
13
- path: z.ZodDefault<z.ZodOptional<z.ZodString>>;
14
- }, "strip", z.ZodTypeAny, {
15
- path: string;
16
- }, {
17
- path?: string | undefined;
18
- }>, {
19
- path?: string;
20
- }, {
21
- path?: string | undefined;
22
- }, string[]>;
23
- export declare const localReadFileTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
24
- file_path: z.ZodString;
25
- offset: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
26
- limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
27
- }, "strip", z.ZodTypeAny, {
28
- file_path: string;
29
- offset: number;
30
- limit: number;
31
- }, {
32
- file_path: string;
33
- offset?: number | undefined;
34
- limit?: number | undefined;
35
- }>, {
36
- file_path: string;
37
- offset?: number;
38
- limit?: number;
39
- }, {
40
- file_path: string;
41
- offset?: number | undefined;
42
- limit?: number | undefined;
43
- }, string>;
44
- export declare const localWriteFileTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
45
- file_path: z.ZodString;
46
- content: z.ZodString;
47
- }, "strip", z.ZodTypeAny, {
48
- content: string;
49
- file_path: string;
50
- }, {
51
- content: string;
52
- file_path: string;
53
- }>, {
54
- file_path: string;
55
- content: string;
56
- }, {
57
- content: string;
58
- file_path: string;
59
- }, ToolMessage>;
60
- export declare const strReplaceBasedEditTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
61
- command: z.ZodEnum<["view", "str_replace", "create", "insert"]>;
62
- path: z.ZodString;
63
- view_range: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
64
- old_str: z.ZodOptional<z.ZodString>;
65
- new_str: z.ZodOptional<z.ZodString>;
66
- file_text: z.ZodOptional<z.ZodString>;
67
- insert_line: z.ZodOptional<z.ZodNumber>;
68
- }, "strip", z.ZodTypeAny, {
69
- path: string;
70
- command: "view" | "str_replace" | "create" | "insert";
71
- view_range?: [number, number] | undefined;
72
- old_str?: string | undefined;
73
- new_str?: string | undefined;
74
- file_text?: string | undefined;
75
- insert_line?: number | undefined;
76
- }, {
77
- path: string;
78
- command: "view" | "str_replace" | "create" | "insert";
79
- view_range?: [number, number] | undefined;
80
- old_str?: string | undefined;
81
- new_str?: string | undefined;
82
- file_text?: string | undefined;
83
- insert_line?: number | undefined;
84
- }>, {
85
- command: "view" | "str_replace" | "create" | "insert";
86
- path: string;
87
- view_range?: [number, number];
88
- old_str?: string;
89
- new_str?: string;
90
- file_text?: string;
91
- insert_line?: number;
92
- }, {
93
- path: string;
94
- command: "view" | "str_replace" | "create" | "insert";
95
- view_range?: [number, number] | undefined;
96
- old_str?: string | undefined;
97
- new_str?: string | undefined;
98
- file_text?: string | undefined;
99
- insert_line?: number | undefined;
100
- }, ToolMessage>;
101
- export declare const localGlobTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
102
- pattern: z.ZodString;
103
- base_path: z.ZodDefault<z.ZodOptional<z.ZodString>>;
104
- max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
105
- include_dirs: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
106
- recursive: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
107
- }, "strip", z.ZodTypeAny, {
108
- pattern: string;
109
- recursive: boolean;
110
- base_path: string;
111
- max_results: number;
112
- include_dirs: boolean;
113
- }, {
114
- pattern: string;
115
- recursive?: boolean | undefined;
116
- base_path?: string | undefined;
117
- max_results?: number | undefined;
118
- include_dirs?: boolean | undefined;
119
- }>, {
120
- pattern: string;
121
- base_path?: string;
122
- max_results?: number;
123
- include_dirs?: boolean;
124
- recursive?: boolean;
125
- }, {
126
- pattern: string;
127
- recursive?: boolean | undefined;
128
- base_path?: string | undefined;
129
- max_results?: number | undefined;
130
- include_dirs?: boolean | undefined;
131
- }, string>;
132
- export declare const localGrepTool: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
133
- pattern: z.ZodString;
134
- files: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
135
- search_path: z.ZodOptional<z.ZodString>;
136
- file_pattern: z.ZodDefault<z.ZodOptional<z.ZodString>>;
137
- max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
138
- case_sensitive: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
139
- context_lines: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
140
- regex: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
141
- }, "strip", z.ZodTypeAny, {
142
- pattern: string;
143
- max_results: number;
144
- file_pattern: string;
145
- case_sensitive: boolean;
146
- context_lines: number;
147
- regex: boolean;
148
- files?: string | string[] | undefined;
149
- search_path?: string | undefined;
150
- }, {
151
- pattern: string;
152
- files?: string | string[] | undefined;
153
- max_results?: number | undefined;
154
- search_path?: string | undefined;
155
- file_pattern?: string | undefined;
156
- case_sensitive?: boolean | undefined;
157
- context_lines?: number | undefined;
158
- regex?: boolean | undefined;
159
- }>, {
160
- pattern: string;
161
- files?: string | string[];
162
- search_path?: string;
163
- file_pattern?: string;
164
- max_results?: number;
165
- case_sensitive?: boolean;
166
- context_lines?: number;
167
- regex?: boolean;
168
- }, {
169
- pattern: string;
170
- files?: string | string[] | undefined;
171
- max_results?: number | undefined;
172
- search_path?: string | undefined;
173
- file_pattern?: string | undefined;
174
- case_sensitive?: boolean | undefined;
175
- context_lines?: number | undefined;
176
- regex?: boolean | undefined;
177
- }, string>;
@@ -1,498 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { spawn } from "child_process";
4
- import { promisify } from "util";
5
- import { glob as nodeGlob } from "glob";
6
- import { tool } from "@langchain/core/tools";
7
- import { ToolMessage } from "@langchain/core/messages";
8
- import { z } from "zod";
9
- import { TOOL_DESCRIPTION, WRITE_DESCRIPTION, GLOB_DESCRIPTION, GREP_DESCRIPTION, STR_REPLACE_EDIT_DESCRIPTION, } from "./local_prompts.js";
10
- const globAsync = promisify(nodeGlob);
11
- export function ls(dirPath = ".", _state) {
12
- try {
13
- if (!fs.existsSync(dirPath)) {
14
- return [`Error: Path '${dirPath}' does not exist`];
15
- }
16
- const stat = fs.statSync(dirPath);
17
- if (!stat.isDirectory()) {
18
- return [`Error: Path '${dirPath}' is not a directory`];
19
- }
20
- const items = fs.readdirSync(dirPath);
21
- return items.sort();
22
- }
23
- catch (e) {
24
- return [`Error listing directory: ${String(e)}`];
25
- }
26
- }
27
- export function readFile(filePath, offset = 0, limit = 2000, _state) {
28
- try {
29
- if (!fs.existsSync(filePath)) {
30
- return `Error: File '${filePath}' not found`;
31
- }
32
- const stat = fs.statSync(filePath);
33
- if (!stat.isFile()) {
34
- return `Error: '${filePath}' is not a file`;
35
- }
36
- let content;
37
- try {
38
- content = fs.readFileSync(filePath, "utf-8");
39
- }
40
- catch {
41
- // Try reading with error handling for non-UTF8 content
42
- const buffer = fs.readFileSync(filePath);
43
- content = buffer.toString("utf-8", 0, buffer.length);
44
- }
45
- if (!content || content.trim() === "") {
46
- return "System reminder: File exists but has empty contents";
47
- }
48
- const lines = content.split("\n");
49
- const startIdx = offset;
50
- const endIdx = Math.min(startIdx + limit, lines.length);
51
- if (startIdx >= lines.length) {
52
- return `Error: Line offset ${offset} exceeds file length (${lines.length} lines)`;
53
- }
54
- const resultLines = [];
55
- for (let i = startIdx; i < endIdx; i++) {
56
- let lineContent = lines[i];
57
- if (lineContent.length > 2000) {
58
- lineContent = lineContent.substring(0, 2000);
59
- }
60
- const lineNumber = i + 1;
61
- resultLines.push(`${lineNumber.toString().padStart(6)}\t${lineContent}`);
62
- }
63
- return resultLines.join("\n");
64
- }
65
- catch (e) {
66
- return `Error reading file: ${String(e)}`;
67
- }
68
- }
69
- export function writeFile(filePath, content, _state) {
70
- try {
71
- const dir = path.dirname(filePath);
72
- if (!fs.existsSync(dir)) {
73
- fs.mkdirSync(dir, { recursive: true });
74
- }
75
- fs.writeFileSync(filePath, content, "utf-8");
76
- return `Successfully wrote to file '${filePath}'`;
77
- }
78
- catch (e) {
79
- return `Error writing file: ${String(e)}`;
80
- }
81
- }
82
- export async function glob(pattern, basePath = ".", maxResults = 100, includeDirs = false, recursive = true, _state) {
83
- try {
84
- if (!fs.existsSync(basePath)) {
85
- return `Error: Path '${basePath}' does not exist`;
86
- }
87
- const stat = fs.statSync(basePath);
88
- if (!stat.isDirectory()) {
89
- return `Error: Path '${basePath}' is not a directory`;
90
- }
91
- const options = {
92
- cwd: basePath,
93
- absolute: true,
94
- dot: true,
95
- };
96
- const matches = await globAsync(pattern, options);
97
- const results = [];
98
- for (const match of matches) {
99
- if (results.length >= maxResults)
100
- break;
101
- const matchStat = fs.statSync(match);
102
- if (matchStat.isFile()) {
103
- results.push(match);
104
- }
105
- else if (matchStat.isDirectory() && includeDirs) {
106
- results.push(match + "/");
107
- }
108
- }
109
- results.sort();
110
- if (results.length === 0) {
111
- const searchType = recursive ? "recursive" : "non-recursive";
112
- const dirsNote = includeDirs ? " (including directories)" : "";
113
- return `No matches found for pattern '${pattern}' in '${basePath}' (${searchType} search${dirsNote})`;
114
- }
115
- const resultCount = results.length;
116
- let header = `Found ${resultCount} matches for pattern '${pattern}'`;
117
- if (resultCount >= maxResults) {
118
- header += ` (limited to ${maxResults} results)`;
119
- }
120
- header += ":\n\n";
121
- return header + results.join("\n");
122
- }
123
- catch (e) {
124
- return `Error in glob search: ${String(e)}`;
125
- }
126
- }
127
- export function grep(pattern, files, searchPath, filePattern = "*", maxResults = 50, caseSensitive = false, contextLines = 0, regex = false, _state) {
128
- return new Promise((resolve) => {
129
- try {
130
- if (!files && !searchPath) {
131
- resolve("Error: Must provide either 'files' parameter or 'searchPath' parameter");
132
- return;
133
- }
134
- const cmd = ["rg"];
135
- if (regex) {
136
- cmd.push("-e", pattern);
137
- }
138
- else {
139
- cmd.push("-F", pattern);
140
- }
141
- if (!caseSensitive) {
142
- cmd.push("-i");
143
- }
144
- if (contextLines > 0) {
145
- cmd.push("-C", contextLines.toString());
146
- }
147
- if (maxResults > 0) {
148
- cmd.push("-m", maxResults.toString());
149
- }
150
- if (filePattern !== "*") {
151
- cmd.push("-g", filePattern);
152
- }
153
- if (files) {
154
- if (typeof files === "string") {
155
- cmd.push(files);
156
- }
157
- else {
158
- cmd.push(...files);
159
- }
160
- }
161
- else if (searchPath) {
162
- cmd.push(searchPath);
163
- }
164
- const child = spawn(cmd[0], cmd.slice(1), {
165
- cwd: searchPath &&
166
- fs.existsSync(searchPath) &&
167
- fs.statSync(searchPath).isDirectory()
168
- ? searchPath
169
- : undefined,
170
- stdio: ["pipe", "pipe", "pipe"],
171
- });
172
- let stdout = "";
173
- let stderr = "";
174
- child.stdout.on("data", (data) => {
175
- stdout += data.toString();
176
- });
177
- child.stderr.on("data", (data) => {
178
- stderr += data.toString();
179
- });
180
- const timeout = setTimeout(() => {
181
- child.kill();
182
- resolve("Error: ripgrep search timed out");
183
- }, 30000);
184
- child.on("close", (code) => {
185
- clearTimeout(timeout);
186
- if (code === 0) {
187
- resolve(stdout);
188
- }
189
- else if (code === 1) {
190
- const patternDesc = regex
191
- ? `regex pattern '${pattern}'`
192
- : `text '${pattern}'`;
193
- const caseDesc = caseSensitive
194
- ? " (case-sensitive)"
195
- : " (case-insensitive)";
196
- resolve(`No matches found for ${patternDesc}${caseDesc}`);
197
- }
198
- else {
199
- resolve(`Error running ripgrep: ${stderr}`);
200
- }
201
- });
202
- child.on("error", (err) => {
203
- clearTimeout(timeout);
204
- if (err.message.includes("ENOENT")) {
205
- resolve("Error: ripgrep (rg) not found. Please install ripgrep to use this tool.");
206
- }
207
- else {
208
- resolve(`Error running ripgrep: ${String(err)}`);
209
- }
210
- });
211
- }
212
- catch (e) {
213
- resolve(`Error in grep search: ${String(e)}`);
214
- }
215
- });
216
- }
217
- export function strReplaceBasedEditToolFunction(command, filePath, oldStr, newStr, viewRange, fileText, insertLine, _state) {
218
- try {
219
- const pathObj = path.resolve(filePath);
220
- if (command === "view") {
221
- if (fs.existsSync(pathObj)) {
222
- const stat = fs.statSync(pathObj);
223
- if (stat.isDirectory()) {
224
- try {
225
- const items = fs.readdirSync(pathObj);
226
- const sortedItems = items
227
- .map((item) => {
228
- const itemPath = path.join(pathObj, item);
229
- const itemStat = fs.statSync(itemPath);
230
- return itemStat.isDirectory() ? `${item}/` : item;
231
- })
232
- .sort();
233
- return sortedItems.join("\n");
234
- }
235
- catch (e) {
236
- return `Error listing directory: ${String(e)}`;
237
- }
238
- }
239
- else if (stat.isFile()) {
240
- try {
241
- const content = fs.readFileSync(pathObj, "utf-8");
242
- const lines = content.split("\n");
243
- let selectedLines = lines;
244
- let startNum = 1;
245
- if (viewRange) {
246
- const [startLine, endLine] = viewRange;
247
- const startIdx = Math.max(0, startLine - 1);
248
- const endIdx = Math.min(lines.length, endLine);
249
- selectedLines = lines.slice(startIdx, endIdx);
250
- startNum = startLine;
251
- }
252
- const resultLines = selectedLines.map((line, i) => {
253
- const lineNum = startNum + i;
254
- return `${lineNum.toString().padStart(4)} | ${line}`;
255
- });
256
- return resultLines.length > 0
257
- ? resultLines.join("\n")
258
- : "File is empty";
259
- }
260
- catch {
261
- return `Error: File contains non-UTF-8 content`;
262
- }
263
- }
264
- }
265
- return `Error: Path '${filePath}' does not exist`;
266
- }
267
- else if (command === "str_replace") {
268
- if (!oldStr || newStr === undefined) {
269
- return "Error: str_replace requires both oldStr and newStr parameters";
270
- }
271
- if (!fs.existsSync(pathObj)) {
272
- return `Error: File '${filePath}' not found`;
273
- }
274
- const stat = fs.statSync(pathObj);
275
- if (!stat.isFile()) {
276
- return `Error: '${filePath}' is not a file`;
277
- }
278
- let content;
279
- try {
280
- content = fs.readFileSync(pathObj, "utf-8");
281
- }
282
- catch {
283
- return `Error: File contains non-UTF-8 content`;
284
- }
285
- if (!content.includes(oldStr)) {
286
- return `Error: String not found in file`;
287
- }
288
- const occurrences = (content.match(new RegExp(oldStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g")) || []).length;
289
- if (occurrences > 1) {
290
- return `Error: String appears ${occurrences} times. Please provide more specific context.`;
291
- }
292
- const newContent = content.replace(oldStr, newStr);
293
- fs.writeFileSync(pathObj, newContent, "utf-8");
294
- return `Successfully replaced text in '${filePath}'`;
295
- }
296
- else if (command === "create") {
297
- if (fileText === undefined) {
298
- return "Error: create command requires fileText parameter";
299
- }
300
- if (fs.existsSync(pathObj)) {
301
- return `Error: File '${filePath}' already exists`;
302
- }
303
- const dir = path.dirname(pathObj);
304
- if (!fs.existsSync(dir)) {
305
- fs.mkdirSync(dir, { recursive: true });
306
- }
307
- fs.writeFileSync(pathObj, fileText, "utf-8");
308
- return `Successfully created file '${filePath}'`;
309
- }
310
- else if (command === "insert") {
311
- if (newStr === undefined || insertLine === undefined) {
312
- return "Error: insert command requires both newStr and insertLine parameters";
313
- }
314
- if (!fs.existsSync(pathObj)) {
315
- return `Error: File '${filePath}' not found`;
316
- }
317
- const stat = fs.statSync(pathObj);
318
- if (!stat.isFile()) {
319
- return `Error: '${filePath}' is not a file`;
320
- }
321
- let content;
322
- try {
323
- content = fs.readFileSync(pathObj, "utf-8");
324
- }
325
- catch {
326
- return `Error: File contains non-UTF-8 content`;
327
- }
328
- const lines = content.split("\n");
329
- if (insertLine < 0 || insertLine > lines.length) {
330
- return `Error: insertLine ${insertLine} out of range (0-${lines.length})`;
331
- }
332
- let insertText = newStr;
333
- if (insertText && !insertText.endsWith("\n")) {
334
- insertText += "\n";
335
- }
336
- lines.splice(insertLine, 0, insertText.slice(0, -1)); // Remove the added newline since split/join handles it
337
- fs.writeFileSync(pathObj, lines.join("\n"), "utf-8");
338
- return `Successfully inserted text at line ${insertLine} in '${filePath}'`;
339
- }
340
- else {
341
- return `Error: Unknown command '${command}'`;
342
- }
343
- }
344
- catch (e) {
345
- return `Error: ${String(e)}`;
346
- }
347
- }
348
- // LangChain tool wrappers
349
- export const localLsTool = tool((input) => {
350
- return ls(input.path);
351
- }, {
352
- name: "ls",
353
- description: "List files and directories in the local filesystem",
354
- schema: z.object({
355
- path: z
356
- .string()
357
- .optional()
358
- .default(".")
359
- .describe("Directory path to list"),
360
- }),
361
- });
362
- export const localReadFileTool = tool((input) => {
363
- return readFile(input.file_path, input.offset, input.limit);
364
- }, {
365
- name: "read_file",
366
- description: TOOL_DESCRIPTION,
367
- schema: z.object({
368
- file_path: z.string().describe("Absolute path to the file to read"),
369
- offset: z
370
- .number()
371
- .optional()
372
- .default(0)
373
- .describe("Line offset to start reading from"),
374
- limit: z
375
- .number()
376
- .optional()
377
- .default(2000)
378
- .describe("Maximum number of lines to read"),
379
- }),
380
- });
381
- export const localWriteFileTool = tool((input, config) => {
382
- const result = writeFile(input.file_path, input.content);
383
- return new ToolMessage({
384
- content: result,
385
- tool_call_id: config.toolCall?.id,
386
- });
387
- }, {
388
- name: "write_file",
389
- description: WRITE_DESCRIPTION,
390
- schema: z.object({
391
- file_path: z.string().describe("Absolute path to the file to write"),
392
- content: z.string().describe("Content to write to the file"),
393
- }),
394
- });
395
- export const strReplaceBasedEditTool = tool((input, config) => {
396
- const result = strReplaceBasedEditToolFunction(input.command, input.path, input.old_str, input.new_str, input.view_range, input.file_text, input.insert_line);
397
- return new ToolMessage({
398
- content: result,
399
- tool_call_id: config.toolCall?.id,
400
- });
401
- }, {
402
- name: "str_replace_based_edit_tool",
403
- description: STR_REPLACE_EDIT_DESCRIPTION,
404
- schema: z.object({
405
- command: z
406
- .enum(["view", "str_replace", "create", "insert"])
407
- .describe("Action to perform"),
408
- path: z.string().describe("Absolute path to the file"),
409
- view_range: z
410
- .tuple([z.number(), z.number()])
411
- .optional()
412
- .describe("Line range for view command [start, end]"),
413
- old_str: z
414
- .string()
415
- .optional()
416
- .describe("String to replace (required for str_replace)"),
417
- new_str: z
418
- .string()
419
- .optional()
420
- .describe("Replacement string (required for str_replace and insert)"),
421
- file_text: z
422
- .string()
423
- .optional()
424
- .describe("Content for new file (required for create)"),
425
- insert_line: z
426
- .number()
427
- .optional()
428
- .describe("Line number to insert at (required for insert)"),
429
- }),
430
- });
431
- export const localGlobTool = tool(async (input) => {
432
- return await glob(input.pattern, input.base_path, input.max_results, input.include_dirs, input.recursive);
433
- }, {
434
- name: "glob",
435
- description: GLOB_DESCRIPTION,
436
- schema: z.object({
437
- pattern: z.string().describe("Glob pattern to match files"),
438
- base_path: z
439
- .string()
440
- .optional()
441
- .default(".")
442
- .describe("Base directory to search from"),
443
- max_results: z
444
- .number()
445
- .optional()
446
- .default(100)
447
- .describe("Maximum number of results"),
448
- include_dirs: z
449
- .boolean()
450
- .optional()
451
- .default(false)
452
- .describe("Include directories in results"),
453
- recursive: z
454
- .boolean()
455
- .optional()
456
- .default(true)
457
- .describe("Recursive search"),
458
- }),
459
- });
460
- export const localGrepTool = tool(async (input) => {
461
- return await grep(input.pattern, input.files, input.search_path, input.file_pattern, input.max_results, input.case_sensitive, input.context_lines, input.regex);
462
- }, {
463
- name: "grep",
464
- description: GREP_DESCRIPTION,
465
- schema: z.object({
466
- pattern: z.string().describe("Text pattern to search for"),
467
- files: z
468
- .union([z.string(), z.array(z.string())])
469
- .optional()
470
- .describe("Specific files to search in"),
471
- search_path: z.string().optional().describe("Directory to search in"),
472
- file_pattern: z
473
- .string()
474
- .optional()
475
- .default("*")
476
- .describe("File pattern to filter by"),
477
- max_results: z
478
- .number()
479
- .optional()
480
- .default(50)
481
- .describe("Maximum number of results"),
482
- case_sensitive: z
483
- .boolean()
484
- .optional()
485
- .default(false)
486
- .describe("Case sensitive search"),
487
- context_lines: z
488
- .number()
489
- .optional()
490
- .default(0)
491
- .describe("Number of context lines around matches"),
492
- regex: z
493
- .boolean()
494
- .optional()
495
- .default(false)
496
- .describe("Use regex pattern matching"),
497
- }),
498
- });