claude_agent/tools/
kill.rs

1//! KillShell tool - terminates background shell processes.
2
3use std::sync::Arc;
4
5use async_trait::async_trait;
6use schemars::JsonSchema;
7use serde::Deserialize;
8
9use super::SchemaTool;
10use super::context::ExecutionContext;
11use super::process::ProcessManager;
12use crate::types::ToolResult;
13
14#[derive(Debug, Deserialize, JsonSchema)]
15#[schemars(deny_unknown_fields)]
16pub struct KillShellInput {
17    /// The ID of the background shell to kill
18    pub shell_id: String,
19}
20
21pub struct KillShellTool {
22    process_manager: Arc<ProcessManager>,
23}
24
25impl KillShellTool {
26    pub fn new() -> Self {
27        Self {
28            process_manager: Arc::new(ProcessManager::new()),
29        }
30    }
31
32    pub fn with_process_manager(manager: Arc<ProcessManager>) -> Self {
33        Self {
34            process_manager: manager,
35        }
36    }
37
38    pub fn process_manager(&self) -> &Arc<ProcessManager> {
39        &self.process_manager
40    }
41}
42
43impl Default for KillShellTool {
44    fn default() -> Self {
45        Self::new()
46    }
47}
48
49#[async_trait]
50impl SchemaTool for KillShellTool {
51    type Input = KillShellInput;
52
53    const NAME: &'static str = "KillShell";
54    const DESCRIPTION: &'static str = r#"
55- Kills a running background bash shell by its ID
56- Takes a shell_id parameter identifying the shell to kill
57- Returns a success or failure status
58- Use this tool when you need to terminate a long-running shell
59- Shell IDs can be obtained from Bash tool responses when using run_in_background"#;
60
61    async fn handle(&self, input: KillShellInput, _context: &ExecutionContext) -> ToolResult {
62        match self.process_manager.kill(&input.shell_id).await {
63            Ok(()) => ToolResult::success(format!("Process '{}' terminated", input.shell_id)),
64            Err(e) => ToolResult::error(e),
65        }
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72    use crate::tools::Tool;
73    use crate::types::ToolOutput;
74    use std::path::PathBuf;
75
76    #[tokio::test]
77    async fn test_kill_nonexistent_shell() {
78        let tool = KillShellTool::new();
79        let context = ExecutionContext::default();
80        let result = tool
81            .execute(
82                serde_json::json!({"shell_id": "nonexistent_shell"}),
83                &context,
84            )
85            .await;
86        assert!(result.is_error());
87    }
88
89    #[tokio::test]
90    async fn test_kill_running_process() {
91        let mgr = Arc::new(ProcessManager::new());
92        let id = mgr.spawn("sleep 10", &PathBuf::from("/tmp")).await.unwrap();
93
94        let tool = KillShellTool::with_process_manager(mgr.clone());
95        let context = ExecutionContext::default();
96        let result = tool
97            .execute(serde_json::json!({"shell_id": id}), &context)
98            .await;
99
100        match &result.output {
101            ToolOutput::Success(msg) => assert!(msg.contains("terminated")),
102            _ => panic!("Expected success"),
103        }
104
105        assert!(!mgr.is_running(&id).await);
106    }
107}