use serde::Deserialize;
use crate::cli::SessionAction;
use crate::commands::project::resolve_dir;
use crate::formatters::session::deploy_summary_line;
pub(crate) async fn start_session(
client: &reqwest::Client,
url: &str,
dir: Option<String>,
) -> anyhow::Result<()> {
let path = resolve_dir(dir)?;
let path = path.canonicalize().unwrap_or(path);
if let Some((_source_id, _workspace, git_root)) = crate::commands::guided::derive_project(&path)
{
let new_action = SessionAction::New {
repo: git_root.to_string_lossy().to_string(),
git_ref: "HEAD".to_string(),
task: String::new(),
name_hint: None,
runtime: trusty_mpm::runtime::RuntimeKind::default(),
};
crate::commands::managed_route::run(client, url, &new_action).await?;
return Ok(());
}
let fw = trusty_mpm::core::paths::FrameworkPaths::default();
start_session_in_place(client, url, &path, &fw).await
}
async fn start_session_in_place(
client: &reqwest::Client,
url: &str,
path: &std::path::Path,
fw: &trusty_mpm::core::paths::FrameworkPaths,
) -> anyhow::Result<()> {
match trusty_mpm::core::session_launch::prepare_session(fw, path) {
Ok(report) => {
println!(
"{}",
deploy_summary_line(
"Agents",
report.deploy.deployed.len(),
report.deploy.skipped.len(),
report.deploy.unchanged.len(),
)
);
println!(
"{}",
deploy_summary_line(
"Skills",
report.skill_deploy.deployed.len(),
report.skill_deploy.skipped.len(),
report.skill_deploy.unchanged.len(),
)
);
if report.instructions.claude_md_created {
println!(" Created CLAUDE.md stub in {}", path.display());
}
println!(
"Instructions: {} agents in delegation authority",
report.instructions.agent_count
);
println!(
" Merged instructions written to {}",
report.stash.display()
);
if let Some(ctx) = report.catchup_context {
println!("\n---\n\n## Recent Activity (catch-up)\n\n{ctx}");
}
for err in &report.roster_errors {
eprintln!("error: roster provisioning gap: {err}");
}
}
Err(err) => eprintln!("warning: session preparation failed: {err}"),
}
#[derive(Deserialize)]
struct Body {
#[serde(default)]
name: String,
}
let body: Body = client
.post(format!("{url}/sessions"))
.json(&serde_json::json!({
"project": path,
"project_path": path,
}))
.send()
.await?
.error_for_status()?
.json()
.await?;
let workdir = path.to_string_lossy().to_string();
let new_session = std::process::Command::new("tmux")
.args(["new-session", "-d", "-s", &body.name, "-c", &workdir])
.status();
match new_session {
Ok(status) if status.success() => {
let send = std::process::Command::new("tmux")
.args([
"send-keys",
"-t",
&body.name,
&format!(
"claude {}",
trusty_mpm::core::model_inject::PERMISSION_MODE_FLAG
),
"Enter",
])
.status();
match send {
Ok(s) if s.success() => {
println!("started session {} (tmux + claude)", body.name);
}
Ok(_) | Err(_) => {
eprintln!(
"warning: tmux session {} created but failed to start claude",
body.name
);
println!("started session {}", body.name);
}
}
}
Ok(_) | Err(_) => {
eprintln!(
"warning: failed to create tmux session {}; run `claude` manually in {}",
body.name, workdir
);
println!("started session {}", body.name);
}
}
Ok(())
}
#[cfg(test)]
#[path = "start_tests.rs"]
mod tests;