use crate::cli::{CliCompressionLevel, OptimizerAction, OverseerAction};
use crate::commands::daemon::{daemon_healthy, print_status};
use crate::formatters::session::short_id;
use crate::types::EventRow;
pub(crate) const SUB_AGENT_ENV: &str = trusty_common::claude_config::CLAUDE_MPM_SUB_AGENT_ENV_VAR;
pub(crate) async fn status(client: &reqwest::Client, url: &str) -> anyhow::Result<()> {
if !daemon_healthy(client, url).await {
println!("daemon: unreachable");
return Ok(());
}
print_status(client, url).await
}
pub(crate) async fn events(client: &reqwest::Client, url: &str) -> anyhow::Result<()> {
use serde::Deserialize;
#[derive(Deserialize)]
struct Body {
events: Vec<EventRow>,
}
let body: Body = client
.get(format!("{url}/events/poll"))
.send()
.await?
.error_for_status()?
.json()
.await?;
for e in &body.events {
println!("{} {} {}", e.at, short_id(&e.session), e.event);
}
Ok(())
}
pub(crate) async fn doctor(url: &str) -> anyhow::Result<()> {
use trusty_mpm::client::{CommandExecutor, CommandResult, TrustyCommand};
use trusty_mpm::core::doctor::CheckStatus;
let executor = CommandExecutor::new(url.to_string());
match executor.execute(TrustyCommand::Doctor).await {
CommandResult::Doctor(report) => {
println!("trusty-mpm doctor");
for check in &report.checks {
println!(
" {} {:<13} {}",
status_icon(check.status),
check.name,
check.message,
);
}
println!(
"\noverall: {} {}",
status_icon(report.overall),
match report.overall {
CheckStatus::Ok => "all checks passed",
CheckStatus::Warn => "passed with warnings",
CheckStatus::Fail => "one or more checks failed",
},
);
}
CommandResult::Error(msg) => eprintln!("doctor failed: {msg}"),
other => eprintln!("doctor: unexpected result {other:?}"),
}
Ok(())
}
fn status_icon(status: trusty_mpm::core::doctor::CheckStatus) -> &'static str {
use trusty_mpm::core::doctor::CheckStatus;
match status {
CheckStatus::Ok => "\u{2705}",
CheckStatus::Warn => "\u{26a0}\u{fe0f}",
CheckStatus::Fail => "\u{274c}",
}
}
pub(crate) async fn hook(client: &reqwest::Client, url: &str) -> anyhow::Result<()> {
if std::env::var_os(SUB_AGENT_ENV).is_some() {
return Ok(());
}
let event = std::env::var("CLAUDE_HOOK_EVENT").unwrap_or_else(|_| "Unknown".to_string());
let session_id = std::env::var("CLAUDE_SESSION_ID").unwrap_or_default();
let body = serde_json::json!({
"session_id": session_id,
"event": event,
"payload": {}
});
let req = client
.post(format!("{url}/hooks"))
.timeout(std::time::Duration::from_secs(2))
.json(&body)
.send();
let _ = req.await;
Ok(())
}
pub(crate) async fn coordinator(url: &str, message: String) -> anyhow::Result<()> {
use trusty_mpm::client::{CommandExecutor, CommandResult, TrustyCommand};
let executor = CommandExecutor::new(url.to_string());
match executor
.execute(TrustyCommand::CoordinatorChat { message })
.await
{
CommandResult::ChatReply { reply } => println!("{reply}"),
CommandResult::Error(msg) => {
eprintln!("coordinator: {msg}");
std::process::exit(1);
}
other => eprintln!("unexpected coordinator result: {other:?}"),
}
Ok(())
}
pub(crate) async fn overseer(
client: &reqwest::Client,
url: &str,
action: OverseerAction,
) -> anyhow::Result<()> {
match action {
OverseerAction::Status => {
let body: serde_json::Value = client
.get(format!("{url}/overseer"))
.send()
.await?
.error_for_status()?
.json()
.await?;
let overseer = &body["overseer"];
let enabled = overseer
.get("enabled")
.and_then(|v| v.as_bool())
.unwrap_or(false);
let handler = overseer
.get("handler")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
println!(
"overseer: {} (handler: {handler})",
if enabled { "enabled" } else { "disabled" }
);
}
}
Ok(())
}
pub(crate) async fn optimizer(
client: &reqwest::Client,
url: &str,
action: OptimizerAction,
) -> anyhow::Result<()> {
match action {
OptimizerAction::Status => {
let body: serde_json::Value = client
.get(format!("{url}/optimizer"))
.send()
.await?
.json()
.await?;
println!("{}", serde_json::to_string_pretty(&body["optimizer"])?);
}
OptimizerAction::Set { level } => {
let level_name = match level {
CliCompressionLevel::Off => "Off",
CliCompressionLevel::Trim => "Trim",
CliCompressionLevel::Summarise => "Summarise",
CliCompressionLevel::Caveman => "Caveman",
};
let paths = trusty_mpm::core::paths::FrameworkPaths::default();
let path = paths.optimizer_config();
std::fs::create_dir_all(&paths.hooks)?;
let contents = format!(
"# trusty-mpm token optimizer — framework hook configuration\n\
# Edited by: trusty-mpm optimizer set\n\n\
[default]\nlevel = \"{level_name}\"\n\n\
[tools]\n"
);
std::fs::write(&path, contents)?;
println!("optimizer level set to {level_name} ({})", path.display());
}
}
Ok(())
}
pub(crate) async fn attach_cmd(
client: &reqwest::Client,
url: &str,
target: &str,
json: bool,
) -> anyhow::Result<()> {
use trusty_mpm::core::{ResolveResult, SessionSummary, resolve_target};
let resp: serde_json::Value = client
.get(format!("{url}/sessions"))
.send()
.await?
.json()
.await?;
let empty = vec![];
let raw = resp
.get("sessions")
.and_then(|v| v.as_array())
.unwrap_or(&empty);
let sessions: Vec<SessionSummary> = raw
.iter()
.filter_map(|v| {
Some(SessionSummary {
id: v["id"].as_str()?.to_string(),
name: v["tmux_name"].as_str().map(str::to_string),
workdir: v["workdir"].as_str().unwrap_or("").to_string(),
last_active: v["last_seen"]["secs_since_epoch"].as_u64().unwrap_or(0),
})
})
.collect();
match resolve_target(target, &sessions) {
ResolveResult::Found(id) => {
if json {
if let Some(s) = raw.iter().find(|v| v["id"].as_str() == Some(&id)) {
println!("{}", serde_json::to_string_pretty(s)?);
}
return Ok(());
}
let resolved_url = trusty_mpm::core::resolve_daemon_url(Some(url));
trusty_mpm::tui::run_focused(resolved_url, 1000, Some(id)).await
}
ResolveResult::Ambiguous(ids) => {
eprintln!(
"Ambiguous target '{target}' — matched {} sessions:",
ids.len()
);
for id in &ids {
eprintln!(" {id}");
}
std::process::exit(1);
}
ResolveResult::NotFound => {
eprintln!("No session matched '{target}'.");
if !sessions.is_empty() {
eprintln!("Available sessions:");
for s in &sessions {
let name = s.name.as_deref().unwrap_or("-");
eprintln!(" {} ({}) {}", s.id, name, s.workdir);
}
}
std::process::exit(1);
}
}
}