mod settings;
#[cfg(test)]
mod tests;
use std::path::{Path, PathBuf};
use crate::core::agent_deployer::{DeployResult, deploy_agents};
use crate::core::instruction_pipeline::{PipelineInput, PipelineOutput, build_instructions};
use crate::core::paths::FrameworkPaths;
use crate::core::skill_deployer::{DeployStats, deploy_skills};
use settings::{
deploy_output_style, inject_trusty_memory_mcp, inject_trusty_search_mcp,
preseed_workspace_trust_home, remove_global_trusty_memory_hooks, write_output_style,
write_project_hooks,
};
#[derive(Debug)]
pub struct PrepReport {
pub deploy: DeployResult,
pub skill_deploy: DeployStats,
pub instructions: PipelineOutput,
pub stash: PathBuf,
pub output_style: Option<PathBuf>,
pub hooks_written: bool,
}
#[derive(Debug, thiserror::Error)]
pub enum PrepError {
#[error("agent deploy failed: {0}")]
Deploy(String),
#[error("skill deploy failed: {0}")]
SkillDeploy(String),
#[error("instruction pipeline failed: {0}")]
Instructions(#[from] crate::core::instruction_pipeline::PipelineError),
#[error("io error for {path}: {source}")]
Io {
path: PathBuf,
source: std::io::Error,
},
}
pub fn prepare_session(fw: &FrameworkPaths, project_dir: &Path) -> Result<PrepReport, PrepError> {
let deploy = deploy_agents(&fw.agent_source_dir(), &fw.claude_agents_dir())
.map_err(|err| PrepError::Deploy(err.to_string()))?;
let skill_deploy = deploy_skills(&fw.skill_source_dir(), &fw.claude_skills_dir())
.map_err(|err| PrepError::SkillDeploy(err.to_string()))?;
let input = PipelineInput {
framework_instructions_path: fw.framework_instructions_path(),
agents_dir: fw.claude_agents_dir(),
claude_md_path: project_dir.join("CLAUDE.md"),
};
let instructions = build_instructions(&input)?;
let resolved_prompt = crate::core::instruction_overrides::resolve_pm_prompt(project_dir);
let stash_dir = project_dir.join(".trusty-mpm");
std::fs::create_dir_all(&stash_dir).map_err(|source| PrepError::Io {
path: stash_dir.clone(),
source,
})?;
let stash = stash_dir.join("last-instructions.md");
std::fs::write(&stash, &resolved_prompt).map_err(|source| PrepError::Io {
path: stash.clone(),
source,
})?;
if let Err(err) = write_output_style(project_dir) {
tracing::warn!("failed to set trusty-mpm output style: {err}");
}
let hooks_written = match write_project_hooks(project_dir) {
Ok(()) => true,
Err(err) => {
tracing::warn!("failed to write trusty-memory project hooks: {err}");
false
}
};
if let Err(err) = inject_trusty_memory_mcp(project_dir) {
tracing::warn!("failed to inject trusty-memory MCP server: {err}");
}
if let Err(err) = inject_trusty_search_mcp(project_dir) {
tracing::warn!("failed to inject trusty-search MCP server: {err}");
}
if let Err(err) = preseed_workspace_trust_home(project_dir) {
tracing::warn!("failed to pre-seed workspace trust: {err}");
}
if let Err(err) = remove_global_trusty_memory_hooks() {
tracing::warn!("failed to remove global trusty-memory hooks: {err}");
}
let output_style = match dirs::home_dir() {
Some(home) => match deploy_output_style(&home) {
Ok(path) => Some(path),
Err(err) => {
tracing::warn!("failed to deploy trusty-mpm output style file: {err}");
None
}
},
None => {
tracing::warn!("skipping output style deploy: home directory unresolved");
None
}
};
Ok(PrepReport {
deploy,
skill_deploy,
instructions,
stash,
output_style,
hooks_written,
})
}
pub fn build_system_prompt() -> Option<String> {
let home = dirs::home_dir()?;
let path = home
.join(".trusty-mpm")
.join("framework")
.join("instructions")
.join("INSTRUCTIONS.md");
if let Ok(contents) = std::fs::read_to_string(&path) {
let trimmed = contents.trim_end();
if !trimmed.is_empty() {
return Some(trimmed.to_string());
}
}
let generated = crate::core::instruction_pipeline::install_system_prompt().ok()?;
let contents = std::fs::read_to_string(&generated).ok()?;
let trimmed = contents.trim_end();
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_string())
}
}
pub fn build_system_prompt_for(project_dir: &Path) -> String {
crate::core::instruction_overrides::resolve_pm_prompt(project_dir)
}