git_worktree_manager/operations/
guard.rs1use std::io::Read;
15use std::path::Path;
16
17use crate::error::{CwError, Result};
18
19#[derive(Debug, serde::Deserialize)]
20struct HookPayload {
21 tool_name: Option<String>,
22 tool_input: Option<serde_json::Value>,
23}
24
25fn read_input(source: &str) -> std::io::Result<String> {
26 if source == "-" {
27 let mut s = String::new();
28 std::io::stdin().read_to_string(&mut s)?;
29 Ok(s)
30 } else {
31 std::fs::read_to_string(source)
32 }
33}
34
35fn cwd_is_healthy(cwd: &Path) -> bool {
36 cwd.exists() && cwd.is_dir()
37}
38
39pub fn run(tool_input_source: &str) -> Result<()> {
40 let raw = read_input(tool_input_source).map_err(CwError::Io)?;
41 let payload: HookPayload = serde_json::from_str(&raw).map_err(CwError::Json)?;
42
43 if payload.tool_name.as_deref() != Some("Bash") {
44 return Ok(());
45 }
46 let input = match payload.tool_input {
47 Some(v) => v,
48 None => return Ok(()),
49 };
50 let cwd_str = input.get("cwd").and_then(|v| v.as_str());
51 let cwd = match cwd_str {
52 Some(s) => std::path::PathBuf::from(s),
53 None => std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")),
54 };
55 if cwd_is_healthy(&cwd) {
56 return Ok(());
57 }
58
59 let command = input.get("command").and_then(|v| v.as_str()).unwrap_or("");
60 eprintln!(
61 "STOP. gw guard: cwd '{}' no longer exists or is not a directory. \
62 The worktree may have been removed while this session was paused. \
63 Do NOT execute any further commands. Ask the user to confirm the \
64 situation before continuing. (blocked command: '{}')",
65 cwd.display(),
66 command,
67 );
68 Err(CwError::ExitCode(2))
69}