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 = match serde_json::from_str(&raw) {
45 Ok(p) => p,
46 Err(_) => return Ok(()),
47 };
48
49 if payload.tool_name.as_deref() != Some("Bash") {
50 return Ok(());
51 }
52 let input = match payload.tool_input {
53 Some(v) => v,
54 None => return Ok(()),
55 };
56 let cwd_str = input.get("cwd").and_then(|v| v.as_str());
57 let cwd = match cwd_str {
58 Some(s) => std::path::PathBuf::from(s),
59 None => std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")),
60 };
61 if cwd_is_healthy(&cwd) {
62 return Ok(());
63 }
64
65 let command = input.get("command").and_then(|v| v.as_str()).unwrap_or("");
66 eprintln!(
67 "STOP. gw guard: cwd '{}' no longer exists or is not a directory. \
68 The worktree may have been removed while this session was paused. \
69 Do NOT execute any further commands. Ask the user to confirm the \
70 situation before continuing. (blocked command: '{}')",
71 cwd.display(),
72 command,
73 );
74 Err(CwError::ExitCode(2))
75}