use pushkin_core::edits::Replacement;
use serde_json::Value;
use super::super::shared::{relativize, session_from};
use super::super::{FileWrite, Intent, ParseOutcome, ToolAction};
pub(crate) fn normalize_hermes(value: &Value) -> ParseOutcome {
let session = session_from(value, "session_id");
let Some(tool_input) = value.get("tool_input") else {
return Err(None);
};
let path = tool_input
.get("path")
.or_else(|| tool_input.get("file_path"))
.and_then(Value::as_str)
.map(relativize)
.ok_or(None)?;
let tool_name = value.get("tool_name").and_then(Value::as_str);
if tool_name == Some("patch") {
return Ok(ToolAction {
session,
files: vec![FileWrite {
path,
content: String::new(),
edits: hermes_replacements(tool_input),
}],
is_stop: false,
intent: Intent::MutateNoContent("patch"),
command: None,
});
}
let well_formed = tool_name.is_some();
let content = tool_input.get("content").and_then(Value::as_str);
match (well_formed, content) {
(true, Some(content)) => Ok(ToolAction {
session,
files: vec![FileWrite {
path,
content: content.to_owned(),
edits: Vec::new(),
}],
is_stop: false,
intent: Intent::Write,
command: None,
}),
_ => Err(Some(path)),
}
}
pub(crate) fn hermes_replacements(tool_input: &Value) -> Vec<Replacement> {
match tool_input.get("mode").and_then(Value::as_str) {
None | Some("replace") => {}
Some(_) => return Vec::new(),
}
if tool_input.get("replace_all").and_then(Value::as_bool) == Some(true) {
return Vec::new();
}
let Some(old) = tool_input.get("old_string").and_then(Value::as_str) else {
return Vec::new();
};
let Some(new) = tool_input.get("new_string").and_then(Value::as_str) else {
return Vec::new();
};
vec![Replacement {
old: old.to_owned(),
new: new.to_owned(),
replace_all: false,
anchor: None,
}]
}