//! `spectra mcp --auto` (spec ยง15.1): one MCP entry for every folder. The //! shim finds the agent for the folder its client started in, from the //! agent homes under `$SPECTRA_AGENTS` (else `~/.spectra-agents`), each of //! which `spectra agent add` labels with an `agent.toml` naming the //! agent, its kind, its project and the project's folder. Each folder //! still signs as its own agent. A folder with no agent gets a quiet //! server that says how to add one, instead of a failure. use anyhow::{Context, Result}; use serde_json::{json, Value as Json}; use spectra_core::keys::KeyPair; use spectra_mcp::client::Client; use std::path::{Path, PathBuf}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; /// What `agent add` writes into an agent's home. #[derive(Clone, Debug)] pub struct Label { pub name: String, pub kind: String, pub folder: Option, pub home: PathBuf, } pub fn agents_dir() -> PathBuf { if let Some(d) = std::env::var_os("SPECTRA_AGENTS").filter(|d| !d.is_empty()) { return PathBuf::from(d); } std::env::var_os("HOME").map(PathBuf::from).unwrap_or_default().join(".spectra-agents") } pub fn write_label(home: &Path, name: &str, kind: &str, project: &str, folder: Option<&str>) -> Result<()> { let mut t = toml::Table::new(); t.insert("name".into(), name.into()); t.insert("kind".into(), kind.into()); t.insert("project".into(), project.into()); if let Some(f) = folder.filter(|f| !f.is_empty()) { t.insert("folder".into(), f.into()); } let text = format!("# Written by `spectra agent add`: lets `spectra mcp --auto` find this agent.\n{t}"); spectra_core::durable::write_atomic(&home.join("agent.toml"), text.as_bytes())?; Ok(()) } fn read_label(home: &Path) -> Option