chore: fix all 702 clippy warnings across codebase - auto-fix 475 via cargo clippy --fix - fix remaining 227 manually: uninlined_format_args, redundant_closure, match_same_arms, underscore_binding, format_push_string, items_after_statements, needless_pass_by_value, clone_on_copy, case_sensitive_extension, single_match/let-else, write_with_newline, and other clippy lints
92 lines
3.3 KiB
Rust
92 lines
3.3 KiB
Rust
//! Tool for listing the immediate contents of a workspace directory.
|
|
//!
|
|
//! Flow: resolve the requested path against workspace roots → validate
|
|
//! it exists and is a directory → read its direct children with
|
|
//! `fs::read_dir`, tagging subdirectories with a trailing `/` → format
|
|
//! into a header + newline-joined listing.
|
|
//!
|
|
//! Why: gives the agent a quick, one-level view of the workspace
|
|
//! structure without pulling in the full recursive directory cache.
|
|
|
|
use std::fs;
|
|
use serde_json::{json, Value};
|
|
use anyhow::{Result, anyhow};
|
|
use super::super::Tool;
|
|
use super::super::ToolCtx;
|
|
|
|
/// Tool that lists the immediate contents of a workspace directory.
|
|
pub struct DirList;
|
|
|
|
impl Tool for DirList {
|
|
fn name(&self) -> &'static str {
|
|
"dir_list"
|
|
}
|
|
|
|
fn description(&self) -> &'static str {
|
|
"List files and directories in a directory. Use this to explore the workspace structure."
|
|
}
|
|
|
|
fn parameters(&self) -> Value {
|
|
json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"path": {
|
|
"type": "string",
|
|
"description": "Directory path to list (relative to workspace root)"
|
|
}
|
|
},
|
|
"required": ["path"]
|
|
})
|
|
}
|
|
|
|
/// List the immediate entries of the requested workspace directory.
|
|
///
|
|
/// Flow: extract `path` argument → resolve against workspace roots →
|
|
/// short-circuit with a plain message if the path doesn't exist or
|
|
/// isn't a directory → `read_dir` → map each entry to its name
|
|
/// (appending `/` for subdirectories) → join into a formatted listing
|
|
/// with an entry-count header showing the canonicalized path.
|
|
///
|
|
/// Why: entries whose metadata fails to read (`e.ok()` filter) are
|
|
/// silently skipped rather than aborting the whole listing.
|
|
///
|
|
/// Return: header + newline-joined entry names, or an error if the
|
|
/// `path` argument is missing or `read_dir` fails outright.
|
|
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
|
|
let rel = args.get("path")
|
|
.and_then(|v| v.as_str())
|
|
.ok_or_else(|| anyhow!("missing required argument: path"))?;
|
|
|
|
let path = super::super::resolve_path(&ctx.workspaces, rel)?;
|
|
|
|
if !path.exists() {
|
|
return Ok(format!("path '{}' does not exist (resolved to {})", rel, path.display()));
|
|
}
|
|
if !path.is_dir() {
|
|
return Ok(format!("path '{}' is not a directory (resolved to {})", rel, path.display()));
|
|
}
|
|
|
|
let entries: Vec<String> = fs::read_dir(&path)
|
|
.map_err(|e| anyhow!("failed to read directory '{rel}': {e}"))?
|
|
.filter_map(std::result::Result::ok)
|
|
.map(|e| {
|
|
let name = e.file_name().to_string_lossy().to_string();
|
|
let is_dir = e.file_type().is_ok_and(|t| t.is_dir());
|
|
if is_dir {
|
|
format!("{name}/")
|
|
} else {
|
|
name
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
let canon = path.canonicalize().unwrap_or(path);
|
|
let header = format!("{} entries in {}:\n", entries.len(), canon.display());
|
|
if entries.is_empty() {
|
|
Ok(format!("{} (empty directory)", header.trim()))
|
|
} else {
|
|
Ok(header + &entries.join("\n"))
|
|
}
|
|
}
|
|
}
|