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)
37 .map_err(|e| SynapticError::Tool(format!("serialization: {}", e)))
38}
39
40#[tool]
42async fn read_file(
43 #[field] backend: Arc<dyn Backend>,
44 path: String,
46 #[default = 0]
48 offset: usize,
49 #[default = 2000]
51 limit: usize,
52) -> Result<Value, SynapticError> {
53 let content = backend.read_file(&path, offset, limit).await?;
54 Ok(Value::String(content))
55}
56
57#[tool]
59async fn write_file(
60 #[field] backend: Arc<dyn Backend>,
61 path: String,
63 content: String,
65) -> Result<Value, SynapticError> {
66 backend.write_file(&path, &content).await?;
67 Ok(Value::String(format!("wrote {}", path)))
68}
69
70#[tool]
72async fn edit_file(
73 #[field] backend: Arc<dyn Backend>,
74 path: String,
76 old_string: String,
78 new_string: String,
80 #[default = false]
82 replace_all: bool,
83) -> Result<Value, SynapticError> {
84 backend
85 .edit_file(&path, &old_string, &new_string, replace_all)
86 .await?;
87 Ok(Value::String(format!("edited {}", path)))
88}
89
90#[tool(name = "glob")]
92async fn glob_files(
93 #[field] backend: Arc<dyn Backend>,
94 pattern: String,
96 #[default = ".".to_string()]
98 path: String,
99) -> Result<Value, SynapticError> {
100 let matches = backend.glob(&pattern, &path).await?;
101 Ok(Value::String(matches.join("\n")))
102}
103
104#[tool]
106async fn grep(
107 #[field] backend: Arc<dyn Backend>,
108 pattern: String,
110 path: Option<String>,
112 glob: Option<String>,
114 output_mode: Option<String>,
116) -> Result<Value, SynapticError> {
117 let mode = match output_mode.as_deref() {
118 Some("content") => GrepOutputMode::Content,
119 Some("count") => GrepOutputMode::Count,
120 _ => GrepOutputMode::FilesWithMatches,
121 };
122 let result = backend
123 .grep(&pattern, path.as_deref(), glob.as_deref(), mode)
124 .await?;
125 Ok(Value::String(result))
126}
127
128#[tool]
130async fn execute(
131 #[field] backend: Arc<dyn Backend>,
132 command: String,
134 timeout: Option<u64>,
136) -> Result<Value, SynapticError> {
137 let duration = timeout.map(std::time::Duration::from_secs);
138 let result = backend.execute(&command, duration).await?;
139 Ok(json!({
140 "stdout": result.stdout,
141 "stderr": result.stderr,
142 "exit_code": result.exit_code,
143 }))
144}