mod descriptors;
pub(crate) mod inherited;
mod reviewer_read;
mod skill_bodies;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod tests_facts;
mod trim;
mod unsettled;
pub(crate) use descriptors::{Grant, Undescribed, require_described};
pub(crate) use trim::trim_to_context;
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 DEFAULT_MAX_TOKENS: u32 = 4096;
pub(super) const GOAL_FILE: &str = "goal.md";
pub(super) const SOUL_FILE: &str = "soul.md";
pub(crate) const SYSTEM_SLOT_FILES: [&str; 3] = [
GOAL_FILE,
SOUL_FILE,
crate::workspace::agent_name::NAME_FILE,
];
pub(super) fn spawn_branch(
workspace: &Path,
worktree_path: &Path,
agent_id: &str,
fork_point: &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(),
fork_point,
],
)
.map_err(|source| Error::Git {
op: "worktree add",
source,
})
}
pub(super) fn compose_system(goal: &str, name: Option<&str>, soul: &str) -> String {
let identity = name.map_or_else(String::new, |n| format!("Your name is {n}.\n\n"));
format!("<goal>\n{goal}\n</goal>\n\n{identity}{soul}")
}
pub(super) fn write_dispatch_files(
worktree_path: &Path,
goal_text: &str,
soul_text: &str,
pins: &crate::prompt::PinnedDocs,
) -> 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)?;
pins.write_into(worktree_path)?;
Ok(())
}
pub(super) fn commit_dispatch(
worktree_path: &Path,
conv_id: &str,
name: Option<&str>,
pins: &crate::prompt::PinnedDocs,
resolved: &super::Resolved<'_>,
deps: &Deps<'_>,
) -> Result<(), Error> {
trim_to_context(worktree_path, conv_id, &resolved.grant, name, deps.git)?;
let mut add_args: Vec<&str> = vec!["add", GOAL_FILE, SOUL_FILE];
add_args.extend(pins.iter().map(crate::prompt::PinnedDoc::dest));
deps.git
.run(worktree_path, &add_args)
.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(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(())
}