Replace memory_list with memory_tree tool

The memory_tree tool provides a hierarchical view of the workspace
with configurable depth (default 1). This is more useful for
exploring nested directory structures.

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-02 21:44:07 -08:00
co-authored by Claude Opus 4.5
parent 3718cfa767
commit 3f7624eefc
2 changed files with 84 additions and 34 deletions
+1 -1
View File
@@ -347,7 +347,7 @@ Four tools for LLM use:
- **`memory_search`** - Hybrid search, MUST be called before answering questions about prior work - **`memory_search`** - Hybrid search, MUST be called before answering questions about prior work
- **`memory_write`** - Write to any path (memory, daily_log, or custom paths) - **`memory_write`** - Write to any path (memory, daily_log, or custom paths)
- **`memory_read`** - Read any file by path - **`memory_read`** - Read any file by path
- **`memory_list`** - List directory contents - **`memory_tree`** - View workspace structure as a tree (depth parameter, default 1)
### Hybrid Search (RRF) ### Hybrid Search (RRF)
+83 -33
View File
@@ -316,39 +316,94 @@ impl Tool for MemoryReadTool {
} }
} }
/// Tool for listing workspace files. /// Tool for viewing workspace structure as a tree.
/// ///
/// Use this to explore the workspace structure. /// Returns a hierarchical view of files and directories with configurable depth.
pub struct MemoryListTool { pub struct MemoryTreeTool {
workspace: Arc<Workspace>, workspace: Arc<Workspace>,
} }
impl MemoryListTool { impl MemoryTreeTool {
/// Create a new memory list tool. /// Create a new memory tree tool.
pub fn new(workspace: Arc<Workspace>) -> Self { pub fn new(workspace: Arc<Workspace>) -> Self {
Self { workspace } Self { workspace }
} }
/// Recursively build tree structure.
async fn build_tree(
&self,
path: &str,
current_depth: usize,
max_depth: usize,
) -> Result<Vec<serde_json::Value>, ToolError> {
if current_depth > max_depth {
return Ok(Vec::new());
}
let entries = self
.workspace
.list(path)
.await
.map_err(|e| ToolError::ExecutionFailed(format!("List failed: {}", e)))?;
let mut result = Vec::new();
for entry in entries {
let mut node = serde_json::json!({
"name": entry.name(),
"path": entry.path,
"is_directory": entry.is_directory,
});
if entry.is_directory && current_depth < max_depth {
// Recurse into directory
let children =
Box::pin(self.build_tree(&entry.path, current_depth + 1, max_depth)).await?;
if !children.is_empty() {
node["children"] = serde_json::Value::Array(children);
}
} else if !entry.is_directory {
// Include file metadata
if let Some(updated) = entry.updated_at {
node["updated_at"] = serde_json::Value::String(updated.to_rfc3339());
}
if let Some(preview) = &entry.content_preview {
node["preview"] = serde_json::Value::String(preview.clone());
}
}
result.push(node);
}
Ok(result)
}
} }
#[async_trait] #[async_trait]
impl Tool for MemoryListTool { impl Tool for MemoryTreeTool {
fn name(&self) -> &str { fn name(&self) -> &str {
"memory_list" "memory_tree"
} }
fn description(&self) -> &str { fn description(&self) -> &str {
"List files and directories in the workspace. Use this to explore \ "View the workspace structure as a tree. Use this to explore \
the workspace structure and discover available files." the workspace hierarchy and discover available files and directories."
} }
fn parameters_schema(&self) -> serde_json::Value { fn parameters_schema(&self) -> serde_json::Value {
serde_json::json!({ serde_json::json!({
"type": "object", "type": "object",
"properties": { "properties": {
"directory": { "path": {
"type": "string", "type": "string",
"description": "Directory to list (empty string or '/' for root)", "description": "Root path to start from (empty string for workspace root)",
"default": "" "default": ""
},
"depth": {
"type": "integer",
"description": "Maximum depth to traverse (1 = immediate children only)",
"default": 1,
"minimum": 1,
"maximum": 10
} }
} }
}) })
@@ -361,27 +416,20 @@ impl Tool for MemoryListTool {
) -> Result<ToolOutput, ToolError> { ) -> Result<ToolOutput, ToolError> {
let start = std::time::Instant::now(); let start = std::time::Instant::now();
let directory = params let path = params.get("path").and_then(|v| v.as_str()).unwrap_or("");
.get("directory")
.and_then(|v| v.as_str())
.unwrap_or("");
let entries = self let depth = params
.workspace .get("depth")
.list(directory) .and_then(|v| v.as_u64())
.await .unwrap_or(1)
.map_err(|e| ToolError::ExecutionFailed(format!("List failed: {}", e)))?; .clamp(1, 10) as usize;
let tree = self.build_tree(path, 1, depth).await?;
let output = serde_json::json!({ let output = serde_json::json!({
"directory": directory, "root": if path.is_empty() { "/" } else { path },
"entries": entries.iter().map(|e| serde_json::json!({ "depth": depth,
"path": e.path, "tree": tree,
"name": e.name(),
"is_directory": e.is_directory,
"updated_at": e.updated_at.map(|t| t.to_rfc3339()),
"preview": e.content_preview,
})).collect::<Vec<_>>(),
"count": entries.len(),
}); });
Ok(ToolOutput::success(output, start.elapsed())) Ok(ToolOutput::success(output, start.elapsed()))
@@ -457,13 +505,15 @@ mod tests {
} }
#[test] #[test]
fn test_memory_list_schema() { fn test_memory_tree_schema() {
let workspace = make_test_workspace(); let workspace = make_test_workspace();
let tool = MemoryListTool::new(workspace); let tool = MemoryTreeTool::new(workspace);
assert_eq!(tool.name(), "memory_list"); assert_eq!(tool.name(), "memory_tree");
let schema = tool.parameters_schema(); let schema = tool.parameters_schema();
assert!(schema["properties"]["directory"].is_object()); assert!(schema["properties"]["path"].is_object());
assert!(schema["properties"]["depth"].is_object());
assert_eq!(schema["properties"]["depth"]["default"], 1);
} }
} }