1use std::path::PathBuf;
2use std::sync::atomic::{AtomicU64, Ordering};
3use std::sync::Arc;
4use std::time::Duration;
5
6use crate::tools::{LocalTransport, RunShell, ToolTransport};
7use crate::ToolRegistry;
8use serde_json::json;
9
10use crate::tui::events::UiEvent;
11
12pub fn build_bash_registry(root: &std::path::Path) -> ToolRegistry {
13 let transport: Arc<dyn ToolTransport> = Arc::new(LocalTransport);
14 ToolRegistry::new(transport).register(Arc::new(
15 RunShell::new(root).with_timeout(Duration::from_secs(300)),
16 ))
17}
18
19pub fn resolve_workspace_root() -> PathBuf {
20 std::env::var("RECURSIVE_WORKSPACE")
21 .map(PathBuf::from)
22 .ok()
23 .or_else(|| std::env::current_dir().ok())
24 .unwrap_or_else(|| PathBuf::from("."))
25}
26
27pub async fn run_bash_command(
28 registry: &ToolRegistry,
29 seq: &AtomicU64,
30 cmd: String,
31 event_tx: &tokio::sync::mpsc::UnboundedSender<UiEvent>,
32) {
33 let n = seq.fetch_add(1, Ordering::Relaxed);
34 let id = format!("ui-bash-{n}");
35 let arguments = json!({ "command": cmd });
36 let arguments_str = arguments.to_string();
37
38 let _ = event_tx.send(UiEvent::ToolCall {
39 id: id.clone(),
40 name: "run_shell".into(),
41 arguments: arguments_str,
42 });
43
44 let (output, success) = match registry.invoke("run_shell", arguments).await {
45 Ok(out) => (out, true),
46 Err(e) => (format!("ERROR: {e}"), false),
47 };
48
49 let _ = event_tx.send(UiEvent::ToolResult {
50 id,
51 name: "run_shell".into(),
52 output,
53 success,
54 });
55}