magi-code 0.77.1

Repository-aware CLI coding agent for terminal work
Documentation
use crate::{
    config::{CliConfigOverrides, EffectiveConfig, McPaths, Settings},
    instructions::{InstructionFile, discover_agents_with_additional_markdown},
    mcp::manager::McpManager,
    sessions::SessionManager,
    skills::{SkillDiscovery, SkillRoots, discover_skills, filter_enabled_skills},
};
use anyhow::Result;
use std::{
    env,
    path::PathBuf,
    sync::{Arc, Mutex},
};

#[derive(Clone)]
pub(super) struct ServiceRuntime {
    pub(super) config: EffectiveConfig,
    pub(super) settings: Settings,
    pub(super) instructions: Vec<InstructionFile>,
    pub(super) skills: SkillDiscovery,
    pub(super) session_manager: SessionManager,
    pub(super) cwd: PathBuf,
    pub(super) mcp: Option<Arc<Mutex<McpManager>>>,
}

impl ServiceRuntime {
    pub(super) fn load() -> Result<Self> {
        Self::load_for_profile(false)
    }

    pub(super) fn load_persistent() -> Result<Self> {
        Self::load_for_profile(true)
    }

    /// Load adapter-owned paths without consulting cwd or MC_HOME for their values.
    #[cfg(any(unix, test))]
    pub(super) fn load_unix(workspace: PathBuf, state_root: PathBuf) -> Result<Self> {
        anyhow::ensure!(
            workspace.is_absolute() && state_root.is_absolute(),
            "absolute service paths required"
        );
        let cwd = workspace.canonicalize()?;
        anyhow::ensure!(cwd.is_dir(), "workspace must be a directory");
        let paths = McPaths::from_root_and_project_dir(state_root, cwd.clone());
        paths.ensure_runtime_dirs()?;
        let paths = McPaths::from_root_and_project_dir(paths.root.canonicalize()?, cwd.clone());
        Self::load_with_paths(cwd, paths)
    }

    fn load_for_profile(canonical_paths: bool) -> Result<Self> {
        let cwd = env::current_dir()?;
        let paths = McPaths::resolve()?;
        paths.ensure_runtime_dirs()?;
        let (cwd, paths) = if canonical_paths {
            let cwd = cwd.canonicalize()?;
            let paths = McPaths::from_root_and_project_dir(paths.root.canonicalize()?, cwd.clone());
            (cwd, paths)
        } else {
            (cwd, paths)
        };
        Self::load_with_paths(cwd, paths)
    }

    fn load_with_paths(cwd: PathBuf, paths: McPaths) -> Result<Self> {
        let (config, settings, _, _) = crate::config::load_startup_config_with_settings(
            paths.clone(),
            CliConfigOverrides::default(),
        )?;
        let instructions = discover_agents_with_additional_markdown(
            &paths.user_agents,
            &cwd,
            &settings.instructions.additional_markdown_paths,
        )?;
        let mut skill_roots = SkillRoots::from_paths(&paths.root, &cwd);
        skill_roots.additional_paths = settings.skills.additional_paths.clone();
        let discovered_skills = discover_skills(&skill_roots);
        let disabled_skills = crate::config::disabled_skill_names_from_settings(&settings);
        let skills = filter_enabled_skills(&discovered_skills, &disabled_skills);
        let mcp = (!settings.mcp_servers.is_empty()).then(|| {
            Arc::new(Mutex::new(McpManager::from_settings_with_paths(
                &settings.mcp_servers,
                Some(&paths.root),
            )))
        });
        Ok(Self {
            config,
            settings,
            instructions,
            skills,
            session_manager: SessionManager::new(paths.sessions.clone()),
            cwd,
            mcp,
        })
    }

    pub(super) fn current_provider_auth_ready(&self) -> Result<bool> {
        self.reloaded()?.config.current_store_provider_auth_ready()
    }

    pub(super) fn reloaded(&self) -> Result<Self> {
        self.with_frontend_settings(crate::config::read_settings(&self.config.paths)?)
    }

    pub(super) fn capture_turn_settings(&self) -> Result<Self> {
        self.with_frontend_settings(crate::config::read_settings_locked(&self.config.paths)?)
    }

    fn with_frontend_settings(&self, settings: Settings) -> Result<Self> {
        let mut runtime = self.clone();
        // Provider definitions, instructions and tools remain the startup snapshot.
        runtime.settings.selected_model = settings.selected_model;
        runtime.settings.fast = settings.fast;
        runtime.config = EffectiveConfig::from_loaded_settings(
            self.config.paths.clone(),
            CliConfigOverrides::default(),
            runtime.settings.clone(),
        )?;
        Ok(runtime)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn explicit_runtime_paths_are_canonical_and_do_not_change_process_context() {
        let temp = tempfile::tempdir().unwrap();
        let cwd = env::current_dir().unwrap();
        let home = env::var_os("MC_HOME");
        let workspace = temp.path().join("workspace");
        std::fs::create_dir(&workspace).unwrap();
        let root = temp.path().join("state");
        let runtime = ServiceRuntime::load_unix(workspace.join("."), root.clone()).unwrap();
        assert_eq!(runtime.cwd, workspace.canonicalize().unwrap());
        assert_eq!(runtime.config.paths.root, root.canonicalize().unwrap());
        assert_eq!(env::current_dir().unwrap(), cwd);
        assert_eq!(env::var_os("MC_HOME"), home);
        assert!(ServiceRuntime::load_unix(PathBuf::from("relative"), root).is_err());
    }
}