synaptic_deep/tools/
mod.rs1use serde_json::{json, Value};
2use std::sync::Arc;
3use synaptic_core::{SynapticError, Tool};
4use synaptic_macros::{tool, traceable};
5
6use crate::backend::{Backend, GrepOutputMode};
7
8#[traceable(skip = "backend")]
13pub fn create_filesystem_tools(backend: Arc<dyn Backend>) -> Vec<Arc<dyn Tool>> {
14 let mut tools: Vec<Arc<dyn Tool>> = vec![
15 ls(backend.clone()),
16 read_file(backend.clone()),
17 write_file(backend.clone()),
18 edit_file(backend.clone()),
19 glob_files(backend.clone()),
20 grep(backend.clone()),
21 ];
22 if backend.supports_execution() {
23 tools.push(execute(backend));
24 }
25 tools
26}
27
28#[tool]
30async fn ls(
31 #[field] backend: Arc<dyn Backend>,
32 path: String,
34) -> Result<Value, SynapticError> {
35 let entries = backend.ls(&path).await?;
36 serde_json::to_value(entries).map_err(|e| SynapticError::Tool(format!("serialization: {}", e)))
37}
38
39#[tool]
41async fn read_file(
42 #[field] backend: Arc<dyn Backend>,
43 path: String,
45 #[default = 0]
47 offset: usize,
48 #[default = 2000]
50 limit: usize,
51) -> Result<Value, SynapticError> {
52 let content = backend.read_file(&path, offset, limit).await?;
53 Ok(Value::String(content))
54}
55
56#[tool]
58async fn write_file(
59 #[field] backend: Arc<dyn Backend>,
60 path: String,
62 content: String,
64) -> Result<Value, SynapticError> {
65 backend.write_file(&path, &content).await?;
66 Ok(Value::String(format!("wrote {}", path)))
67}
68
69#[tool]
71async fn edit_file(
72 #[field] backend: Arc<dyn Backend>,
73 path: String,
75 old_string: String,
77 new_string: String,
79 #[default = false]
81 replace_all: bool,
82) -> Result<Value, SynapticError> {
83 backend
84 .edit_file(&path, &old_string, &new_string, replace_all)
85 .await?;
86 Ok(Value::String(format!("edited {}", path)))
87}
88
89#[tool(name = "glob")]
91async fn glob_files(
92 #[field] backend: Arc<dyn Backend>,
93 pattern: String,
95 #[default = ".".to_string()]
97 path: String,
98) -> Result<Value, SynapticError> {
99 let matches = backend.glob(&pattern, &path).await?;
100 Ok(Value::String(matches.join("\n")))
101}
102
103#[tool]
105async fn grep(
106 #[field] backend: Arc<dyn Backend>,
107 pattern: String,
109 path: Option<String>,
111 glob: Option<String>,
113 output_mode: Option<String>,
115) -> Result<Value, SynapticError> {
116 let mode = match output_mode.as_deref() {
117 Some("content") => GrepOutputMode::Content,
118 Some("count") => GrepOutputMode::Count,
119 _ => GrepOutputMode::FilesWithMatches,
120 };
121 let result = backend
122 .grep(&pattern, path.as_deref(), glob.as_deref(), mode)
123 .await?;
124 Ok(Value::String(result))
125}
126
127#[tool]
129async fn execute(
130 #[field] backend: Arc<dyn Backend>,
131 command: String,
133 timeout: Option<u64>,
135) -> Result<Value, SynapticError> {
136 let duration = timeout.map(std::time::Duration::from_secs);
137 let result = backend.execute(&command, duration).await?;
138 Ok(json!({
139 "stdout": result.stdout,
140 "stderr": result.stderr,
141 "exit_code": result.exit_code,
142 }))
143}