From efb3b03390d40c41cf54a009230b8db3eac7072c Mon Sep 17 00:00:00 2001 From: Kieran Klukas Date: Tue, 12 May 2026 10:44:06 -0400 Subject: [PATCH] feat(prompts): remove long prompt option --- internal/agent/agent_tool.go | 4 +- internal/agent/agentic_fetch_tool.go | 4 +- internal/agent/templates/agent_tool.md | 14 -- internal/agent/templates/agentic_fetch.md | 63 --------- internal/agent/tools/crush_info.go | 4 +- internal/agent/tools/crush_logs.go | 4 +- internal/agent/tools/diagnostics.go | 4 +- internal/agent/tools/diagnostics.md | 25 +--- internal/agent/tools/download.go | 4 +- internal/agent/tools/download.md | 29 +--- internal/agent/tools/edit.go | 4 +- internal/agent/tools/edit.md | 148 +-------------------- internal/agent/tools/fetch.go | 4 +- internal/agent/tools/fetch.md | 46 +------ internal/agent/tools/glob.go | 4 +- internal/agent/tools/glob.md | 41 +----- internal/agent/tools/grep.go | 4 +- internal/agent/tools/grep.md | 50 +------ internal/agent/tools/job_kill.go | 4 +- internal/agent/tools/job_output.go | 4 +- internal/agent/tools/job_output.md | 23 +--- internal/agent/tools/list_mcp_resources.go | 4 +- internal/agent/tools/list_mcp_resources.md | 19 +-- internal/agent/tools/ls.go | 4 +- internal/agent/tools/ls.md | 35 +---- internal/agent/tools/lsp_restart.go | 4 +- internal/agent/tools/lsp_restart.md | 26 +--- internal/agent/tools/multiedit.go | 4 +- internal/agent/tools/multiedit.md | 126 +----------------- internal/agent/tools/read_mcp_resource.go | 4 +- internal/agent/tools/read_mcp_resource.md | 21 +-- internal/agent/tools/references.go | 4 +- internal/agent/tools/references.md | 27 +--- internal/agent/tools/sourcegraph.go | 4 +- internal/agent/tools/sourcegraph.md | 56 +------- internal/agent/tools/todos.go | 4 +- internal/agent/tools/todos.md | 91 +------------ internal/agent/tools/tools.go | 22 --- internal/agent/tools/view.go | 4 +- internal/agent/tools/view.md | 39 +----- internal/agent/tools/web_fetch.go | 4 +- internal/agent/tools/web_fetch.md | 29 +--- internal/agent/tools/web_search.go | 4 +- internal/agent/tools/web_search.md | 19 +-- internal/agent/tools/write.go | 4 +- internal/agent/tools/write.md | 31 +---- 46 files changed, 67 insertions(+), 1009 deletions(-) diff --git a/internal/agent/agent_tool.go b/internal/agent/agent_tool.go index 05278e40dca5b5bd53e2b4722fd6ad9e6917a888..b009d319a9475df5548438974c803c5130750c44 100644 --- a/internal/agent/agent_tool.go +++ b/internal/agent/agent_tool.go @@ -13,7 +13,7 @@ import ( ) //go:embed templates/agent_tool.md -var agentToolDescription []byte +var agentToolDescription string type AgentParams struct { Prompt string `json:"prompt" description:"The task for the agent to perform"` @@ -39,7 +39,7 @@ func (c *coordinator) agentTool(ctx context.Context) (fantasy.AgentTool, error) } return fantasy.NewParallelAgentTool( AgentToolName, - tools.FirstLineDescription(agentToolDescription), + agentToolDescription, func(ctx context.Context, params AgentParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.Prompt == "" { return fantasy.NewTextErrorResponse("prompt is required"), nil diff --git a/internal/agent/agentic_fetch_tool.go b/internal/agent/agentic_fetch_tool.go index 140ddd317b61ad36b39c9bd8f7e11beea3eebbe5..edd9370a4b5bd5934dec9537821cf079babbcfbb 100644 --- a/internal/agent/agentic_fetch_tool.go +++ b/internal/agent/agentic_fetch_tool.go @@ -17,7 +17,7 @@ import ( ) //go:embed templates/agentic_fetch.md -var agenticFetchToolDescription []byte +var agenticFetchToolDescription string // agenticFetchValidationResult holds the validated parameters from the tool call context. type agenticFetchValidationResult struct { @@ -65,7 +65,7 @@ func (c *coordinator) agenticFetchTool(_ context.Context, client *http.Client) ( return fantasy.NewParallelAgentTool( tools.AgenticFetchToolName, - tools.FirstLineDescription(agenticFetchToolDescription), + agenticFetchToolDescription, func(ctx context.Context, params tools.AgenticFetchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { validationResult, err := validateAgenticFetchParams(ctx, params) if err != nil { diff --git a/internal/agent/templates/agent_tool.md b/internal/agent/templates/agent_tool.md index e9c62f1031b77325e514d215a2c10d9f92c17ec7..36e6874e39947575b18382d0bf2272bd2faacb47 100644 --- a/internal/agent/templates/agent_tool.md +++ b/internal/agent/templates/agent_tool.md @@ -1,15 +1 @@ Launch a new agent that has access to the following tools: glob, grep, ls, view. When you are searching for a keyword or file and are not confident that you will find the right match on the first try, use the agent tool to perform the search for you. - - -- If you are searching for a keyword like "config" or "logger", or for questions like "which file does X?", the Agent tool is strongly recommended -- If you want to read a specific file path, use the View or GlobTool tool instead of the Agent tool, to find the match more quickly -- If you are searching for a specific class definition like "class Foo", use the GlobTool tool instead, to find the match more quickly - - - -1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses -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. -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. -4. The agent's outputs should generally be trusted -5. IMPORTANT: The agent can not use Bash, Replace, Edit, so can not modify files. If you want to use these tools, use them directly instead of going through the agent. - diff --git a/internal/agent/templates/agentic_fetch.md b/internal/agent/templates/agentic_fetch.md index 4911f8071a6a11db63810d82df230b130a1c8cbf..336a280bfbc5398b34553b9a8b0bc4544a184ce5 100644 --- a/internal/agent/templates/agentic_fetch.md +++ b/internal/agent/templates/agentic_fetch.md @@ -1,64 +1 @@ Fetch a URL or search the web using an AI sub-agent that can extract, summarize, and answer questions. Slower and costlier than fetch; use fetch for raw content or API responses. - - -Use this tool when you need to: -- Search the web for information (omit the url parameter) -- Extract specific information from a webpage (provide a url) -- Answer questions about web content -- Summarize or analyze web pages -- Research topics by searching and following links - -DO NOT use this tool when: -- You just need raw content without analysis (use fetch instead - faster and cheaper) -- You want direct access to API responses or JSON (use fetch instead) -- You don't need the content processed or interpreted (use fetch instead) - - - -- Provide a prompt describing what information you want to find or extract (required) -- Optionally provide a URL to fetch and analyze specific content -- If no URL is provided, the agent will search the web to find relevant information -- The tool spawns a sub-agent with web_search, web_fetch, and analysis tools -- Returns the agent's response about the content - - - -- prompt: What information you want to find or extract (required) -- url: The URL to fetch content from (optional - if not provided, agent will search the web) - - - -- IMPORTANT: If an MCP-provided web fetch tool is available, prefer using that tool instead of this one, as it may have fewer restrictions. All MCP-provided tools start with "mcp_". -- When using URL mode: The URL must be a fully-formed valid URL. HTTP URLs will be automatically upgraded to HTTPS. -- When searching: Just provide the prompt describing what you want to find - the agent will search and fetch relevant pages. -- The sub-agent can perform multiple searches and fetch multiple pages to gather comprehensive information. -- This tool is read-only and does not modify any files. -- Results will be summarized if the content is very large. -- This tool uses AI processing and costs more tokens than the simple fetch tool. - - - -- Max response size: 5MB per page -- Only supports HTTP and HTTPS protocols -- Cannot handle authentication or cookies -- Some websites may block automated requests -- Uses additional tokens for AI processing -- Search results depend on DuckDuckGo availability - - - -- Be specific in your prompt about what information you want -- For research tasks, omit the URL and let the agent search and follow relevant links -- For complex pages, ask the agent to focus on specific sections -- The agent has access to web_search, web_fetch, grep, and view tools -- If you just need raw content, use the fetch tool instead to save tokens - - - -Search for information: -- prompt: "What are the main new features in the latest Python release?" - -Fetch and analyze a URL: -- url: "https://docs.python.org/3/whatsnew/3.12.html" -- prompt: "Summarize the key changes in Python 3.12" - diff --git a/internal/agent/tools/crush_info.go b/internal/agent/tools/crush_info.go index e04b3c72508d3110a70a961f3aefa5be5e7a1eea..045d88c339a32a5821f8834e210d5e6637cc9e5b 100644 --- a/internal/agent/tools/crush_info.go +++ b/internal/agent/tools/crush_info.go @@ -17,7 +17,7 @@ import ( const CrushInfoToolName = "crush_info" //go:embed crush_info.md -var crushInfoDescription []byte +var crushInfoDescription string type CrushInfoParams struct{} @@ -30,7 +30,7 @@ func NewCrushInfoTool( ) fantasy.AgentTool { return fantasy.NewAgentTool( CrushInfoToolName, - string(crushInfoDescription), + crushInfoDescription, func(ctx context.Context, _ CrushInfoParams, _ fantasy.ToolCall) (fantasy.ToolResponse, error) { return fantasy.NewTextResponse(buildCrushInfo(cfg, lspManager, allSkills, activeSkills, skillTracker)), nil }) diff --git a/internal/agent/tools/crush_logs.go b/internal/agent/tools/crush_logs.go index cb12dd50702e2b209e3e0d87fdbc0f6156c9645d..1de2ea3fc8ec1423d0b21f6099766b8f30880e01 100644 --- a/internal/agent/tools/crush_logs.go +++ b/internal/agent/tools/crush_logs.go @@ -19,7 +19,7 @@ import ( const CrushLogsToolName = "crush_logs" //go:embed crush_logs.md -var crushLogsDescription []byte +var crushLogsDescription string // Max line size to prevent memory issues with very long log lines (1 MB). const maxLogLineSize = 1024 * 1024 @@ -58,7 +58,7 @@ type CrushLogsParams struct { func NewCrushLogsTool(logFile string) fantasy.AgentTool { return fantasy.NewAgentTool( CrushLogsToolName, - string(crushLogsDescription), + crushLogsDescription, func(ctx context.Context, params CrushLogsParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { result := runCrushLogs(logFile, params) return fantasy.NewTextResponse(result), nil diff --git a/internal/agent/tools/diagnostics.go b/internal/agent/tools/diagnostics.go index 5d729d53daf7df92bdde734521b5796eec64fa32..561ba176bd47700c2fec67418e4e1d6cc145a2df 100644 --- a/internal/agent/tools/diagnostics.go +++ b/internal/agent/tools/diagnostics.go @@ -22,12 +22,12 @@ type DiagnosticsParams struct { const DiagnosticsToolName = "lsp_diagnostics" //go:embed diagnostics.md -var diagnosticsDescription []byte +var diagnosticsDescription string func NewDiagnosticsTool(lspManager *lsp.Manager) fantasy.AgentTool { return fantasy.NewAgentTool( DiagnosticsToolName, - FirstLineDescription(diagnosticsDescription), + diagnosticsDescription, func(ctx context.Context, params DiagnosticsParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if lspManager.Clients().Len() == 0 { return fantasy.NewTextErrorResponse("no LSP clients available"), nil diff --git a/internal/agent/tools/diagnostics.md b/internal/agent/tools/diagnostics.md index 3f59a673ebdf25c924e45295b72d46911bb753df..284224a8a704f0734c2c62e1e8eec091ecd021e4 100644 --- a/internal/agent/tools/diagnostics.md +++ b/internal/agent/tools/diagnostics.md @@ -1,24 +1 @@ -Get LSP errors, warnings, and hints for a file or the whole project. - - -- Provide file path to get diagnostics for that file -- Leave path empty to get diagnostics for entire project -- Results displayed in structured format with severity levels - - - -- Displays errors, warnings, and hints -- Groups diagnostics by severity -- Provides detailed information about each diagnostic - - - -- Results limited to diagnostics provided by LSP clients -- May not cover all possible code issues -- Does not provide suggestions for fixing issues - - - -- Use with other tools for comprehensive code review -- Combine with LSP client for real-time diagnostics - +Get LSP errors, warnings, and hints for a file or the whole project. \ No newline at end of file diff --git a/internal/agent/tools/download.go b/internal/agent/tools/download.go index 340282ebdb3b48b40be7ac51a3da26e3c304388d..2cb7e19d61533cc44ca6cd9d1a235ef0c8f08b6a 100644 --- a/internal/agent/tools/download.go +++ b/internal/agent/tools/download.go @@ -32,7 +32,7 @@ type DownloadPermissionsParams struct { const DownloadToolName = "download" //go:embed download.md -var downloadDescription []byte +var downloadDescription string func NewDownloadTool(permissions permission.Service, workingDir string, client *http.Client) fantasy.AgentTool { if client == nil { @@ -48,7 +48,7 @@ func NewDownloadTool(permissions permission.Service, workingDir string, client * } return fantasy.NewParallelAgentTool( DownloadToolName, - FirstLineDescription(downloadDescription), + downloadDescription, func(ctx context.Context, params DownloadParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.URL == "" { return fantasy.NewTextErrorResponse("URL parameter is required"), nil diff --git a/internal/agent/tools/download.md b/internal/agent/tools/download.md index 5bfb4d1c66cd83e04a5f98930deb41ed90b6a876..3adb507e4d49300c8dbd2e263c9f9ec65f54629e 100644 --- a/internal/agent/tools/download.md +++ b/internal/agent/tools/download.md @@ -1,28 +1 @@ -Download a URL directly to a local file (binary-safe, streaming, max 100MB); overwrites without warning. For reading content into context use fetch. - - -- Provide URL to download from -- Specify local file path where content should be saved -- Optional timeout for request - - - -- Downloads any file type (binary or text) -- Auto-creates parent directories if missing -- Handles large files efficiently with streaming -- Sets reasonable timeouts to prevent hanging -- Validates input parameters before requests - - - -- Max file size: 100MB -- Only supports HTTP and HTTPS protocols -- Cannot handle authentication or cookies -- Some websites may block automated requests -- Will overwrite existing files without warning - - - -- Use absolute paths or paths relative to working directory -- Set appropriate timeouts for large files or slow connections - +Download a URL directly to a local file (binary-safe, streaming, max 100MB); overwrites without warning. For reading content into context use fetch. \ No newline at end of file diff --git a/internal/agent/tools/edit.go b/internal/agent/tools/edit.go index 592fbc76886a37906bad0f66e9de8077901d1ce9..afafe1bc7f7be77e92e9e257458dcaee8a87e5be 100644 --- a/internal/agent/tools/edit.go +++ b/internal/agent/tools/edit.go @@ -49,7 +49,7 @@ var ( ) //go:embed edit.md -var editDescription []byte +var editDescription string type editContext struct { ctx context.Context @@ -68,7 +68,7 @@ func NewEditTool( ) fantasy.AgentTool { return fantasy.NewAgentTool( EditToolName, - FirstLineDescription(editDescription), + editDescription, func(ctx context.Context, params EditParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.FilePath == "" { return fantasy.NewTextErrorResponse("file_path is required"), nil diff --git a/internal/agent/tools/edit.md b/internal/agent/tools/edit.md index 3e5fa1cf6bcb004a6792a0ced504998bc8d6f0b8..4c1b18eed8d85deb517bdb117ef17a30a41961c0 100644 --- a/internal/agent/tools/edit.md +++ b/internal/agent/tools/edit.md @@ -1,147 +1 @@ -Edit a file by exact find-and-replace; can also create or delete content. For renames/moves use bash. For large edits use write. - - -1. Use View tool to understand file contents and context -2. For new files: Use LS tool to verify parent directory exists -3. **CRITICAL**: Note exact whitespace, indentation, and formatting from View output - - - -1. file_path: Absolute path to file (required) -2. old_string: Text to replace (must match exactly including whitespace/indentation) -3. new_string: Replacement text -4. replace_all: Replace all occurrences (default false) - - - - -- Create file: provide file_path + new_string, leave old_string empty -- Delete content: provide file_path + old_string, leave new_string empty - - - -EXACT MATCHING: The tool is extremely literal. Text must match **EXACTLY** - -- Every space and tab character -- Every blank line -- Every newline character -- Indentation level (count the spaces/tabs) -- Comment spacing (`// comment` vs `//comment`) -- Brace positioning (`func() {` vs `func(){`) - -Common failures: - -``` -Expected: " func foo() {" (4 spaces) -Provided: " func foo() {" (2 spaces) ❌ FAILS - -Expected: "}\n\nfunc bar() {" (2 newlines) -Provided: "}\nfunc bar() {" (1 newline) ❌ FAILS - -Expected: "// Comment" (space after //) -Provided: "//Comment" (no space) ❌ FAILS -``` - -UNIQUENESS (when replace_all=false): old_string MUST uniquely identify target instance - -- Include 3-5 lines context BEFORE and AFTER change point -- Include exact whitespace, indentation, surrounding code -- If text appears multiple times, add more context to make it unique - -SINGLE INSTANCE: Tool changes ONE instance when replace_all=false - -- For multiple instances: set replace_all=true OR make separate calls with unique context -- Plan calls carefully to avoid conflicts - -VERIFICATION BEFORE USING: Before every edit - -1. View the file and locate exact target location -2. Check how many instances of target text exist -3. Copy the EXACT text including all whitespace -4. Verify you have enough context for unique identification -5. Double-check indentation matches (count spaces/tabs) -6. Plan separate calls or use replace_all for multiple changes - - - -Tool fails if: -- old_string matches multiple locations and replace_all=false -- old_string doesn't match exactly (including whitespace) -- Insufficient context causes wrong instance change -- Indentation is off by even one space -- Missing or extra blank lines -- Wrong tabs vs spaces - - - -If you get "old_string not found in file": - -1. **View the file again** at the specific location -2. **Copy more context** - include entire function if needed -3. **Check whitespace**: - - Count indentation spaces/tabs - - Look for blank lines - - Check for trailing spaces -4. **Verify character-by-character** that your old_string matches -5. **Never guess** - always View the file to get exact text - - - - -- Ensure edits result in correct, idiomatic code -- Don't leave code in broken state -- Use absolute file paths (starting with /) -- Use forward slashes (/) for cross-platform compatibility -- Multiple edits to same file: send all in single message with multiple tool calls -- **When in doubt, include MORE context rather than less** -- Match the existing code style exactly (spaces, tabs, blank lines) - - - -Before submitting an edit, verify: - -- [ ] Viewed the file first -- [ ] Counted indentation spaces/tabs -- [ ] Included blank lines if they exist -- [ ] Matched brace/bracket positioning -- [ ] Included 3-5 lines of surrounding context -- [ ] Verified text appears exactly once (or using replace_all) -- [ ] Copied text character-for-character, not approximated - - - -✅ Correct: Exact match with context - -``` -old_string: "func ProcessData(input string) error {\n if input == \"\" {\n return errors.New(\"empty input\")\n }\n return nil\n}" - -new_string: "func ProcessData(input string) error {\n if input == \"\" {\n return errors.New(\"empty input\")\n }\n // New validation\n if len(input) > 1000 {\n return errors.New(\"input too long\")\n }\n return nil\n}" -``` - -❌ Incorrect: Not enough context - -``` -old_string: "return nil" // Appears many times! -``` - -❌ Incorrect: Wrong indentation - -``` -old_string: " if input == \"\" {" // 2 spaces -// But file actually has: " if input == \"\" {" // 4 spaces -``` - -✅ Correct: Including context to make unique - -``` -old_string: "func ProcessData(input string) error {\n if input == \"\" {\n return errors.New(\"empty input\")\n }\n return nil" -``` - - - - - -- Forward slashes work throughout (C:/path/file) -- File permissions handled automatically -- Line endings converted automatically (\n ↔ \r\n) - +Edit a file by exact find-and-replace; can also create or delete content. For renames/moves use bash. For large edits use write. \ No newline at end of file diff --git a/internal/agent/tools/fetch.go b/internal/agent/tools/fetch.go index d547c57bf14585919b00988a4c1503e6b17893b1..d58a37fdd046ba0113d7f6935548c712be2fefde 100644 --- a/internal/agent/tools/fetch.go +++ b/internal/agent/tools/fetch.go @@ -22,7 +22,7 @@ const ( ) //go:embed fetch.md -var fetchDescription []byte +var fetchDescription string func NewFetchTool(permissions permission.Service, workingDir string, client *http.Client) fantasy.AgentTool { if client == nil { @@ -39,7 +39,7 @@ func NewFetchTool(permissions permission.Service, workingDir string, client *htt return fantasy.NewParallelAgentTool( FetchToolName, - FirstLineDescription(fetchDescription), + fetchDescription, func(ctx context.Context, params FetchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.URL == "" { return fantasy.NewTextErrorResponse("URL parameter is required"), nil diff --git a/internal/agent/tools/fetch.md b/internal/agent/tools/fetch.md index f468fe9706cf30c21bba2fef826f093cda9d8b70..151c7f36c83a923abfae3574eacf31acc96de978 100644 --- a/internal/agent/tools/fetch.md +++ b/internal/agent/tools/fetch.md @@ -1,45 +1 @@ -Fetch raw content from a URL as text, markdown, or html (max 100KB); no AI processing. For analysis or extraction use agentic_fetch. - - -Use this tool when you need: -- Raw, unprocessed content from a URL -- Direct access to API responses or JSON data -- HTML/text/markdown content without interpretation -- Simple, fast content retrieval without analysis -- To save tokens by avoiding AI processing - -DO NOT use this tool when you need to: -- Extract specific information from a webpage (use agentic_fetch instead) -- Answer questions about web content (use agentic_fetch instead) -- Analyze or summarize web pages (use agentic_fetch instead) - - - -- Provide URL to fetch content from -- Specify desired output format (text, markdown, or html) -- Optional timeout for request - - - -- Supports three output formats: text, markdown, html -- Auto-handles HTTP redirects -- Fast and lightweight - no AI processing -- Sets reasonable timeouts to prevent hanging -- Validates input parameters before requests - - - -- Max response size: 100KB -- Only supports HTTP and HTTPS protocols -- Cannot handle authentication or cookies -- Some websites may block automated requests -- Returns raw content only - no analysis or extraction - - - -- Use text format for plain text content or simple API responses -- Use markdown format for content that should be rendered with formatting -- Use html format when you need raw HTML structure -- Set appropriate timeouts for potentially slow websites -- If the user asks to analyze or extract from a page, use agentic_fetch instead - +Fetch raw content from a URL as text, markdown, or html (max 100KB); no AI processing. For analysis or extraction use agentic_fetch. \ No newline at end of file diff --git a/internal/agent/tools/glob.go b/internal/agent/tools/glob.go index dd10c570e541a8bbe6405ca6a6bb33c45583f09c..fff81236027112f9722eb84e988926ada6f13f6c 100644 --- a/internal/agent/tools/glob.go +++ b/internal/agent/tools/glob.go @@ -20,7 +20,7 @@ import ( const GlobToolName = "glob" //go:embed glob.md -var globDescription []byte +var globDescription string type GlobParams struct { Pattern string `json:"pattern" description:"The glob pattern to match files against"` @@ -35,7 +35,7 @@ type GlobResponseMetadata struct { func NewGlobTool(workingDir string) fantasy.AgentTool { return fantasy.NewAgentTool( GlobToolName, - FirstLineDescription(globDescription), + globDescription, func(ctx context.Context, params GlobParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.Pattern == "" { return fantasy.NewTextErrorResponse("pattern is required"), nil diff --git a/internal/agent/tools/glob.md b/internal/agent/tools/glob.md index 8d6b437d222a4f81e62473085f9058bed2fa3099..e1038856691883102e6cdf82315079d91ab25c68 100644 --- a/internal/agent/tools/glob.md +++ b/internal/agent/tools/glob.md @@ -1,40 +1 @@ -Find files by name/pattern (glob syntax), sorted by modification time; max 100 results; skips hidden files. Use grep to search file contents. - - -- Provide glob pattern to match against file paths -- Optional starting directory (defaults to current working directory) -- Results sorted with most recently modified files first - - - -- '\*' matches any sequence of non-separator characters -- '\*\*' matches any sequence including separators -- '?' matches any single non-separator character -- '[...]' matches any character in brackets -- '[!...]' matches any character not in brackets - - - -- '*.js' - JavaScript files in current directory -- '**/*.js' - JavaScript files in any subdirectory -- 'src/**/*.{ts,tsx}' - TypeScript files in src directory -- '*.{html,css,js}' - HTML, CSS, and JS files - - - -- Results limited to 100 files (newest first) -- Does not search file contents (use Grep for that) -- Hidden files (starting with '.') skipped - - - -- Path separators handled automatically (/ and \ work) -- Uses ripgrep (rg) if available, otherwise Go implementation -- Patterns should use forward slashes (/) for compatibility - - - -- Combine with Grep: find files with Glob, search contents with Grep -- For iterative exploration requiring multiple searches, consider Agent tool -- Check if results truncated and refine pattern if needed - +Find files by name/pattern (glob syntax), sorted by modification time; max 100 results; skips hidden files. Use grep to search file contents. \ No newline at end of file diff --git a/internal/agent/tools/grep.go b/internal/agent/tools/grep.go index 5092db38bd67a870ab2d76dc5e290969d39fa48d..9b75d400c7d0c1dadbb529ddf49e3301e4892d31 100644 --- a/internal/agent/tools/grep.go +++ b/internal/agent/tools/grep.go @@ -90,7 +90,7 @@ const ( ) //go:embed grep.md -var grepDescription []byte +var grepDescription string // escapeRegexPattern escapes special regex characters so they're treated as literal characters func escapeRegexPattern(pattern string) string { @@ -107,7 +107,7 @@ func escapeRegexPattern(pattern string) string { func NewGrepTool(workingDir string, config config.ToolGrep) fantasy.AgentTool { return fantasy.NewAgentTool( GrepToolName, - FirstLineDescription(grepDescription), + grepDescription, func(ctx context.Context, params GrepParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.Pattern == "" { return fantasy.NewTextErrorResponse("pattern is required"), nil diff --git a/internal/agent/tools/grep.md b/internal/agent/tools/grep.md index 6cebf1834e8f913f8ae76928712b08bdf577fe8c..057a20c6ca529455c45c902830af5b11a2726962 100644 --- a/internal/agent/tools/grep.md +++ b/internal/agent/tools/grep.md @@ -1,49 +1 @@ -Search file contents by regex or literal text; returns matching file paths sorted by modification time (max 100); respects .gitignore. Use glob to filter by filename, not contents. - - -- Provide regex pattern to search within file contents -- Set literal_text=true for exact text with special characters (recommended for non-regex users) -- Optional starting directory (defaults to current working directory) -- Optional include pattern to filter which files to search -- Results sorted with most recently modified files first - - - -When literal_text=false (supports standard regex): - -- 'function' searches for literal text "function" -- 'log\..\*Error' finds text starting with "log." and ending with "Error" -- 'import\s+.\*\s+from' finds import statements in JavaScript/TypeScript - - - -- '\*.js' - Only search JavaScript files -- '\*.{ts,tsx}' - Only search TypeScript files -- '\*.go' - Only search Go files - - - -- Results limited to 100 files (newest first) -- Performance depends on number of files searched -- Very large binary files may be skipped -- Hidden files (starting with '.') skipped - - - -- Respects .gitignore patterns to skip ignored files/directories -- Respects .crushignore patterns for additional ignore rules -- Both ignore files auto-detected in search root directory - - - -- Uses ripgrep (rg) if available for better performance -- Falls back to Go implementation if ripgrep unavailable -- File paths normalized automatically for compatibility - - - -- For faster searches: use Glob to find relevant files first, then Grep -- For iterative exploration requiring multiple searches, consider Agent tool -- Check if results truncated and refine search pattern if needed -- Use literal_text=true for exact text with special characters (dots, parentheses, etc.) - +Search file contents by regex or literal text; returns matching file paths sorted by modification time (max 100); respects .gitignore. Use glob to filter by filename, not contents. \ No newline at end of file diff --git a/internal/agent/tools/job_kill.go b/internal/agent/tools/job_kill.go index 575496d96714d34147e97b5ca2aefaacd6c68df5..bd0e7e8fcda42b08f9eb9fd7ed1eabbc8e7029dd 100644 --- a/internal/agent/tools/job_kill.go +++ b/internal/agent/tools/job_kill.go @@ -14,7 +14,7 @@ const ( ) //go:embed job_kill.md -var jobKillDescription []byte +var jobKillDescription string type JobKillParams struct { ShellID string `json:"shell_id" description:"The ID of the background shell to terminate"` @@ -29,7 +29,7 @@ type JobKillResponseMetadata struct { func NewJobKillTool() fantasy.AgentTool { return fantasy.NewAgentTool( JobKillToolName, - string(jobKillDescription), + jobKillDescription, func(ctx context.Context, params JobKillParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.ShellID == "" { return fantasy.NewTextErrorResponse("missing shell_id"), nil diff --git a/internal/agent/tools/job_output.go b/internal/agent/tools/job_output.go index c6fa37ad130a29984f1d852d1e38cc0ecb7ede92..783487c2e23695ee8685f8f7f12e6fc0208ae86c 100644 --- a/internal/agent/tools/job_output.go +++ b/internal/agent/tools/job_output.go @@ -15,7 +15,7 @@ const ( ) //go:embed job_output.md -var jobOutputDescription []byte +var jobOutputDescription string type JobOutputParams struct { ShellID string `json:"shell_id" description:"The ID of the background shell to retrieve output from"` @@ -33,7 +33,7 @@ type JobOutputResponseMetadata struct { func NewJobOutputTool() fantasy.AgentTool { return fantasy.NewAgentTool( JobOutputToolName, - FirstLineDescription(jobOutputDescription), + jobOutputDescription, func(ctx context.Context, params JobOutputParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.ShellID == "" { return fantasy.NewTextErrorResponse("missing shell_id"), nil diff --git a/internal/agent/tools/job_output.md b/internal/agent/tools/job_output.md index ddc8132dc9049dbba6dfe5f5fecd290354bccc98..25336c08cdc44164d137beea38be5823a85f9814 100644 --- a/internal/agent/tools/job_output.md +++ b/internal/agent/tools/job_output.md @@ -1,22 +1 @@ -Get stdout/stderr from a background shell by ID; set wait=true to block until completion. - - -- Provide the shell ID returned from a background bash execution -- Returns the current stdout and stderr output -- Indicates whether the shell has completed execution -- Set wait=true to block until the shell completes or the request context is done - - - -- View output from running background processes -- Check if background process has completed -- Get cumulative output from process start -- Optionally wait for process completion (returns early on context cancel) - - - -- Use this to monitor long-running processes -- Check the 'done' status to see if process completed -- Can be called multiple times to view incremental output -- Use wait=true when you need the final output and exit status (or current output if the request cancels) - +Get stdout/stderr from a background shell by ID; set wait=true to block until completion. \ No newline at end of file diff --git a/internal/agent/tools/list_mcp_resources.go b/internal/agent/tools/list_mcp_resources.go index 8f932a331aa6484c2db1dd31dbe17f90c42c4307..983970a08231fe749816e3b5bc50eac09875844d 100644 --- a/internal/agent/tools/list_mcp_resources.go +++ b/internal/agent/tools/list_mcp_resources.go @@ -26,12 +26,12 @@ type ListMCPResourcesPermissionsParams struct { const ListMCPResourcesToolName = "list_mcp_resources" //go:embed list_mcp_resources.md -var listMCPResourcesDescription []byte +var listMCPResourcesDescription string func NewListMCPResourcesTool(cfg *config.ConfigStore, permissions permission.Service) fantasy.AgentTool { return fantasy.NewParallelAgentTool( ListMCPResourcesToolName, - FirstLineDescription(listMCPResourcesDescription), + listMCPResourcesDescription, func(ctx context.Context, params ListMCPResourcesParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { params.MCPName = strings.TrimSpace(params.MCPName) if params.MCPName == "" { diff --git a/internal/agent/tools/list_mcp_resources.md b/internal/agent/tools/list_mcp_resources.md index 774d8e6f0b74d1b78fa9af7a82bfc3982c2a7bbb..7a419abcbfcaa224ede71d206314cd7f849e2790 100644 --- a/internal/agent/tools/list_mcp_resources.md +++ b/internal/agent/tools/list_mcp_resources.md @@ -1,18 +1 @@ -List available resource URIs from an MCP server by name; use before read_mcp_resource. - - -Use this tool to discover which resources are available before reading them. - - - -- Provide MCP server name -- Returns resource titles and URIs - - - -- mcp_name: The MCP server name - - - -- Results include resource titles, URIs, and metadata when available - +List available resource URIs from an MCP server by name; use before read_mcp_resource. \ No newline at end of file diff --git a/internal/agent/tools/ls.go b/internal/agent/tools/ls.go index 023eb8b7ad70a63eb25d95207f3c3c22de5cf408..8bf5f0d414192df05bfa26713295a57fe45e6ae9 100644 --- a/internal/agent/tools/ls.go +++ b/internal/agent/tools/ls.go @@ -53,12 +53,12 @@ const ( ) //go:embed ls.md -var lsDescription []byte +var lsDescription string func NewLsTool(permissions permission.Service, workingDir string, lsConfig config.ToolLs) fantasy.AgentTool { return fantasy.NewAgentTool( LSToolName, - FirstLineDescription(lsDescription), + lsDescription, func(ctx context.Context, params LSParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { searchPath, err := fsext.Expand(cmp.Or(params.Path, workingDir)) if err != nil { diff --git a/internal/agent/tools/ls.md b/internal/agent/tools/ls.md index cb7c6756e0e19c57e6fe0ffc8a0297d4810cda4a..4ff5001acf0ec4619fce6467d05da70c47cc58ac 100644 --- a/internal/agent/tools/ls.md +++ b/internal/agent/tools/ls.md @@ -1,34 +1 @@ -List files and directories as a tree; skips hidden files and common system dirs; max 1000 files. Use glob to find files by pattern, grep to search contents. - - -- Provide path to list (defaults to current working directory) -- Optional glob patterns to ignore -- Results displayed in tree structure - - - -- Hierarchical view of files and directories -- Auto-skips hidden files/directories (starting with '.') -- Skips common system directories like __pycache__ -- Can filter files matching specific patterns - - - -- Results limited to 1000 files -- Large directories truncated -- No file sizes or permissions shown -- Cannot recursively list all directories in large projects - - - -- Hidden file detection uses Unix convention (files starting with '.') -- Windows hidden files (with hidden attribute) not auto-skipped -- Common Windows directories (System32, Program Files) not in default ignore -- Path separators handled automatically (/ and \ work) - - - -- Use Glob for finding files by name patterns instead of browsing -- Use Grep for searching file contents -- Combine with other tools for effective exploration - +List files and directories as a tree; skips hidden files and common system dirs; max 1000 files. Use glob to find files by pattern, grep to search contents. \ No newline at end of file diff --git a/internal/agent/tools/lsp_restart.go b/internal/agent/tools/lsp_restart.go index ebbca2d5f0c5619155935737f8a19c0d1ce6d3af..544bc325aed3864fa2f4072e0f29414e99296a0c 100644 --- a/internal/agent/tools/lsp_restart.go +++ b/internal/agent/tools/lsp_restart.go @@ -16,7 +16,7 @@ import ( const LSPRestartToolName = "lsp_restart" //go:embed lsp_restart.md -var lspRestartDescription []byte +var lspRestartDescription string type LSPRestartParams struct { // Name is the optional name of a specific LSP client to restart. @@ -27,7 +27,7 @@ type LSPRestartParams struct { func NewLSPRestartTool(lspManager *lsp.Manager) fantasy.AgentTool { return fantasy.NewAgentTool( LSPRestartToolName, - FirstLineDescription(lspRestartDescription), + lspRestartDescription, func(ctx context.Context, params LSPRestartParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if lspManager.Clients().Len() == 0 { return fantasy.NewTextErrorResponse("no LSP clients available to restart"), nil diff --git a/internal/agent/tools/lsp_restart.md b/internal/agent/tools/lsp_restart.md index 72728db6f915fba8a98422e0fc19d96fe70f1a0b..740a18d7d4f93d4a7ae918be397715fe66195f70 100644 --- a/internal/agent/tools/lsp_restart.md +++ b/internal/agent/tools/lsp_restart.md @@ -1,25 +1 @@ -Restart one or all LSP clients by name; use when diagnostics are stale or the LSP is unresponsive. - - -- Restart all running LSP clients or a specific LSP client by name -- Useful when LSP servers become unresponsive or need to be reloaded -- Parameters: - - name (optional): Specific LSP client name to restart. If not provided, all clients will be restarted. - - - -- Gracefully shuts down all LSP clients -- Restarts them with their original configuration -- Reports success/failure for each client - - - -- Only restarts clients that were successfully started -- Does not modify LSP configurations -- Requires LSP clients to be already running - - - -- Use when LSP diagnostics are stale or unresponsive -- Call this tool if you notice LSP features not working properly - +Restart one or all LSP clients by name; use when diagnostics are stale or the LSP is unresponsive. \ No newline at end of file diff --git a/internal/agent/tools/multiedit.go b/internal/agent/tools/multiedit.go index 8ce30544882fec3ea831fa732300f2aefdee913d..34d060f42bc1cc9be0dac78c740e58a1da804b61 100644 --- a/internal/agent/tools/multiedit.go +++ b/internal/agent/tools/multiedit.go @@ -55,7 +55,7 @@ type MultiEditResponseMetadata struct { const MultiEditToolName = "multiedit" //go:embed multiedit.md -var multieditDescription []byte +var multieditDescription string func NewMultiEditTool( lspManager *lsp.Manager, @@ -66,7 +66,7 @@ func NewMultiEditTool( ) fantasy.AgentTool { return fantasy.NewAgentTool( MultiEditToolName, - FirstLineDescription(multieditDescription), + multieditDescription, func(ctx context.Context, params MultiEditParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.FilePath == "" { return fantasy.NewTextErrorResponse("file_path is required"), nil diff --git a/internal/agent/tools/multiedit.md b/internal/agent/tools/multiedit.md index 26c0a87dca3dd6112131a5cf42f8708e6e1087b0..4428aaddcecbf5b1c4ab75682997ea1f7e25b698 100644 --- a/internal/agent/tools/multiedit.md +++ b/internal/agent/tools/multiedit.md @@ -1,125 +1 @@ -Apply multiple find-and-replace edits to a single file in one operation; edits run sequentially. Prefer over edit for multiple changes to the same file. Same exact-match rules as edit apply. - - -1. Use View tool to understand file contents and context -2. Verify directory path is correct -3. CRITICAL: Note exact whitespace, indentation, and formatting from View output - - - -1. file_path: Absolute path to file (required) -2. edits: Array of edit operations, each containing: - - old_string: Text to replace (must match exactly including whitespace/indentation) - - new_string: Replacement text - - replace_all: Replace all occurrences (optional, defaults to false) - - - -- Edits applied sequentially in provided order. -- Each edit operates on result of previous edit. -- PARTIAL SUCCESS: If some edits fail, successful edits are still applied. Failed edits are returned in the response. -- File is modified if at least one edit succeeds. -- Ideal for several changes to different parts of same file. - - - -All instructions from the Edit tool documentation apply verbatim to every edit item: -- Critical requirements for exact matching and uniqueness -- Warnings and common failures (tabs vs spaces, blank lines, brace placement, etc.) -- Verification steps before using, recovery steps, best practices, and whitespace checklist -Use the same level of precision as Edit. Multiedit often fails due to formatting mismatches—double-check whitespace for every edit. - - - -1. Apply Edit tool rules to EACH edit (see edit.md). -2. Edits are applied in order; successful edits are kept even if later edits fail. -3. Plan sequence carefully: earlier edits change the file content that later edits must match. -4. Ensure each old_string is unique at its application time (after prior edits). -5. Check the response for failed edits and retry them if needed. - - - -1. View the file and copy exact text (including whitespace) for each target. -2. Check how many instances each old_string has BEFORE the sequence starts. -3. Dry-run mentally: after applying edit #N, will edit #N+1 still match? Adjust old_string/new_string accordingly. -4. Prefer fewer, larger context blocks over many tiny fragments that are easy to misalign. -5. If edits are independent, consider separate multiedit batches per logical region. - - - -- Operation continues even if some edits fail; check response for failed edits. -- Earlier edits can invalidate later matches (added/removed spaces, lines, or reordered text). -- Mixed tabs/spaces, trailing spaces, or missing blank lines commonly cause failures. -- replace_all may affect unintended regions—use carefully or provide more context. - - - -If some edits fail: -1. Check the response metadata for the list of failed edits with their error messages. -2. View the file again to see the current state after successful edits. -3. Adjust the failed edits based on the new file content. -4. Retry the failed edits with corrected old_string values. -5. Consider breaking complex batches into smaller, independent operations. - - - -- Ensure all edits result in correct, idiomatic code; don't leave code broken. -- Use absolute file paths (starting with /). -- Use replace_all only when you're certain; otherwise provide unique context. -- Match existing style exactly (spaces, tabs, blank lines). -- Review failed edits in the response and retry with corrections. - - - -For EACH edit, verify: -- [ ] Viewed the file first -- [ ] Counted indentation spaces/tabs -- [ ] Included blank lines if present -- [ ] Matched brace/bracket positioning -- [ ] Included 3–5 lines of surrounding context -- [ ] Verified text appears exactly once (or using replace_all deliberately) -- [ ] Copied text character-for-character, not approximated - - - -✅ Correct: Sequential edits where the second match accounts for the first change - -``` -edits: [ - { - old_string: "func A() {\n doOld()\n}", - new_string: "func A() {\n doNew()\n}", - }, - { - // Uses context that still exists AFTER the first replacement - old_string: "func B() {\n callA()\n}", - new_string: "func B() {\n callA()\n logChange()\n}", - }, -] -``` - -❌ Incorrect: Second old_string no longer matches due to whitespace change introduced by the first edit - -``` -edits: [ - { - old_string: "func A() {\n doOld()\n}", - new_string: "func A() {\n\n doNew()\n}", // Added extra blank line - }, - { - old_string: "func A() {\n doNew()\n}", // Missing the new blank line, will FAIL - new_string: "func A() {\n doNew()\n logChange()\n}", - }, -] -``` - -✅ Correct: Handling partial success - -``` -// If edit 2 fails, edit 1 is still applied -// Response will indicate: -// - edits_applied: 1 -// - edits_failed: [{index: 2, error: "...", edit: {...}}] -// You can then retry edit 2 with corrected context -``` - +Apply multiple find-and-replace edits to a single file in one operation; edits run sequentially. Prefer over edit for multiple changes to the same file. Same exact-match rules as edit apply. \ No newline at end of file diff --git a/internal/agent/tools/read_mcp_resource.go b/internal/agent/tools/read_mcp_resource.go index 39c7012063ea08052c77acacf24a4a2f45904e73..6b613cfbf97e07f32435d1c776dd54dbb646c725 100644 --- a/internal/agent/tools/read_mcp_resource.go +++ b/internal/agent/tools/read_mcp_resource.go @@ -28,12 +28,12 @@ type ReadMCPResourcePermissionsParams struct { const ReadMCPResourceToolName = "read_mcp_resource" //go:embed read_mcp_resource.md -var readMCPResourceDescription []byte +var readMCPResourceDescription string func NewReadMCPResourceTool(cfg *config.ConfigStore, permissions permission.Service) fantasy.AgentTool { return fantasy.NewParallelAgentTool( ReadMCPResourceToolName, - FirstLineDescription(readMCPResourceDescription), + readMCPResourceDescription, func(ctx context.Context, params ReadMCPResourceParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { params.MCPName = strings.TrimSpace(params.MCPName) params.URI = strings.TrimSpace(params.URI) diff --git a/internal/agent/tools/read_mcp_resource.md b/internal/agent/tools/read_mcp_resource.md index a350395d0955a5e5e15288cfe4803f61c7bc68c6..d6c768ad772d4ed18d1f7651ceed529658f65c97 100644 --- a/internal/agent/tools/read_mcp_resource.md +++ b/internal/agent/tools/read_mcp_resource.md @@ -1,20 +1 @@ -Read a resource by URI from an MCP server; returns text content. - - -Use this tool to fetch a specific resource URI exposed by an MCP server. - - - -- Provide MCP server name and resource URI -- Returns resource text content - - - -- mcp_name: The MCP server name -- uri: The resource URI to read - - - -- Returns text content by concatenating resource parts -- Binary resources are returned as UTF-8 text when possible - +Read a resource by URI from an MCP server; returns text content. \ No newline at end of file diff --git a/internal/agent/tools/references.go b/internal/agent/tools/references.go index d6fc03dfd933c71ef01e6d11662c88cc1c316b02..9600f30b066259d8e6822cd063ae2b0bb342687d 100644 --- a/internal/agent/tools/references.go +++ b/internal/agent/tools/references.go @@ -31,12 +31,12 @@ type referencesTool struct { const ReferencesToolName = "lsp_references" //go:embed references.md -var referencesDescription []byte +var referencesDescription string func NewReferencesTool(lspManager *lsp.Manager) fantasy.AgentTool { return fantasy.NewAgentTool( ReferencesToolName, - FirstLineDescription(referencesDescription), + referencesDescription, func(ctx context.Context, params ReferencesParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.Symbol == "" { return fantasy.NewTextErrorResponse("symbol is required"), nil diff --git a/internal/agent/tools/references.md b/internal/agent/tools/references.md index ee725b81f4cb4f92bcc4dcdea78186e263f899c5..1fd7ceb385d08dec7fad3eb514d800341125f565 100644 --- a/internal/agent/tools/references.md +++ b/internal/agent/tools/references.md @@ -1,26 +1 @@ -Find all references to a symbol by name via LSP; more accurate than grep for code symbols. - - -- Provide symbol name (e.g., "MyFunction", "myVariable", "MyType"). -- Optional path to narrow search to a directory or file (defaults to current directory). -- Tool automatically locates the symbol and returns all references. - - - -- Semantic-aware reference search (more accurate than grep/glob). -- Returns references grouped by file with line and column numbers. -- Supports multiple programming languages via LSP. -- Finds only real references (not comments or unrelated strings). - - - -- May not find references in files not opened or indexed by the LSP server. -- Results depend on the capabilities of the active LSP providers. - - - -- Use this first when searching for where a symbol is used. -- Do not use grep/glob for symbol searches. -- Narrow scope with the path parameter for faster, more relevant results. -- Use qualified names (e.g., pkg.Func, Class.method) for higher precision. - +Find all references to a symbol by name via LSP; more accurate than grep for code symbols. \ No newline at end of file diff --git a/internal/agent/tools/sourcegraph.go b/internal/agent/tools/sourcegraph.go index 8effed6f65b033b475aec6ce41a7a8a64b801dee..041503ce65d4bedf3245468d155147a372ab9cf6 100644 --- a/internal/agent/tools/sourcegraph.go +++ b/internal/agent/tools/sourcegraph.go @@ -29,7 +29,7 @@ type SourcegraphResponseMetadata struct { const SourcegraphToolName = "sourcegraph" //go:embed sourcegraph.md -var sourcegraphDescription []byte +var sourcegraphDescription string func NewSourcegraphTool(client *http.Client) fantasy.AgentTool { if client == nil { @@ -45,7 +45,7 @@ func NewSourcegraphTool(client *http.Client) fantasy.AgentTool { } return fantasy.NewParallelAgentTool( SourcegraphToolName, - FirstLineDescription(sourcegraphDescription), + sourcegraphDescription, func(ctx context.Context, params SourcegraphParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.Query == "" { return fantasy.NewTextErrorResponse("Query parameter is required"), nil diff --git a/internal/agent/tools/sourcegraph.md b/internal/agent/tools/sourcegraph.md index 05833c5c7535859f490bd8a76cba95331e830a4f..bbfd67d7fdfd12bd7eae9de252fc394f3062cbe5 100644 --- a/internal/agent/tools/sourcegraph.md +++ b/internal/agent/tools/sourcegraph.md @@ -1,55 +1 @@ -Search code across public GitHub repositories via Sourcegraph; supports regex, language/repo/file filters, and symbol search (max 20 results). Only searches public repos. - - -- Provide search query using Sourcegraph syntax -- Optional result count (default: 10, max: 20) -- Optional timeout for request - - - -- "fmt.Println" - exact matches -- "file:.go fmt.Println" - limit to Go files -- "repo:^github\.com/golang/go$ fmt.Println" - specific repos -- "lang:go fmt.Println" - limit to Go code -- "fmt.Println AND log.Fatal" - combined terms -- "fmt\.(Print|Printf|Println)" - regex patterns -- "\"exact phrase\"" - exact phrase matching -- "-file:test" or "-repo:forks" - exclude matches - - - -Repository: repo:name, repo:^exact$, repo:org/repo@branch, -repo:exclude, fork:yes, archived:yes, visibility:public -File: file:\.js$, file:internal/, -file:test, file:has.content(text) -Content: content:"exact", -content:"unwanted", case:yes -Type: type:symbol, type:file, type:path, type:diff, type:commit -Time: after:"1 month ago", before:"2023-01-01", author:name, message:"fix" -Result: select:repo, select:file, select:content, count:100, timeout:30s - - - -- "file:.go context.WithTimeout" - Go code using context.WithTimeout -- "lang:typescript useState type:symbol" - TypeScript React useState hooks -- "repo:^github\.com/kubernetes/kubernetes$ pod list type:file" - Kubernetes pod files -- "file:Dockerfile (alpine OR ubuntu) -content:alpine:latest" - Dockerfiles with base images - - - -- "term1 AND term2" - both terms -- "term1 OR term2" - either term -- "term1 NOT term2" - term1 but not term2 -- "term1 and (term2 or term3)" - grouping with parentheses - - - -- Only searches public repositories -- Rate limits may apply -- Complex queries take longer -- Max 20 results per query - - - -- Use specific file extensions to narrow results -- Add repo: filters for targeted searches -- Use type:symbol for function/method definitions -- Use type:file to find relevant files - +Search code across public GitHub repositories via Sourcegraph; supports regex, language/repo/file filters, and symbol search (max 20 results). Only searches public repos. \ No newline at end of file diff --git a/internal/agent/tools/todos.go b/internal/agent/tools/todos.go index 2f69f7bf84581d9f0e0776d73660bef7ba34ba43..e99d2a7514b0d211b79aab6fcb4764965848fc24 100644 --- a/internal/agent/tools/todos.go +++ b/internal/agent/tools/todos.go @@ -10,7 +10,7 @@ import ( ) //go:embed todos.md -var todosDescription []byte +var todosDescription string const TodosToolName = "todos" @@ -36,7 +36,7 @@ type TodosResponseMetadata struct { func NewTodosTool(sessions session.Service) fantasy.AgentTool { return fantasy.NewAgentTool( TodosToolName, - FirstLineDescription(todosDescription), + todosDescription, func(ctx context.Context, params TodosParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { sessionID := GetSessionFromContext(ctx) if sessionID == "" { diff --git a/internal/agent/tools/todos.md b/internal/agent/tools/todos.md index b508d0d278d669f195fd3c7f0ea34f365f4cb77b..2bcd258647de1c4e3ee71f0848c09bc0129b7fc4 100644 --- a/internal/agent/tools/todos.md +++ b/internal/agent/tools/todos.md @@ -1,90 +1 @@ -Manage a structured task list for multi-step work; each task has pending/in_progress/completed state. Keep exactly one task in_progress at a time. Skip for simple or single-step tasks. - - -Use this tool proactively in these scenarios: - -- Complex multi-step tasks requiring 3+ distinct steps or actions -- Non-trivial tasks requiring careful planning or multiple operations -- User explicitly requests todo list management -- User provides multiple tasks (numbered or comma-separated list) -- After receiving new instructions to capture requirements -- When starting work on a task (mark as in_progress BEFORE beginning) -- After completing a task (mark completed and add new follow-up tasks) - - - -Skip this tool when: - -- Single, straightforward task -- Trivial task with no organizational benefit -- Task completable in less than 3 trivial steps -- Purely conversational or informational request - - - -- **pending**: Task not yet started -- **in_progress**: Currently working on (limit to ONE task at a time) -- **completed**: Task finished successfully - -**IMPORTANT**: Each task requires two forms: -- **content**: Imperative form describing what needs to be done (e.g., "Run tests", "Build the project") -- **active_form**: Present continuous form shown during execution (e.g., "Running tests", "Building the project") - - - -- Update task status in real-time as you work -- Mark tasks complete IMMEDIATELY after finishing (don't batch completions) -- Exactly ONE task must be in_progress at any time (not less, not more) -- Complete current tasks before starting new ones -- Remove tasks that are no longer relevant from the list entirely - - - -ONLY mark a task as completed when you have FULLY accomplished it. - -Never mark completed if: -- Tests are failing -- Implementation is partial -- You encountered unresolved errors -- You couldn't find necessary files or dependencies - -If blocked: -- Keep task as in_progress -- Create new task describing what needs to be resolved - - - -- Create specific, actionable items -- Break complex tasks into smaller, manageable steps -- Use clear, descriptive task names -- Always provide both content and active_form - - - -✅ Good task: -```json -{ - "content": "Implement user authentication with JWT tokens", - "status": "in_progress", - "active_form": "Implementing user authentication with JWT tokens" -} -``` - -❌ Bad task (missing active_form): -```json -{ - "content": "Fix bug", - "status": "pending" -} -``` - - - -**NEVER** print or list todos in your response text. The user sees the todo list in real-time in the UI. - - - -- When in doubt, use this tool - being proactive demonstrates attentiveness -- One task in_progress at a time keeps work focused -- Update immediately after state changes for accurate tracking - +Manage a structured task list for multi-step work; each task has pending/in_progress/completed state. Keep exactly one task in_progress at a time. Skip for simple or single-step tasks. \ No newline at end of file diff --git a/internal/agent/tools/tools.go b/internal/agent/tools/tools.go index a504acfc0955a84de20a6f3200235e7106345fbe..fb67ed544693c3b0ac226adad873bbbd0f45555d 100644 --- a/internal/agent/tools/tools.go +++ b/internal/agent/tools/tools.go @@ -2,10 +2,6 @@ package tools import ( "context" - "os" - "strconv" - "strings" - "testing" "charm.land/fantasy" ) @@ -68,21 +64,3 @@ func NewPermissionDeniedResponse() fantasy.ToolResponse { resp.StopTurn = true return resp } - -// FirstLineDescription returns just the first non-empty line from the embedded -// markdown description. The full description can be used by setting -// CRUSH_SHORT_TOOL_DESCRIPTIONS=0. -func FirstLineDescription(content []byte) string { - if !testing.Testing() { - if v, err := strconv.ParseBool(os.Getenv("CRUSH_SHORT_TOOL_DESCRIPTIONS")); err == nil && !v { - return strings.TrimSpace(string(content)) - } - } - for line := range strings.SplitSeq(string(content), "\n") { - line = strings.TrimSpace(line) - if line != "" { - return line - } - } - return "" -} diff --git a/internal/agent/tools/view.go b/internal/agent/tools/view.go index cad16c9b11215bff76c17f0238f6907e102955b3..fef294cecf17efb28d69fe3a9c3b1e8d65de11f2 100644 --- a/internal/agent/tools/view.go +++ b/internal/agent/tools/view.go @@ -24,7 +24,7 @@ import ( ) //go:embed view.md -var viewDescription []byte +var viewDescription string type ViewParams struct { FilePath string `json:"file_path" description:"The path to the file to read"` @@ -79,7 +79,7 @@ func NewViewTool( ) fantasy.AgentTool { return fantasy.NewAgentTool( ViewToolName, - FirstLineDescription(viewDescription), + viewDescription, func(ctx context.Context, params ViewParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.FilePath == "" { return fantasy.NewTextErrorResponse("file_path is required"), nil diff --git a/internal/agent/tools/view.md b/internal/agent/tools/view.md index 6b2f2834769b35a5ca54a686091d01d9aa8cfe3c..6d616d661146fc3d17e2267a6ad3a9f73f807add 100644 --- a/internal/agent/tools/view.md +++ b/internal/agent/tools/view.md @@ -1,38 +1 @@ -Read a file by path with line numbers; supports offset and line limit (default 2000, max 200KB returned file content section); renders images (PNG, JPEG, GIF, WebP); use ls for directories. - - -- Provide file path to read -- Optional offset: start reading from specific line (0-based) -- Optional limit: control lines read (default 2000) -- Don't use for directories (use LS tool instead) -- Supports image files (PNG, JPEG, GIF, WebP) - - - -- Displays contents with line numbers -- Can read from any file position using offset -- Handles large files by limiting lines read -- Auto-truncates very long lines for display -- Suggests similar filenames when file not found -- Renders image files directly in terminal - - - -- Max returned file content section: 200KB after offset/limit are applied -- Default limit: 2000 lines -- Lines >2000 chars truncated -- Binary files (except images) cannot be displayed - - - -- Handles Windows (CRLF) and Unix (LF) line endings -- Works with forward slashes (/) and backslashes (\) -- Auto-detects text encoding for common formats - - - -- Use with Glob to find files first -- For code exploration: Grep to find relevant files, then View to examine -- For large files: use offset parameter for specific sections -- View tool automatically detects and renders image files - +Read a file by path with line numbers; supports offset and line limit (default 2000, max 200KB); renders images (PNG, JPEG, GIF, BMP, SVG, WebP); use ls for directories. diff --git a/internal/agent/tools/web_fetch.go b/internal/agent/tools/web_fetch.go index 1b09012db9c7dd95ef322dbb41b2fb2fd4d7a3b7..31a6f0888f421b5b04c49090e6010002dee1de89 100644 --- a/internal/agent/tools/web_fetch.go +++ b/internal/agent/tools/web_fetch.go @@ -13,7 +13,7 @@ import ( ) //go:embed web_fetch.md -var webFetchToolDescription []byte +var webFetchToolDescription string // NewWebFetchTool creates a simple web fetch tool for sub-agents (no permissions needed). func NewWebFetchTool(workingDir string, client *http.Client) fantasy.AgentTool { @@ -31,7 +31,7 @@ func NewWebFetchTool(workingDir string, client *http.Client) fantasy.AgentTool { return fantasy.NewParallelAgentTool( WebFetchToolName, - FirstLineDescription(webFetchToolDescription), + webFetchToolDescription, func(ctx context.Context, params WebFetchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.URL == "" { return fantasy.NewTextErrorResponse("url is required"), nil diff --git a/internal/agent/tools/web_fetch.md b/internal/agent/tools/web_fetch.md index 470722286d80d26c0bccc3f04e5b0133fb391ec7..51249e574297ddd27139e24d4749eaf20c125851 100644 --- a/internal/agent/tools/web_fetch.md +++ b/internal/agent/tools/web_fetch.md @@ -1,28 +1 @@ -Fetch a web URL and return content as markdown; for use inside sub-agents. Large pages (>50KB) are saved to a temp file for grep/view. - - -- Provide a URL to fetch -- The tool fetches the content and returns it as markdown -- Use this when you need to follow links from the current page -- After fetching, analyze the content to answer the user's question - - - -- Automatically converts HTML to markdown for easier analysis -- For large pages (>50KB), saves content to a temporary file and provides the path -- You can then use grep/view tools to search through the file -- Handles UTF-8 content validation - - - -- Max response size: 5MB -- Only supports HTTP and HTTPS protocols -- Cannot handle authentication or cookies -- Some websites may block automated requests - - - -- For large pages saved to files, use grep to find relevant sections first -- Don't fetch unnecessary pages - only when needed to answer the question -- Focus on extracting specific information from the fetched content - +Fetch a web URL and return content as markdown; for use inside sub-agents. Large pages (>50KB) are saved to a temp file for grep/view. \ No newline at end of file diff --git a/internal/agent/tools/web_search.go b/internal/agent/tools/web_search.go index 45541154783a98ad5a61ee5240b8c0ddf8f18f79..0d66c60456dc15a65dc8b1ca19a124b97723be7e 100644 --- a/internal/agent/tools/web_search.go +++ b/internal/agent/tools/web_search.go @@ -11,7 +11,7 @@ import ( ) //go:embed web_search.md -var webSearchToolDescription []byte +var webSearchToolDescription string // NewWebSearchTool creates a web search tool for sub-agents (no permissions needed). func NewWebSearchTool(client *http.Client) fantasy.AgentTool { @@ -29,7 +29,7 @@ func NewWebSearchTool(client *http.Client) fantasy.AgentTool { return fantasy.NewParallelAgentTool( WebSearchToolName, - FirstLineDescription(webSearchToolDescription), + webSearchToolDescription, func(ctx context.Context, params WebSearchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.Query == "" { return fantasy.NewTextErrorResponse("query is required"), nil diff --git a/internal/agent/tools/web_search.md b/internal/agent/tools/web_search.md index 3dd8f736ec41d2e6dd648a4affc275c006bb9173..922bfec3fdf371e763507b553f9a730ab91670a9 100644 --- a/internal/agent/tools/web_search.md +++ b/internal/agent/tools/web_search.md @@ -1,18 +1 @@ -Search the web via DuckDuckGo; returns titles, URLs, and snippets. Follow up with web_fetch to get full page content. - - -- Provide a search query to find information on the web -- Returns a list of search results with titles, URLs, and snippets -- Use this to find relevant web pages, then use web_fetch to get full content - - - -- query: The search query string (required) -- max_results: Maximum number of results to return (default: 10, max: 20) - - - -- Use specific, targeted search queries for better results -- After getting results, use web_fetch to get the full content of relevant pages -- Combine multiple searches to gather comprehensive information - +Search the web via DuckDuckGo; returns titles, URLs, and snippets. Follow up with web_fetch to get full page content. \ No newline at end of file diff --git a/internal/agent/tools/write.go b/internal/agent/tools/write.go index ce243b452059b396682d9f3c2773c69199b9f2a5..41b3d5961639330d15584378317670fdcd8a2997 100644 --- a/internal/agent/tools/write.go +++ b/internal/agent/tools/write.go @@ -22,7 +22,7 @@ import ( ) //go:embed write.md -var writeDescription []byte +var writeDescription string type WriteParams struct { FilePath string `json:"file_path" description:"The path to the file to write"` @@ -52,7 +52,7 @@ func NewWriteTool( ) fantasy.AgentTool { return fantasy.NewAgentTool( WriteToolName, - FirstLineDescription(writeDescription), + writeDescription, func(ctx context.Context, params WriteParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) { if params.FilePath == "" { return fantasy.NewTextErrorResponse("file_path is required"), nil diff --git a/internal/agent/tools/write.md b/internal/agent/tools/write.md index 922f3796172d104cad716bdd8b6db3228144e281..678d42a92b76b835e331728a9345a39f22370af8 100644 --- a/internal/agent/tools/write.md +++ b/internal/agent/tools/write.md @@ -1,30 +1 @@ -Create or overwrite a file with given content; auto-creates parent dirs. Cannot append. Read the file first to avoid conflicts. For surgical changes use edit or multiedit. - - -- Provide file path to write -- Include content to write to file (may be empty for a new empty file) -- Tool creates necessary parent directories automatically - - - -- Creates new files or overwrites existing ones -- Auto-creates parent directories if missing -- Checks if file modified since last read for safety -- Avoids unnecessary writes when content unchanged - - - -- Read file before writing to avoid conflicts -- Cannot append (rewrites entire file) - - - -- Use forward slashes (/) for compatibility - - - -- Use View tool first to examine existing files before modifying -- Use LS tool to verify location when creating new files -- Combine with Glob/Grep to find and modify multiple files -- Include descriptive comments when changing existing code - +Create or overwrite a file with given content; auto-creates parent dirs. Cannot append. Read the file first to avoid conflicts. For surgical changes use edit or multiedit. \ No newline at end of file