use std::sync::Arc;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::Value;
use sgr_agent_core::agent_tool::{Tool, ToolError, ToolOutput, parse_args};
use sgr_agent_core::context::AgentContext;
use sgr_agent_core::schema::json_schema_for;
use crate::backend::FileBackend;
use crate::helpers::backend_err;
use crate::trust::infer_trust;
pub struct ReadAllTool<B: FileBackend>(pub Arc<B>);
#[derive(Deserialize, JsonSchema)]
struct ReadAllArgs {
path: String,
}
#[async_trait::async_trait]
impl<B: FileBackend> Tool for ReadAllTool<B> {
fn name(&self) -> &str {
"read_all"
}
fn description(&self) -> &str {
"Read ALL files in a directory in one call. Much faster than listing then reading one by one. Returns each file with its path header."
}
fn is_read_only(&self) -> bool {
true
}
fn parameters_schema(&self) -> Value {
json_schema_for::<ReadAllArgs>()
}
async fn execute(&self, args: Value, ctx: &mut AgentContext) -> Result<ToolOutput, ToolError> {
self.execute_readonly(args, ctx).await
}
async fn execute_readonly(
&self,
args: Value,
_ctx: &AgentContext,
) -> Result<ToolOutput, ToolError> {
let a: ReadAllArgs = parse_args(&args)?;
let listing = self.0.list(&a.path).await.map_err(backend_err)?;
let mut output = String::new();
let mut count = 0u32;
for line in listing.lines().skip(1) {
let name = line.trim();
if name.is_empty() || name.ends_with('/') {
continue;
}
let full_path = if a.path.ends_with('/') {
format!("{}{}", a.path, name)
} else {
format!("{}/{}", a.path, name)
};
match self.0.read(&full_path, false, 0, 0).await {
Ok(content) => {
let trust = infer_trust(&full_path);
output.push_str(&format!(
"--- {} [{}] ---\n{}\n\n",
full_path, trust, content
));
count += 1;
}
Err(e) => {
output.push_str(&format!("--- {} ---\n[error: {}]\n\n", full_path, e));
}
}
}
if count == 0 {
output.push_str("(no files found)\n");
}
Ok(ToolOutput::text(output))
}
}