use std::path::Path;
use serde_json::Value;
pub const HELD_PREFIX: &str = "refs/litany/held/";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Held {
pub tool_use_id: String,
pub tool: String,
pub reason: String,
}
pub fn parse(blob: &str) -> Option<Held> {
let value: Value = serde_json::from_str(blob).ok()?;
let text = |key: &str| value.get(key).and_then(Value::as_str).map(str::to_owned);
Some(Held {
tool_use_id: text("tool_use_id")?,
tool: text("tool")?,
reason: text("reason").unwrap_or_default(),
})
}
pub fn read(workspace: &Path, agent_id: &str) -> Option<Held> {
let repo = workspace.join("repo.git");
let out = crate::git_env::output(crate::git_env::git().arg("--git-dir").arg(&repo).args([
"cat-file",
"blob",
&format!("{HELD_PREFIX}{agent_id}"),
]))
.ok()
.filter(|out| out.status.success())?;
parse(&String::from_utf8_lossy(&out.stdout))
}
pub fn all(workspace: &Path) -> Vec<(String, Held)> {
let repo = workspace.join("repo.git");
let Some(out) =
crate::git_env::output(crate::git_env::git().arg("--git-dir").arg(&repo).args([
"for-each-ref",
"--format=%(refname)",
HELD_PREFIX,
]))
.ok()
.filter(|out| out.status.success())
else {
return Vec::new();
};
String::from_utf8_lossy(&out.stdout)
.lines()
.filter_map(|line| line.strip_prefix(HELD_PREFIX))
.filter_map(|agent| Some((agent.to_owned(), read(workspace, agent)?)))
.collect()
}
#[cfg(test)]
mod tests;