diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-27 21:13:14 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-27 21:13:14 +0300 |
| commit | 0ef94cdbc1b49af82f48ca4c7eec3eb0d73ae091 (patch) | |
| tree | fcaf72315c50cde448659b8e30438a8f2cd10f00 /src | |
| parent | 19be0a6ca78bca0579a96143532c687b7a90420f (diff) | |
fix: improve claude subagent hook lifecycle
fixes #58
Diffstat (limited to 'src')
| -rw-r--r-- | src/integration/assets/claude/herdr-agent-state.sh | 4 | ||||
| -rw-r--r-- | src/integration/mod.rs | 75 |
2 files changed, 76 insertions, 3 deletions
diff --git a/src/integration/assets/claude/herdr-agent-state.sh b/src/integration/assets/claude/herdr-agent-state.sh index d8acdd76..527fe9f8 100644 --- a/src/integration/assets/claude/herdr-agent-state.sh +++ b/src/integration/assets/claude/herdr-agent-state.sh @@ -46,8 +46,8 @@ if hook_input_file: hook_input = {} is_subagent = bool(hook_input.get("agent_id")) -if is_subagent: - raise SystemExit(0) +if is_subagent and action in ("idle", "release"): + action = "working" request_id = f"{source}:{int(time.time() * 1000)}:{random.randrange(1_000_000):06d}" if action == "release": diff --git a/src/integration/mod.rs b/src/integration/mod.rs index 5240420a..5d478d0e 100644 --- a/src/integration/mod.rs +++ b/src/integration/mod.rs @@ -141,6 +141,27 @@ pub(crate) fn install_claude() -> io::Result<ClaudeInstallPaths> { )?; ensure_command_hook( hooks, + "PostToolUse", + format!("bash {quoted_hook_path} working"), + 10, + Some("*"), + )?; + ensure_command_hook( + hooks, + "PostToolUseFailure", + format!("bash {quoted_hook_path} working"), + 10, + Some("*"), + )?; + ensure_command_hook( + hooks, + "SubagentStop", + format!("bash {quoted_hook_path} working"), + 10, + Some("*"), + )?; + ensure_command_hook( + hooks, "Stop", format!("bash {quoted_hook_path} idle"), 10, @@ -304,6 +325,21 @@ pub(crate) fn uninstall_claude() -> io::Result<ClaudeUninstallResult> { "PermissionRequest", &format!("bash {quoted_hook_path} blocked"), )?; + updated_settings |= remove_command_hook( + hooks, + "PostToolUse", + &format!("bash {quoted_hook_path} working"), + )?; + updated_settings |= remove_command_hook( + hooks, + "PostToolUseFailure", + &format!("bash {quoted_hook_path} working"), + )?; + updated_settings |= remove_command_hook( + hooks, + "SubagentStop", + &format!("bash {quoted_hook_path} working"), + )?; updated_settings |= remove_command_hook(hooks, "Stop", &format!("bash {quoted_hook_path} idle"))?; updated_settings |= remove_command_hook( @@ -747,6 +783,20 @@ mod tests { .unwrap() .contains(" blocked") ); + assert!(settings["hooks"]["PostToolUse"][0]["hooks"][0]["command"] + .as_str() + .unwrap() + .contains(" working")); + assert!( + settings["hooks"]["PostToolUseFailure"][0]["hooks"][0]["command"] + .as_str() + .unwrap() + .contains(" working") + ); + assert!(settings["hooks"]["SubagentStop"][0]["hooks"][0]["command"] + .as_str() + .unwrap() + .contains(" working")); assert!(settings["hooks"]["Stop"][0]["hooks"][0]["command"] .as_str() .unwrap() @@ -790,6 +840,21 @@ mod tests { .len(), 1 ); + assert_eq!( + settings["hooks"]["PostToolUse"].as_array().unwrap().len(), + 1 + ); + assert_eq!( + settings["hooks"]["PostToolUseFailure"] + .as_array() + .unwrap() + .len(), + 1 + ); + assert_eq!( + settings["hooks"]["SubagentStop"].as_array().unwrap().len(), + 1 + ); assert_eq!(settings["hooks"]["Stop"].as_array().unwrap().len(), 1); assert_eq!(settings["hooks"]["SessionEnd"].as_array().unwrap().len(), 1); @@ -810,7 +875,11 @@ mod tests { fs::write( claude_dir.join("settings.json"), format!( - r#"{{"hooks":{{"UserPromptSubmit":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' working","timeout":10}},{{"type":"command","command":"echo keep","timeout":10}}]}}],"Stop":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' idle","timeout":10}}]}}],"SessionEnd":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' release","timeout":10}}]}}]}}}}"#, + r#"{{"hooks":{{"UserPromptSubmit":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' working","timeout":10}},{{"type":"command","command":"echo keep","timeout":10}}]}}],"PermissionRequest":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' blocked","timeout":10}}]}}],"PostToolUse":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' working","timeout":10}}]}}],"PostToolUseFailure":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' working","timeout":10}}]}}],"SubagentStop":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' working","timeout":10}}]}}],"Stop":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' idle","timeout":10}}]}}],"SessionEnd":[{{"matcher":"*","hooks":[{{"type":"command","command":"bash '{}' release","timeout":10}}]}}]}}}}"#, + hook_path.display(), + hook_path.display(), + hook_path.display(), + hook_path.display(), hook_path.display(), hook_path.display(), hook_path.display(), @@ -838,6 +907,10 @@ mod tests { settings["hooks"]["UserPromptSubmit"][0]["hooks"][0]["command"], "echo keep" ); + assert!(settings["hooks"].get("PermissionRequest").is_none()); + assert!(settings["hooks"].get("PostToolUse").is_none()); + assert!(settings["hooks"].get("PostToolUseFailure").is_none()); + assert!(settings["hooks"].get("SubagentStop").is_none()); assert!(settings["hooks"].get("Stop").is_none()); assert!(settings["hooks"].get("SessionEnd").is_none()); |
