mod descriptors;
use crate::prompt::Deps;
use crate::prompt::Error;
use crate::prompt::step::{META_FILE, REQUEST_FILE, StepMeta};
use serde_json::Value;
use std::path::Path;
pub(super) const GOAL_FILE: &str = "goal.md";
pub(super) const SOUL_FILE: &str = "soul.md";
pub(super) fn spawn_branch(
workspace: &Path,
worktree_path: &Path,
agent_id: &str,
deps: &Deps<'_>,
) -> Result<(), Error> {
let wt_str = worktree_path.to_string_lossy().to_string();
let branch_ref = crate::workspace::agent_ref(agent_id);
deps.git
.run(
&crate::workspace::repo_git(workspace),
&[
"worktree",
"add",
"-b",
branch_ref.as_str(),
wt_str.as_str(),
crate::workspace::DEFAULT_CONFIG_REF,
],
)
.map_err(|source| Error::Git {
op: "worktree add",
source,
})
}
pub(super) fn prepend_goal(goal: &str, soul: &str) -> String {
format!("<goal>\n{goal}\n</goal>\n\n{soul}")
}
pub(super) fn write_dispatch_files(
worktree_path: &Path,
goal_text: &str,
soul_text: &str,
) -> Result<(), Error> {
std::fs::create_dir_all(worktree_path)?;
std::fs::write(worktree_path.join(GOAL_FILE), goal_text)?;
std::fs::write(worktree_path.join(SOUL_FILE), soul_text)?;
Ok(())
}
pub(super) fn commit_dispatch(
worktree_path: &Path,
conv_id: &str,
granted: &[String],
deps: &Deps<'_>,
) -> Result<(), Error> {
trim_to_context(worktree_path, granted, deps.git)?;
deps.git
.run(worktree_path, &["add", GOAL_FILE, SOUL_FILE])
.map_err(|source| Error::Git { op: "add", source })?;
let msg = format!("step 001: dispatch [{conv_id}]");
deps.git
.run(worktree_path, &["commit", "-m", msg.as_str()])
.map_err(|source| Error::Git {
op: "commit",
source,
})
}
pub(crate) fn trim_to_context(
worktree_path: &Path,
granted: &[String],
git: &dyn crate::template::GitRunner,
) -> Result<(), Error> {
let mut args: Vec<&str> = vec!["rm", "-r", "-q", "--ignore-unmatch", "--"];
args.extend_from_slice(crate::workspace::CONTROL_PATHS);
git.run(worktree_path, &args).map_err(|source| Error::Git {
op: "rm control files",
source,
})?;
descriptors::prune_ungranted(worktree_path, granted, git)
}
pub(super) fn read_branch_tip(worktree_path: &Path, deps: &Deps<'_>) -> Result<String, Error> {
deps.git
.run_capture(worktree_path, &["rev-parse", "HEAD"])
.map_err(|source| Error::Git {
op: "rev-parse",
source,
})
}
pub(super) fn write_request(
conv_repo: &Path,
step_dir_rel_str: &str,
request_value: &Value,
) -> Result<(), Error> {
let step_dir_abs = conv_repo.join(step_dir_rel_str);
std::fs::create_dir_all(&step_dir_abs)?;
let bytes = serde_json::to_vec_pretty(request_value).expect("Value is always serializable");
std::fs::write(step_dir_abs.join(REQUEST_FILE), bytes)?;
Ok(())
}
pub(super) fn write_meta(
conv_repo: &Path,
step_dir_rel_str: &str,
meta: &StepMeta,
) -> Result<(), Error> {
let step_dir_abs = conv_repo.join(step_dir_rel_str);
std::fs::create_dir_all(&step_dir_abs)?;
let bytes = serde_json::to_vec_pretty(meta).expect("StepMeta is always serializable");
std::fs::write(step_dir_abs.join(META_FILE), bytes)?;
Ok(())
}