Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/forge_app/src/compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ mod tests {
"Find authentication implementation",
)],
Some(".rs".to_string()),
"/test/path".to_string(),
)
.id("call_4")
.is_success(false)
Expand Down
14 changes: 12 additions & 2 deletions crates/forge_app/src/tool_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,17 @@ impl<
ToolCatalog::SemSearch(input) => {
let env = self.services.get_environment();
let services = self.services.clone();
let cwd = env.cwd.clone();
// Use the provided search path
let workspace_path = PathBuf::from(self.normalize_path(input.path));
// Check if the directory is indexed before attempting search
let is_indexed = services.is_indexed(&workspace_path).await?;
if !is_indexed {
anyhow::bail!(
"Workspace '{}' is not indexed. Use 'fs_search' or 'shell' tool instead to explore this workspace.",
workspace_path.display()
);
}

let limit = env.sem_search_limit;
let top_k = env.sem_search_top_k as u32;
let params: Vec<_> = input
Expand All @@ -219,7 +229,7 @@ impl<
// Execute all queries in parallel
let futures: Vec<_> = params
.into_iter()
.map(|param| services.query_workspace(cwd.clone(), param))
.map(|param| services.query_workspace(workspace_path.clone(), param))
.collect();

let mut results = futures::future::try_join_all(futures).await?;
Expand Down
5 changes: 3 additions & 2 deletions crates/forge_app/src/transformers/trim_context_summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum Operation<'a> {
CodebaseSearch {
queries: &'a [forge_domain::SearchQuery],
file_extension: Option<&'a str>,
path: &'a str,
},
/// Fetch operation for a specific URL
Fetch(&'a str),
Expand All @@ -48,8 +49,8 @@ fn to_op(tool: &SummaryTool) -> Operation<'_> {
SummaryTool::Undo { path } => Operation::File(path),
SummaryTool::Shell { command } => Operation::Shell(command),
SummaryTool::Search { pattern } => Operation::Search(pattern),
SummaryTool::SemSearch { queries, file_extension } => {
Operation::CodebaseSearch { queries, file_extension: file_extension.as_deref() }
SummaryTool::SemSearch { queries, file_extension, path } => {
Operation::CodebaseSearch { queries, file_extension: file_extension.as_deref(), path }
}
SummaryTool::Fetch { url } => Operation::Fetch(url),
SummaryTool::Followup { question } => Operation::Followup(question),
Expand Down
12 changes: 10 additions & 2 deletions crates/forge_domain/src/compact/summary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,14 @@ impl SummaryToolCall {

/// Creates a CodebaseSearch tool call with default values (id: None,
/// is_success: true)
pub fn codebase_search(queries: Vec<SearchQuery>, file_extension: Option<String>) -> Self {
pub fn codebase_search(
queries: Vec<SearchQuery>,
file_extension: Option<String>,
path: String,
) -> Self {
Self {
id: None,
tool: SummaryTool::SemSearch { queries, file_extension },
tool: SummaryTool::SemSearch { queries, file_extension, path },
is_success: true,
}
}
Expand Down Expand Up @@ -188,6 +192,7 @@ pub enum SummaryTool {
SemSearch {
queries: Vec<SearchQuery>,
file_extension: Option<String>,
path: String,
},
Undo {
path: String,
Expand Down Expand Up @@ -311,6 +316,7 @@ fn extract_tool_info(call: &ToolCallFull) -> Option<SummaryTool> {
ToolCatalog::SemSearch(input) => Some(SummaryTool::SemSearch {
queries: input.queries,
file_extension: input.file_extension,
path: input.path,
}),
ToolCatalog::Undo(input) => Some(SummaryTool::Undo { path: input.path }),
ToolCatalog::Fetch(input) => Some(SummaryTool::Fetch { url: input.url }),
Expand Down Expand Up @@ -962,6 +968,7 @@ mod tests {
ToolCatalog::tool_call_semantic_search(
vec![SearchQuery::new("retry mechanism", "find retry logic")],
None,
"/test/path".to_string(),
)
.call_id("call_1"),
],
Expand All @@ -976,6 +983,7 @@ mod tests {
SummaryToolCall::codebase_search(
vec![SearchQuery::new("retry mechanism", "find retry logic")],
None,
"/test/path".to_string(),
)
.id("call_1")
.is_success(false)
Expand Down
9 changes: 9 additions & 0 deletions crates/forge_domain/src/tools/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ impl SearchQuery {
/// Returns the topK most relevant file:line locations with code context. Use
/// multiple varied queries (2-3) for best coverage. For exact string matching
/// (TODO comments, specific function names), use regex search instead.
///
/// IMPORTANT: This tool only works on indexed workspaces. If you receive an
/// error about the workspace not being indexed, use the other tools.
#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, ToolDescription, PartialEq)]
pub struct SemanticSearch {
/// List of search queries to execute in parallel. Using multiple queries
Expand All @@ -233,6 +236,10 @@ pub struct SemanticSearch {
/// only files with this extension will be included in the search results.
#[serde(skip_serializing_if = "Option::is_none")]
pub file_extension: Option<String>,

/// Directory path to search in. Must be an absolute path to an indexed
/// workspace.
pub path: String,
}

/// Request to remove a file at the specified path. Use this when you need to
Expand Down Expand Up @@ -755,10 +762,12 @@ impl ToolCatalog {
pub fn tool_call_semantic_search(
queries: Vec<SearchQuery>,
file_ext: Option<String>,
path: String,
) -> ToolCallFull {
ToolCallFull::from(ToolCatalog::SemSearch(SemanticSearch {
queries,
file_extension: file_ext,
path,
}))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ expression: prompt
<tool>{"name":"read_image","description":"Reads image files from the file system and returns them in base64-encoded\n format for vision-capable models. Supports common image formats: JPEG, PNG,\n WebP, and GIF. The path must be absolute and point to an existing file. Use\n this tool when you need to process, analyze, or display images with vision\n models. Do NOT use this for text files - use the `read` tool instead. Do NOT\n use for other binary files like PDFs, videos, or archives. The tool will\n fail if the file doesn\\'t exist or if the format is unsupported. Returns the\n image content encoded in base64 format ready for vision model consumption.","arguments":{"path":{"description":"The absolute path to the image file (e.g., /home/user/image.png). Relative paths are not supported. The file must exist and be readable.","type":"string","is_required":true}}}</tool>
<tool>{"name":"write","description":"Use it to create a new file at a specified path with the provided content.\n Always provide absolute paths for file locations. The tool\n automatically handles the creation of any missing intermediary directories\n in the specified path.\n IMPORTANT: DO NOT attempt to use this tool to move or rename files, use the\n shell tool instead.","arguments":{"content":{"description":"The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified.","type":"string","is_required":true},"overwrite":{"description":"If set to true, existing files will be overwritten. If not set and the file exists, an error will be returned with the content of the existing file.","type":"boolean","is_required":false},"path":{"description":"The path of the file to write to (absolute path required)","type":"string","is_required":true}}}</tool>
<tool>{"name":"fs_search","description":"Recursively searches directories for files by content (regex) and/or name\n (glob pattern). Provides context-rich results with line numbers for content\n matches. Two modes: content search (when regex provided) or file finder\n (when regex omitted). Uses case-insensitive Rust regex syntax. Requires\n absolute paths. Avoids binary files and excluded directories. Best for code\n exploration, API usage discovery, configuration settings, or finding\n patterns across projects. For large pages, returns the first 200\n lines and stores the complete content in a temporary file for\n subsequent access.","arguments":{"file_pattern":{"description":"Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).","type":"string","is_required":false},"max_search_lines":{"description":"Maximum number of lines to return in the search results.","type":"integer","is_required":false},"path":{"description":"The absolute path of the directory or file to search in. If it's a directory, it will be searched recursively. If it's a file path, only that specific file will be searched.","type":"string","is_required":true},"regex":{"description":"The regular expression pattern to search for in file contents. Uses Rust regex syntax. If not provided, only file name matching will be performed.","type":"string","is_required":false},"start_index":{"description":"Starting index for the search results (1-based).","type":"integer","is_required":false}}}</tool>
<tool>{"name":"sem_search","description":"AI-powered semantic code search. YOUR DEFAULT TOOL for code discovery\n tasks. Use this when you need to find code locations, understand\n implementations, or explore functionality - it works with natural language\n about behavior and concepts, not just keyword matching.\n Start with sem_search when: locating code to modify, understanding how\n features work, finding patterns/examples, or exploring unfamiliar areas.\n Understands queries like \\\"authentication flow\\\" (finds login), \\\"retry logic\\\n (finds backoff), \\\"validation\\\" (finds checking/sanitization).\n Returns the topK most relevant file:line locations with code context. Use\n multiple varied queries (2-3) for best coverage. For exact string matching\n (TODO comments, specific function names), use regex search instead.","arguments":{"file_extension":{"description":"Optional file extension filter (e.g., \".rs\", \".ts\", \".py\"). If provided, only files with this extension will be included in the search results.","type":"string","is_required":false},"queries":{"description":"List of search queries to execute in parallel. Using multiple queries (2-3) with varied phrasings significantly improves results - each query captures different aspects of what you're looking for. Each query pairs a search term with a use_case for reranking. Example: for authentication, try \"user login verification\", \"token generation\", \"OAuth flow\".","type":"array","is_required":true}}}</tool>
<tool>{"name":"sem_search","description":"AI-powered semantic code search. YOUR DEFAULT TOOL for code discovery\n tasks. Use this when you need to find code locations, understand\n implementations, or explore functionality - it works with natural language\n about behavior and concepts, not just keyword matching.\n Start with sem_search when: locating code to modify, understanding how\n features work, finding patterns/examples, or exploring unfamiliar areas.\n Understands queries like \\\"authentication flow\\\" (finds login), \\\"retry logic\\\n (finds backoff), \\\"validation\\\" (finds checking/sanitization).\n Returns file:line locations with code context, ranked by relevance. Use\n multiple varied queries (2-3) for best coverage. For exact string matching\n (TODO comments, specific function names), use regex search instead.\n IMPORTANT: This tool only works on indexed workspaces. If you receive an\n error about the workspace not being indexed, use the \\'fs_search\\' tool\n instead for regex-based file searching.","arguments":{"file_extension":{"description":"Optional file extension filter (e.g., \".rs\", \".ts\", \".py\"). If provided, only files with this extension will be included in the search results.","type":"string","is_required":false},"path":{"description":"Directory path to search in. Must be an absolute path to an indexed workspace. Use the current working directory path from the system context.","type":"string","is_required":true},"queries":{"description":"List of search queries to execute in parallel. Using multiple queries (2-3) with varied phrasings significantly improves results - each query captures different aspects of what you're looking for. Each query pairs a search term with a use_case for reranking. Example: for authentication, try \"user login verification\", \"token generation\", \"OAuth flow\".","type":"array","is_required":true}}}</tool>
<tool>{"name":"remove","description":"Request to remove a file at the specified path. Use this when you need to\n delete an existing file. The path must be absolute. This operation cannot\n be undone, so use it carefully.","arguments":{"path":{"description":"The path of the file to remove (absolute path required)","type":"string","is_required":true}}}</tool>
<tool>{"name":"patch","description":"Modifies files with targeted line operations on matched patterns. Supports\n prepend, append, replace, replace_all, swap operations. Ideal for precise\n changes to configs, code, or docs while preserving context. Not suitable for\n complex refactoring or modifying all pattern occurrences - use `write`\n instead for complete rewrites and `undo` for undoing the last operation.\n Fails if search pattern isn\\'t found.\\\\n\\\\nUsage Guidelines:\\\\n-When editing\n text from Read tool output, preserve the EXACT text character-by-character\n (indentation, spaces, punctuation, special characters) as it appears AFTER\n the line number prefix. Format: \\'line_number:\\'. Never include the prefix.\n CRITICAL: Even tiny differences like \\'allows to\\' vs \\'allows the\\' will fail","arguments":{"content":{"description":"The text to replace it with (must be different from search)","type":"string","is_required":true},"operation":{"description":"The operation to perform on the matched text. Possible options are: - 'prepend': Add content before the matched text - 'append': Add content after the matched text - 'replace': Use only for specific, targeted replacements where you need to modify just the first match. - 'replace_all': Should be used for renaming variables, functions, types, or any widespread replacements across the file. This is the recommended choice for consistent refactoring operations as it ensures all occurrences are updated. - 'swap': Replace the matched text with another text (search for the second text and swap them)","type":"string","is_required":true},"path":{"description":"The path to the file to modify","type":"string","is_required":true},"search":{"description":"The text to replace. When skipped the patch operation applies to the entire content. `Append` adds the new content to the end, `Prepend` adds it to the beginning, and `Replace` fully overwrites the original content. `Swap` requires a search target, so without one, it makes no changes.","type":"string","is_required":false}}}</tool>
<tool>{"name":"undo","description":"Reverts the most recent file operation (create/modify/delete) on a specific\n file. Use this tool when you need to recover from incorrect file changes or\n if a revert is requested by the user.","arguments":{"path":{"description":"The absolute path of the file to revert to its previous state.","type":"string","is_required":true}}}</tool>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,10 @@ expression: tools
}
{
"title": "SemanticSearch",
"description": "AI-powered semantic code search. YOUR DEFAULT TOOL for code discovery tasks. Use this when you need to find code locations, understand implementations, or explore functionality - it works with natural language about behavior and concepts, not just keyword matching.\n\nStart with sem_search when: locating code to modify, understanding how features work, finding patterns/examples, or exploring unfamiliar areas. Understands queries like \"authentication flow\" (finds login), \"retry logic\" (finds backoff), \"validation\" (finds checking/sanitization).\n\nReturns the topK most relevant file:line locations with code context. Use multiple varied queries (2-3) for best coverage. For exact string matching (TODO comments, specific function names), use regex search instead.",
"description": "AI-powered semantic code search. YOUR DEFAULT TOOL for code discovery tasks. Use this when you need to find code locations, understand implementations, or explore functionality - it works with natural language about behavior and concepts, not just keyword matching.\n\nStart with sem_search when: locating code to modify, understanding how features work, finding patterns/examples, or exploring unfamiliar areas. Understands queries like \"authentication flow\" (finds login), \"retry logic\" (finds backoff), \"validation\" (finds checking/sanitization).\n\nReturns file:line locations with code context, ranked by relevance. Use multiple varied queries (2-3) for best coverage. For exact string matching (TODO comments, specific function names), use regex search instead.\n\nIMPORTANT: This tool only works on indexed workspaces. If you receive an error about the workspace not being indexed, use the 'fs_search' tool instead for regex-based file searching.",
"type": "object",
"required": [
"path",
"queries"
],
"properties": {
Expand All @@ -119,6 +120,10 @@ expression: tools
"type": "string",
"nullable": true
},
"path": {
"description": "Directory path to search in. Must be an absolute path to an indexed workspace. Use the current working directory path from the system context.",
"type": "string"
},
"queries": {
"description": "List of search queries to execute in parallel. Using multiple queries (2-3) with varied phrasings significantly improves results - each query captures different aspects of what you're looking for. Each query pairs a search term with a use_case for reranking. Example: for authentication, try \"user login verification\", \"token generation\", \"OAuth flow\".",
"type": "array",
Expand Down
Loading