use serde_json::Value;
pub fn entry_commands(entry: &Value) -> Vec<&str> {
if let Some(hooks) = entry.get("hooks").and_then(Value::as_array) {
return hooks
.iter()
.filter_map(|h| h.get("command").and_then(Value::as_str))
.collect();
}
entry
.get("command")
.and_then(Value::as_str)
.into_iter()
.collect()
}
pub fn primary_handler(entry: &Value) -> Option<&Value> {
if let Some(hooks) = entry.get("hooks").and_then(Value::as_array) {
return hooks.first();
}
entry.get("command").is_some().then_some(entry)
}
pub fn is_openlatch_command(cmd: &str) -> bool {
cmd.contains("openlatch-hook")
}
pub fn is_sidecar_owned(cmd: &str, agent: &str) -> bool {
is_openlatch_command(cmd) && cmd.contains(&format!("--agent {agent} "))
}
pub fn is_sidecar_owned_entry(entry: &Value, agent: &str) -> bool {
entry.get("hooks").is_none()
&& entry_commands(entry)
.first()
.is_some_and(|cmd| is_sidecar_owned(cmd, agent))
}
pub fn is_sidecar_owned_node(node: &jsonc_parser::cst::CstNode, agent: &str) -> bool {
node.to_serde_value()
.is_some_and(|value| is_sidecar_owned_entry(&value, agent))
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn entry_commands_reads_nested_and_flat() {
let nested = json!({
"matcher": "",
"_openlatch": {"v": 1},
"hooks": [
{"type": "command", "command": "a"},
{"type": "command", "command": "b"},
{"type": "command"}
]
});
assert_eq!(entry_commands(&nested), ["a", "b"]);
let flat = json!({"type": "command", "command": "c", "timeout": 10});
assert_eq!(entry_commands(&flat), ["c"]);
assert!(entry_commands(&json!({"type": "command"})).is_empty());
assert!(entry_commands(&json!("not an object")).is_empty());
}
#[test]
fn primary_handler_is_first_nested_or_the_flat_entry() {
let nested = json!({
"matcher": "",
"hooks": [{"command": "a"}, {"command": "b"}]
});
assert_eq!(primary_handler(&nested), Some(&json!({"command": "a"})));
let flat = json!({"type": "command", "command": "c"});
assert_eq!(primary_handler(&flat), Some(&flat));
let both = json!({"command": "x", "hooks": [{"command": "a"}]});
assert_eq!(primary_handler(&both), Some(&json!({"command": "a"})));
assert_eq!(primary_handler(&json!({"hooks": []})), None);
assert_eq!(primary_handler(&json!({"type": "command"})), None);
assert_eq!(primary_handler(&json!("not an object")), None);
}
#[test]
fn sidecar_ownership_is_our_exact_signature() {
let ours = r#""/x/openlatch-hook" --agent cursor --event stop --openlatch-dir "/o""#;
assert!(is_sidecar_owned(ours, "cursor"));
assert!(
!is_sidecar_owned(ours, "codex-cli"),
"another agent's is not ours"
);
let wrapper = "my-wrapper --then openlatch-hook";
assert!(is_openlatch_command(wrapper));
assert!(!is_sidecar_owned(wrapper, "cursor"));
assert!(is_sidecar_owned_entry(&json!({"command": ours}), "cursor"));
assert!(!is_sidecar_owned_entry(
&json!({"hooks": [{"command": ours}]}),
"cursor"
));
}
}