diff --git a/pkg/tools/builtin/deferred.go b/pkg/tools/builtin/deferred.go index 7bd325dcb..2325c9b8b 100644 --- a/pkg/tools/builtin/deferred.go +++ b/pkg/tools/builtin/deferred.go @@ -68,13 +68,10 @@ func (d *DeferredToolset) HasSources() bool { func (d *DeferredToolset) Instructions() string { return `## Deferred Tool Loading -This agent has access to additional tools that can be discovered and loaded on-demand. +Additional tools can be discovered and loaded on-demand. -Use the search_tool to find available tools by name or description pattern. -When searching a tool, prefer to search by action keywords (e.g., "remote", "read", "write") rather than specific tool names. -Use single words to maximize matching results. - -Use the add_tool to activate a discovered tool for use.` +Use search_tool to find tools by action keywords (e.g., "remote", "read", "write"). Prefer single words to maximize matches. +Use add_tool to activate a discovered tool.` } type SearchToolArgs struct { diff --git a/pkg/tools/builtin/fetch.go b/pkg/tools/builtin/fetch.go index 935fbf203..d99cb3c4b 100644 --- a/pkg/tools/builtin/fetch.go +++ b/pkg/tools/builtin/fetch.go @@ -287,20 +287,9 @@ func WithTimeout(timeout time.Duration) FetchToolOption { } func (t *FetchTool) Instructions() string { - return `## "fetch" tool instructions + return `## Fetch Tool -This tool allows you to fetch content from HTTP and HTTPS URLs. - -FEATURES - -- Support for multiple URLs in a single call -- Returns response body and metadata (status code, content type, length) -- Specify the output format (text, markdown, html) -- Respects robots.txt restrictions - -USAGE TIPS -- Use single URLs for simple content fetching -- Use multiple URLs for batch operations` +Fetch content from HTTP/HTTPS URLs. Supports multiple URLs in a single call, output format selection (text, markdown, html), and respects robots.txt.` } func (t *FetchTool) Tools(context.Context) ([]tools.Tool, error) { diff --git a/pkg/tools/builtin/fetch_test.go b/pkg/tools/builtin/fetch_test.go index ed4f13e95..4815f92ae 100644 --- a/pkg/tools/builtin/fetch_test.go +++ b/pkg/tools/builtin/fetch_test.go @@ -77,7 +77,7 @@ func TestFetchTool_Instructions(t *testing.T) { instructions := tools.GetInstructions(tool) - assert.Contains(t, instructions, `"fetch" tool instructions`) + assert.Contains(t, instructions, "Fetch Tool") } func TestFetchTool_StartStop(t *testing.T) { diff --git a/pkg/tools/builtin/lsp.go b/pkg/tools/builtin/lsp.go index 195302bd5..397dcc634 100644 --- a/pkg/tools/builtin/lsp.go +++ b/pkg/tools/builtin/lsp.go @@ -357,62 +357,32 @@ func (t *LSPTool) Stop(ctx context.Context) error { func (t *LSPTool) Instructions() string { return `# LSP Code Intelligence Tools -These tools provide comprehensive code intelligence by connecting to a Language Server Protocol (LSP) server. -All tools are stateless - just call them with the file path and position. +Stateless code intelligence tools via Language Server Protocol. Just provide file path and position. ## Getting Started -At the start of every session working with code, you should use the lsp_workspace tool to learn about the workspace and verify the LSP server is available. This will tell you what language features are supported. +Use lsp_workspace at the start of every session to learn about the workspace and available capabilities. -EXAMPLE: lsp_workspace({}) +## Read Workflow -## Workflows +1. **Find symbols**: Use lsp_workspace_symbols for fuzzy search. Example: lsp_workspace_symbols({"query":"server"}) +2. **Understand file structure**: Use lsp_document_symbols for a hierarchical symbol list +3. **Inspect symbols**: Use lsp_hover for type signatures and documentation +4. **Navigate**: Use lsp_definition to jump to definitions +5. **Understand dependencies**: Use lsp_call_hierarchy (outgoing) or lsp_type_hierarchy (supertypes) -These guidelines should be followed when working with code. There are two workflows: the 'Read Workflow' for understanding code, and the 'Edit Workflow' for modifying code. +## Edit Workflow -### Read Workflow - -The goal of the read workflow is to understand the codebase. - -1. **Find relevant symbols**: If you're looking for a specific type, function, or variable, use lsp_workspace_symbols. This is a fuzzy search that will help you locate symbols even if you don't know the exact name or location. - EXAMPLE: search for the 'Server' type: lsp_workspace_symbols({"query":"server"}) - -2. **Understand a file's structure**: When you have a file path and want to understand its contents, use lsp_document_symbols to get a hierarchical list of all symbols in the file. - EXAMPLE: lsp_document_symbols({"file":"/path/to/server.go"}) - -3. **Understand a symbol**: When you need to understand what a symbol is, use lsp_hover to get its type signature, documentation, and other details. - EXAMPLE: lsp_hover({"file":"/path/to/server.go", "line": 42, "character": 15}) - -4. **Navigate to definitions**: Use lsp_definition to jump to where a symbol is defined. - EXAMPLE: lsp_definition({"file":"/path/to/server.go", "line": 42, "character": 15}) - -5. **Understand dependencies**: Use lsp_call_hierarchy (outgoing) to see what a function calls, or lsp_type_hierarchy (supertypes) to understand inheritance. - -### Edit Workflow - -The editing workflow is iterative. Cycle through these steps until the task is complete. - -1. **Read first**: Before making any edits, follow the Read Workflow to understand the relevant code. - -2. **Find references**: Before modifying the definition of any symbol, you MUST use lsp_references to find all references to that identifier. This is critical for understanding the impact of your change. Read the files containing references to evaluate if any further edits are required. - EXAMPLE: lsp_references({"file":"/path/to/server.go", "line": 42, "character": 15}) - -3. **Check implementations**: Before modifying an interface or abstract method, use lsp_implementations to find all concrete implementations that will need updates. - EXAMPLE: lsp_implementations({"file":"/path/to/interface.go", "line": 15, "character": 2}) - -4. **Make edits**: Make the required edits, including edits to references you identified. Don't proceed to the next step until all planned edits are complete. - -5. **Check for errors**: After every code modification, you MUST call lsp_diagnostics on the files you have edited. This tool will report any build or analysis errors. The tool may provide suggested quick fixes - review these and apply them if correct. - EXAMPLE: lsp_diagnostics({"file":"/path/to/server.go"}) - -6. **Fix errors**: If lsp_diagnostics reports errors, fix them. Use lsp_code_actions to get available quick fixes. Once you've applied a fix, re-run lsp_diagnostics to confirm the issue is resolved. It is OK to ignore 'hint' or 'info' diagnostics if they are not relevant to the current task. - -7. **Format code**: After all edits are complete and error-free, use lsp_format to ensure consistent code style. - EXAMPLE: lsp_format({"file":"/path/to/server.go"}) +1. **Read first**: Follow the Read Workflow to understand relevant code +2. **Find references**: Before modifying any symbol definition, you MUST use lsp_references to find all usages. Example: lsp_references({"file":"/path/to/file.go", "line": 42, "character": 15}) +3. **Check implementations**: Before modifying interfaces, use lsp_implementations to find all concrete implementations +4. **Make edits**: Apply all planned changes +5. **Check errors**: After every modification, you MUST call lsp_diagnostics on edited files. Use lsp_code_actions for suggested fixes. Ignore irrelevant hint/info diagnostics +6. **Format**: Once error-free, use lsp_format for consistent style ## Position Format -Line and character positions are 1-based (first line is line 1, first character is character 1).` +Line and character positions are 1-based.` } // WorkspaceArgs is empty - the workspace tool takes no arguments. @@ -434,331 +404,77 @@ func (t *LSPTool) Tools(context.Context) ([]tools.Tool, error) { { name: ToolNameLSPWorkspace, title: "Get Workspace Info", readOnly: true, params: tools.MustSchemaFor[WorkspaceArgs](), handler: tools.NewHandler(h.workspace), - description: `Get information about the current workspace and LSP server capabilities. - -Use this tool at the start of every session to understand the workspace layout and what language features are available. This helps you know which LSP tools will work. - -Takes no arguments. - -Output format: - Workspace Information: - - Root: /path/to/project - - Server: gopls v0.14.0 - - File types: .go - - Available Capabilities: - - Hover: Yes - - Go to Definition: Yes - - Find References: Yes - - Rename: Yes - - Code Actions: Yes - - Formatting: Yes - ... - -Example: - {}`, + description: `Get workspace info and LSP server capabilities. Use at session start to discover available features. Takes no arguments.`, }, { name: ToolNameLSPHover, title: "Get Symbol Info", readOnly: true, params: tools.MustSchemaFor[PositionArgs](), handler: tools.NewHandler(h.hover), - description: `Get type information and documentation for a symbol at a specific position. - -Returns the type signature, documentation, and any other hover information the language server provides for the symbol under the cursor. - -Output format: -- For functions: signature, parameter types, return type, and docstring -- For variables: type and any inline documentation -- For types: full type definition - -Example: To get info about a function call on line 42, character 15: - {"file": "/path/to/file.go", "line": 42, "character": 15}`, + description: `Get type signature, documentation, and hover info for a symbol at a given position.`, }, { name: ToolNameLSPDefinition, title: "Go to Definition", readOnly: true, params: tools.MustSchemaFor[PositionArgs](), handler: tools.NewHandler(h.definition), - description: `Find the definition location of a symbol at a specific position. - -Returns the file path and line number where the symbol is defined. Works for functions, variables, types, imports, etc. - -Output format: - Found N location(s): - - /path/to/file.go:123:5 - - /path/to/other.go:45:10 - -Example: To find where a function is defined: - {"file": "/path/to/file.go", "line": 42, "character": 15}`, + description: `Find the definition location of a symbol. Returns file path and line number.`, }, { name: ToolNameLSPReferences, title: "Find References", readOnly: true, params: tools.MustSchemaFor[ReferencesArgs](), handler: tools.NewHandler(h.references), - description: `Find all references to a symbol across the codebase. - -Returns all locations where the symbol at the given position is used. - -IMPORTANT: Before modifying the definition of any symbol, you MUST use this tool to find all references. This is critical for understanding the impact of your change. Read the files containing references to evaluate if any further edits are required. - -Output format: - Found N location(s): - - /path/to/file1.go:10:5 - - /path/to/file2.go:25:12 - - /path/to/file3.go:100:3 - -Example: To find all usages of a function: - {"file": "/path/to/file.go", "line": 42, "character": 15} - -Set include_declaration to false to exclude the symbol's definition from results.`, + description: `Find all references to a symbol across the codebase. IMPORTANT: You MUST use this before modifying any symbol definition. Set include_declaration to false to exclude the definition itself.`, }, { name: ToolNameLSPDocumentSymbols, title: "List File Symbols", readOnly: true, params: tools.MustSchemaFor[FileArgs](), handler: tools.NewHandler(h.documentSymbols), - description: `List all symbols defined in a file. - -Returns a hierarchical list of all functions, classes, methods, variables, constants, and other symbols in the file. - -Output format: - - Function main (line 10) - - Struct MyType (line 25) - - Method MyType.DoSomething (line 30) - - Field MyType.Name (line 26) - - Variable globalConfig (line 5) - -Example: To get an overview of a file's structure: - {"file": "/path/to/file.go"}`, + description: `List all symbols (functions, types, methods, variables, etc.) defined in a file as a hierarchical list.`, }, { name: ToolNameLSPWorkspaceSymbols, title: "Search Workspace Symbols", readOnly: true, params: tools.MustSchemaFor[WorkspaceSymbolsArgs](), handler: tools.NewHandler(h.workspaceSymbols), - description: `Search for symbols across the entire workspace/project. - -Returns symbols matching the query from all files in the project. Supports fuzzy matching - you don't need the exact name or location. This is the primary tool for locating symbols in a codebase. - -Output format: - - Function main (/path/to/main.go:10) [in package main] - - Struct Config (/path/to/config.go:25) - - Method Handler.ServeHTTP (/path/to/handler.go:50) [in Handler] - -Example: To find all functions containing "handle": - {"query": "handle"} - -Example: To find the 'Server' type: - {"query": "Server"} - -Leave query empty to list all symbols (may be slow on large projects).`, + description: `Search for symbols across the workspace using fuzzy matching. Primary tool for locating symbols.`, }, { name: ToolNameLSPDiagnostics, title: "Get Diagnostics", readOnly: true, params: tools.MustSchemaFor[FileArgs](), handler: tools.NewHandler(h.getDiagnostics), - description: `Get compiler errors, warnings, and hints for a file. - -Returns all diagnostics reported by the language server for the file, including syntax errors, type errors, unused variables, etc. - -IMPORTANT: After every code modification, you MUST call this tool on the files you have edited. This ensures your changes are valid and don't introduce errors. - -Output format: - Diagnostics for /path/to/file.go: - - [Error] Line 15: undefined: someFunction - - [Warning] Line 42: unused variable 'x' - - [Hint] Line 50: consider using short variable declaration - -If errors are reported, fix them. Use lsp_code_actions to get suggested quick fixes. It is OK to ignore 'hint' or 'info' diagnostics if they are not relevant to the current task. - -Example: To check for errors in a file: - {"file": "/path/to/file.go"}`, + description: `Get compiler errors, warnings, and hints for a file. IMPORTANT: You MUST call this after every code modification on edited files. Use lsp_code_actions for suggested fixes.`, }, { name: ToolNameLSPRename, title: "Rename Symbol", readOnly: false, params: tools.MustSchemaFor[RenameArgs](), handler: tools.NewHandler(h.rename), - description: `Rename a symbol across the entire workspace. - -Safely renames a variable, function, type, or other symbol everywhere it's used. The language server ensures all references are updated correctly. - -This is a WRITE operation that modifies files on disk. - -Before renaming: -1. Use lsp_hover to understand what the symbol is -2. Use lsp_references to see all locations that will be affected - -After renaming: -- Run lsp_diagnostics on modified files to verify the rename didn't break anything - -Output format: - Renamed 'oldName' to 'newName' - Modified 3 file(s): - - /path/to/file1.go (2 changes) - - /path/to/file2.go (1 change) - - /path/to/file3.go (1 change) - -Example: To rename a function from 'processData' to 'handleData': - {"file": "/path/to/file.go", "line": 42, "character": 6, "new_name": "handleData"}`, + description: `Rename a symbol across the entire workspace. WRITE operation - modifies files on disk. Run lsp_diagnostics on modified files afterward.`, }, { name: ToolNameLSPCodeActions, title: "Get Code Actions", readOnly: true, params: tools.MustSchemaFor[CodeActionsArgs](), handler: tools.NewHandler(h.codeActions), - description: `Get available code actions (quick fixes, refactorings) for a line or range. - -Returns a list of suggested actions like: -- Quick fixes for diagnostics (e.g., add missing import, fix typo) -- Refactorings (e.g., extract function, inline variable) -- Source actions (e.g., organize imports, generate code) - -Use this after lsp_diagnostics reports errors to get suggested fixes. Review the suggested fixes and apply them if they are correct. - -Output format: - Available code actions for /path/to/file.go:42: - 1. [quickfix] Add import "fmt" - 2. [refactor.extract] Extract to function - 3. [source.organizeImports] Organize imports - -Example: To get actions for line 42: - {"file": "/path/to/file.go", "start_line": 42} - -For a range of lines: - {"file": "/path/to/file.go", "start_line": 42, "end_line": 50}`, + description: `Get available code actions (quick fixes, refactorings) for a line or range. Use after lsp_diagnostics reports errors.`, }, { name: ToolNameLSPFormat, title: "Format File", readOnly: false, params: tools.MustSchemaFor[FileArgs](), handler: tools.NewHandler(h.format), - description: `Format a file according to language standards. - -Applies the language's standard formatting rules to the entire file. For example: -- Go: gofmt style -- Python: PEP 8 (via black, autopep8, etc.) -- TypeScript/JavaScript: prettier, eslint -- Rust: rustfmt - -This is a WRITE operation that modifies the file on disk. - -Use this after making changes to ensure consistent code style. Only format after lsp_diagnostics reports no errors. - -Output format: - Formatted /path/to/file.go - Applied 5 formatting changes - -Example: To format a file: - {"file": "/path/to/file.go"}`, + description: `Format a file according to language standards. WRITE operation - modifies the file on disk. Only format after lsp_diagnostics reports no errors.`, }, { name: ToolNameLSPCallHierarchy, title: "Call Hierarchy", readOnly: true, params: tools.MustSchemaFor[CallHierarchyArgs](), handler: tools.NewHandler(h.callHierarchy), - description: `Analyze the call hierarchy of a function or method. - -Returns either: -- Incoming calls: All functions/methods that call the target function -- Outgoing calls: All functions/methods that the target function calls - -Use this to understand code dependencies before refactoring: -- Use 'incoming' to find all callers before changing a function's signature -- Use 'outgoing' to understand what a function depends on - -Output format (incoming): - Incoming calls to 'processData': - - handleRequest (/path/to/handler.go:45) calls at lines 52, 67 - - main (/path/to/main.go:10) calls at line 15 - -Output format (outgoing): - Outgoing calls from 'processData': - - validateInput (/path/to/validate.go:20) - - transformData (/path/to/transform.go:30) - -Example: Find who calls a function: - {"file": "/path/to/file.go", "line": 42, "character": 6, "direction": "incoming"} - -Example: Find what a function calls: - {"file": "/path/to/file.go", "line": 42, "character": 6, "direction": "outgoing"}`, + description: `Analyze the call hierarchy of a function or method. Direction: 'incoming' (who calls this) or 'outgoing' (what this calls).`, }, { name: ToolNameLSPTypeHierarchy, title: "Type Hierarchy", readOnly: true, params: tools.MustSchemaFor[TypeHierarchyArgs](), handler: tools.NewHandler(h.typeHierarchy), - description: `Analyze the type hierarchy of a class, interface, or struct. - -Returns either: -- Supertypes: Parent types (interfaces implemented, base classes extended) -- Subtypes: Child types (classes that implement/extend this type) - -This is essential for understanding inheritance chains before refactoring. - -Output format (supertypes): - Supertypes of 'MyHandler': - - Handler (/path/to/handler.go:10) [Interface] - - BaseHandler (/path/to/base.go:20) [Class] - -Output format (subtypes): - Subtypes of 'Handler': - - MyHandler (/path/to/my_handler.go:15) [Class] - - MockHandler (/path/to/mock.go:8) [Class] - -Example: Find parent types: - {"file": "/path/to/file.go", "line": 10, "character": 6, "direction": "supertypes"} - -Example: Find child types: - {"file": "/path/to/file.go", "line": 10, "character": 6, "direction": "subtypes"}`, + description: `Analyze the type hierarchy. Direction: 'supertypes' (parent types) or 'subtypes' (child types).`, }, { name: ToolNameLSPImplementations, title: "Find Implementations", readOnly: true, params: tools.MustSchemaFor[PositionArgs](), handler: tools.NewHandler(h.implementations), - description: `Find all implementations of an interface or abstract method. - -Returns all concrete implementations of the symbol at the given position. -This differs from references in that it only returns actual implementations, -not usages or type references. - -IMPORTANT: Before modifying an interface or abstract method, you MUST use this tool to find all implementations that will need to be updated. This ensures interface changes are complete across the codebase. - -Output format: - Found 3 implementation(s): - - /path/to/handler.go:45:6 - - /path/to/mock_handler.go:12:6 - - /path/to/test_handler.go:8:6 - -Example: Find all implementations of an interface method: - {"file": "/path/to/interface.go", "line": 15, "character": 2}`, + description: `Find all concrete implementations of an interface or abstract method. IMPORTANT: You MUST use this before modifying interfaces to find all implementations needing updates.`, }, { name: ToolNameLSPSignatureHelp, title: "Signature Help", readOnly: true, params: tools.MustSchemaFor[PositionArgs](), handler: tools.NewHandler(h.signatureHelp), - description: `Get function signature and parameter information at a call site. - -Returns detailed information about function parameters when the cursor is inside -a function call. Shows which parameter is currently being typed. - -Output format: - Function: processData(ctx context.Context, data []byte, opts ...Option) error - - Parameters: - 1. ctx context.Context - The context for cancellation - 2. data []byte - The data to process [ACTIVE] - 3. opts ...Option - Optional configuration - - Currently typing parameter 2 of 3 - -Example: Get signature help inside a function call: - {"file": "/path/to/file.go", "line": 42, "character": 25} - -Tip: Position the cursor inside the parentheses of a function call.`, + description: `Get function signature and parameter information at a call site. Position the cursor inside a function call's parentheses.`, }, { name: ToolNameLSPInlayHints, title: "Inlay Hints", readOnly: true, params: tools.MustSchemaFor[InlayHintsArgs](), handler: tools.NewHandler(h.inlayHints), - description: `Get inlay hints (type annotations, parameter names) for a range of code. - -Returns hints that would be displayed inline in an editor, such as: -- Variable type annotations (for languages with type inference) -- Parameter names at call sites -- Return type hints -- Chained method type hints - -Output format: - Inlay hints for /path/to/file.go:10-50: - - Line 15, Col 10: ': string' (type) - - Line 20, Col 25: 'ctx:' (parameter) - - Line 20, Col 35: 'data:' (parameter) - - Line 30, Col 5: ': error' (type) - -Example: Get inlay hints for lines 10-50: - {"file": "/path/to/file.go", "start_line": 10, "end_line": 50} - -Example: Get all inlay hints in a file: - {"file": "/path/to/file.go"}`, + description: `Get inlay hints (type annotations, parameter names) for a file or line range. Omit start_line/end_line to get hints for the entire file.`, }, } diff --git a/pkg/tools/builtin/lsp_test.go b/pkg/tools/builtin/lsp_test.go index 2b404fb15..495804b85 100644 --- a/pkg/tools/builtin/lsp_test.go +++ b/pkg/tools/builtin/lsp_test.go @@ -3,7 +3,6 @@ package builtin import ( "encoding/json" "os/exec" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -68,14 +67,9 @@ func TestLSPTool_ToolDescriptions(t *testing.T) { // Each tool should have a non-empty description assert.NotEmpty(t, tool.Description, "Tool %s should have a description", tool.Name) - // Each description should be detailed (more than 100 chars) - assert.Greater(t, len(tool.Description), 100, - "Tool %s should have a detailed description, got: %s", tool.Name, tool.Description) - - // Each tool should mention the output format or example - assert.True(t, - strings.Contains(tool.Description, "Output format") || strings.Contains(tool.Description, "Example"), - "Tool %s should document output format or provide example", tool.Name) + // Each description should be meaningful (more than 50 chars) + assert.Greater(t, len(tool.Description), 50, + "Tool %s should have a meaningful description, got: %s", tool.Name, tool.Description) } } @@ -86,7 +80,7 @@ func TestLSPTool_Instructions(t *testing.T) { instructions := tool.Instructions() // Should mention the tools are stateless - assert.Contains(t, instructions, "stateless") + assert.Contains(t, instructions, "Stateless") // Should list available operations assert.Contains(t, instructions, "lsp_hover") diff --git a/pkg/tools/builtin/memory.go b/pkg/tools/builtin/memory.go index 1b2d67de9..666781fa0 100644 --- a/pkg/tools/builtin/memory.go +++ b/pkg/tools/builtin/memory.go @@ -49,11 +49,8 @@ type DeleteMemoryArgs struct { func (t *MemoryTool) Instructions() string { return `## Using the memory tool -Before taking any action or responding to the user use the "get_memories" tool to remember things about the user. -Do not talk about using the tool, just use it. - -## Rules -- Use the memory tool generously to remember things about the user.` +Before taking any action or responding, use "get_memories" to recall stored information about the user. +Use the memory tool generously to remember things about the user. Do not mention using this tool.` } func (t *MemoryTool) Tools(context.Context) ([]tools.Tool, error) { diff --git a/pkg/tools/builtin/shell_instructions.go b/pkg/tools/builtin/shell_instructions.go index 36d005946..5762225f9 100644 --- a/pkg/tools/builtin/shell_instructions.go +++ b/pkg/tools/builtin/shell_instructions.go @@ -7,162 +7,36 @@ Execute shell commands in the user's environment with full control over working ## Core Concepts -**Execution Context**: Commands run in the user's default shell with access to all environment variables and the current workspace. -On Windows, PowerShell (pwsh/powershell) is used when available; otherwise, cmd.exe is used. -On Unix-like systems, ${SHELL} is used or /bin/sh as fallback. - -**Working Directory Management**: -- Default execution location: working directory of the agent -- Override with "cwd" parameter for targeted command execution -- Supports both absolute and relative paths - -**Command Isolation**: Each tool call creates a fresh shell session - no state persists between executions. - -**Timeout Protection**: Commands have a default 30-second timeout to prevent hanging. For longer operations, specify a custom timeout. - -## Parameter Reference - -| Parameter | Type | Required | Description | -|-----------|--------|----------|-------------| -| cmd | string | Yes | Shell command to execute | -| cwd | string | Yes | Working directory (use "." for current) | -| timeout | int | No | Timeout in seconds (default: 30) | +- Commands run in the user's default shell (Unix: ${SHELL} or /bin/sh; Windows: pwsh/powershell or cmd.exe) +- Each call creates a fresh shell session — no state persists between executions +- Default working directory: agent's working directory. Override with "cwd" parameter (absolute or relative paths) +- Default timeout: 30s. Use "timeout" parameter for longer operations (e.g., builds, tests) ## Best Practices -### ✅ DO -- Leverage the "cwd" parameter for directory-specific commands, rather than cding within commands -- Quote arguments containing spaces or special characters -- Use pipes and redirections -- Write advanced scripts with heredocs, that replace a lot of simple commands or tool calls -- This tool is great at reading and writing multiple files at once -- Avoid writing shell scripts to the disk. Instead, use heredocs to pipe the script to the SHELL -- Use the timeout parameter for long-running operations (e.g., builds, tests) - -### Git Commits - -When user asks to create git commit - -- Add "Assisted-By: cagent" as a trailer line in the commit message -- Use the format: git commit -m "Your commit message" -m "" -m "Assisted-By: cagent" - -## Usage Examples - -**Basic command execution:** -{ "cmd": "ls -la", "cwd": "." } +- Use "cwd" instead of cd within commands +- Quote arguments with spaces or special characters +- Use pipes, redirections, and heredocs to combine operations +- Prefer inline heredocs over writing shell scripts to disk +- For git commits, add trailer: git commit -m "message" -m "" -m "Assisted-By: cagent" -**Long-running command with custom timeout:** -{ "cmd": "npm run build", "cwd": ".", "timeout": 120 } +## Examples -**Language-specific operations:** { "cmd": "go test ./...", "cwd": ".", "timeout": 180 } -{ "cmd": "npm install", "cwd": "frontend" } -{ "cmd": "python -m pytest tests/", "cwd": "backend", "timeout": 90 } - -**File operations:** -{ "cmd": "find . -name '*.go' -type f", "cwd": "." } { "cmd": "grep -r 'TODO' src/", "cwd": "." } - -**Process management:** -{ "cmd": "ps aux | grep node", "cwd": "." } -{ "cmd": "docker ps --format 'table {{.Names}}\t{{.Status}}'", "cwd": "." } - -**Complex pipelines:** -{ "cmd": "cat package.json | jq '.dependencies'", "cwd": "frontend" } - -**Bash scripts:** -{ "cmd": "cat << 'EOF' | ${SHELL} -echo Hello -EOF" } +{ "cmd": "cat << 'EOF' | ${SHELL}\necho Hello\nEOF" } ## Error Handling -Commands that exit with non-zero status codes will return error information along with any output produced before failure. -Commands that exceed their timeout will be terminated automatically. - ---- +Non-zero exit codes return error info with output. Timed-out commands are terminated automatically. # Background Jobs -Run long-running processes in the background while continuing with other tasks. Perfect for starting servers, watching files, or any process that needs to run alongside other operations. - -## When to Use Background Jobs - -**Use background jobs for:** -- Starting web servers, databases, or other services -- Running file watchers or live reload tools -- Long-running processes that other tasks depend on -- Commands that produce continuous output over time - -**Don't use background jobs for:** -- Quick commands that complete in seconds -- Commands where you need immediate results -- One-time operations (use regular shell tool instead) - -## Background Job Tools - -**run_background_job**: Start a command in the background -- Parameters: cmd (required), cwd (optional, defaults to ".") -- Returns: Job ID for tracking - -**list_background_jobs**: List all background jobs -- No parameters required -- Returns: Status, runtime, and command for each job - -**view_background_job**: View output of a specific job -- Parameters: job_id (required) -- Returns: Current output and job status - -**stop_background_job**: Stop a running job -- Parameters: job_id (required) -- Terminates the process and all child processes - -## Background Job Workflow - -**1. Start a background job:** -{ "cmd": "npm start", "cwd": "frontend" } -→ Returns job ID (e.g., "job_1731772800_1") - -**2. Check running jobs:** -Use list_background_jobs to see all jobs with their status - -**3. View job output:** -{ "job_id": "job_1731772800_1" } -→ Shows current output and status - -**4. Stop job when done:** -{ "job_id": "job_1731772800_1" } -→ Terminates the process and all child processes - -## Important Characteristics - -**Output Buffering**: Each job captures up to 10MB of output. Beyond this limit, new output is discarded to prevent memory issues. - -**Process Groups**: Background jobs and all their child processes are managed as a group. Stopping a job terminates the entire process tree. - -**Environment Inheritance**: Jobs inherit environment variables from when they are started. Changes after job start don't affect running jobs. - -**Automatic Cleanup**: All background jobs are automatically terminated when the agent stops. - -**Job Persistence**: Job history is kept in memory until agent stops. Completed jobs remain queryable. - -## Background Job Examples - -**Start a web server:** -{ "cmd": "python -m http.server 8000", "cwd": "." } - -**Start a development server:** -{ "cmd": "npm run dev", "cwd": "frontend" } - -**Run a file watcher:** -{ "cmd": "go run . watch", "cwd": "." } +Use background jobs for long-running processes (servers, watchers) that should run while other tasks are performed. -**Start a database:** -{ "cmd": "docker run --rm -p 5432:5432 postgres:latest", "cwd": "." } +- **run_background_job**: Start a command, returns job ID. Example: { "cmd": "npm run dev", "cwd": "frontend" } +- **list_background_jobs**: Show all jobs with status and runtime +- **view_background_job**: Get output and status of a job by job_id +- **stop_background_job**: Terminate a job and all its child processes by job_id -**Multiple services pattern:** -1. Start backend: run_background_job with server command -2. Start frontend: run_background_job with dev server -3. Perform tasks: use other tools while services run -4. Check logs: view_background_job to see service output -5. Cleanup: stop_background_job for each service (or let agent cleanup automatically)` +**Notes**: Output capped at 10MB per job. Jobs inherit env vars at start time. All jobs auto-terminate when the agent stops.` diff --git a/pkg/tools/builtin/tasks.go b/pkg/tools/builtin/tasks.go index a7aba64e0..4b674a872 100644 --- a/pkg/tools/builtin/tasks.go +++ b/pkg/tools/builtin/tasks.go @@ -108,22 +108,11 @@ func NewTasksTool(storagePath string) *TasksTool { func (t *TasksTool) Instructions() string { return `## Using the Tasks Tools -These tools provide persistent task management with priorities, dependencies, and status tracking. - -Tasks are persisted to a JSON file so they survive across sessions. - -### Key concepts: -- **Priority**: critical > high > medium > low -- **Status**: pending, in_progress, done, blocked -- **Dependencies**: A task is automatically blocked if any of its dependencies are not done -- **Effective status**: The computed status taking dependencies into account - -### Workflow: -1. Use create_task to create tasks with titles, descriptions, priorities, and dependencies -2. Use list_tasks to see all tasks sorted by priority (blocked tasks last) -3. Use next_task to find the highest-priority actionable task -4. Use update_task to change status, priority, or other fields as work progresses -5. Use add_dependency / remove_dependency to manage task ordering` +Persistent task management with priorities (critical > high > medium > low), statuses (pending, in_progress, done, blocked), and dependencies. + +Tasks are saved to a JSON file and survive across sessions. A task is automatically blocked if any dependency is not done. + +Workflow: create_task → list_tasks/next_task → update_task as work progresses. Use add_dependency/remove_dependency to manage ordering.` } func (t *TasksTool) load() taskStore { diff --git a/pkg/tools/builtin/think.go b/pkg/tools/builtin/think.go index 209b873cc..4eda4ed3f 100644 --- a/pkg/tools/builtin/think.go +++ b/pkg/tools/builtin/think.go @@ -35,14 +35,11 @@ func NewThinkTool() *ThinkTool { func (t *ThinkTool) Instructions() string { return `## Using the think tool -Before taking any action or responding to the user after receiving tool results, use the think tool as a scratchpad to: -- List the specific rules that apply to the current request +Use the think tool as a scratchpad before acting or responding. Use it to: +- List rules that apply to the current request - Check if all required information is collected -- Verify that the planned action complies with all policies -- Iterate over tool results for correctness - -## Rules -- Use the think tool generously to jot down thoughts and ideas.` +- Verify planned actions comply with policies +- Iterate over tool results for correctness` } func (t *ThinkTool) Tools(context.Context) ([]tools.Tool, error) { diff --git a/pkg/tools/builtin/user_prompt.go b/pkg/tools/builtin/user_prompt.go index 09c5aab22..64312064f 100644 --- a/pkg/tools/builtin/user_prompt.go +++ b/pkg/tools/builtin/user_prompt.go @@ -79,51 +79,17 @@ func (t *UserPromptTool) userPrompt(ctx context.Context, params UserPromptArgs) func (t *UserPromptTool) Instructions() string { return `## Using the user_prompt tool -Use the user_prompt tool when you need to ask the user a question or gather input interactively. -This tool displays a dialog to the user and waits for their response. - -### When to use this tool: -- When you need clarification from the user before proceeding -- When you need to collect specific information (e.g., credentials, preferences, choices) -- When the user needs to make a decision between multiple options - -### Schema support: -You can optionally provide a JSON schema to define the expected response structure: -- Object schemas with properties for collecting multiple fields -- Primitive type schemas (string, number, boolean) for simple inputs -- Enum types for presenting a list of choices -- Required fields to ensure necessary information is collected - -### Example schemas: - -Simple string input: -{ - "type": "string", - "title": "API Key", - "description": "Enter your API key" -} +Use user_prompt to ask the user a question or gather input when you need clarification, specific information, or a decision. -Multiple choice: -{ - "type": "string", - "enum": ["option1", "option2", "option3"], - "title": "Select an option" -} +Optionally provide a JSON schema to structure the expected response (object, primitive, or enum types). -Object with multiple fields: -{ - "type": "object", - "properties": { - "username": {"type": "string", "description": "Your username"}, - "remember": {"type": "boolean", "description": "Remember me"} - }, - "required": ["username"] -} +Example schema for multiple choice: +{"type": "string", "enum": ["option1", "option2"], "title": "Select an option"} + +Example schema for structured input: +{"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]} -### Response format: -The tool returns a JSON object with: -- action: "accept" (user provided response), "decline" (user declined), or "cancel" (user cancelled) -- content: The user's response data (only present when action is "accept")` +Response contains "action" (accept/decline/cancel) and "content" (user data, only when accepted).` } func (t *UserPromptTool) Tools(context.Context) ([]tools.Tool, error) {