magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::super::*;
use super::MissionControlApp;

struct SystemPromptWorker {
    generation: u64,
    handle: super::completion_worker::CompletionWorker<state::SystemPromptModalState>,
}

#[derive(Default)]
pub(super) struct SystemPromptRuntime {
    worker: Option<SystemPromptWorker>,
}

impl SystemPromptRuntime {
    pub(super) fn is_pending(&self) -> bool {
        self.worker.is_some()
    }

    pub(super) fn cleanup(&mut self) -> Vec<String> {
        let Some(worker) = self.worker.take() else {
            return Vec::new();
        };
        match worker.handle.join_with_timeout(WORKER_EXIT_JOIN_TIMEOUT) {
            None => vec!["system prompt preparation did not finish before shutdown".into()],
            Some(result) => result.err().into_iter().collect(),
        }
    }
}

impl MissionControlApp {
    pub(super) fn start_system_prompt_preparation(&mut self, ui: &mut state::MissionControlState) {
        if self.system_prompt.is_pending() {
            ui.status = "system prompt preparation already in progress".into();
            return;
        }
        let mut run_config = self
            .state
            .config
            .clone()
            .unwrap_or_else(|| self.config.clone());
        run_config.model = Some(self.state.model.clone());
        let disabled_tools = self.disabled_tools.clone();
        let code_mode_setting_enabled = self.settings.tools.code_mode.enabled;
        let code_mode_subagents_enabled = code_mode_setting_enabled
            && !self
                .settings
                .tools
                .code_mode
                .disabled
                .iter()
                .any(|name| name == "subagents");
        let disabled_profiles = self.disabled_subagent_profiles.clone();
        let discovery = self.subagent_profile_discovery.clone();
        let instructions = self.instructions.clone();
        let skills = self.skills.clone();
        let primary_agent = ui.selected_primary_agent_profile();
        let handle = super::completion_worker::CompletionWorker::spawn(
            "magi-system-prompt",
            self.events.clone(),
            move || {
                let disabled_tools = disabled_tools
                    .lock()
                    .map(|disabled| disabled.clone())
                    .unwrap_or_else(|_| {
                        crate::tools::MVP_TOOL_CAPABILITIES
                            .iter()
                            .map(|tool| tool.canonical_name().to_string())
                            .collect()
                    });
                let disabled_profiles =
                    disabled_profiles
                        .lock()
                        .map(|disabled| disabled.clone())
                        .map_err(|_| "disabled subagent profiles lock poisoned".to_string())?;
                let discovery = crate::subagents::profiles::filter_enabled_profiles(
                    &discovery,
                    &disabled_profiles,
                );
                let section = if disabled_tools
                    .contains(crate::tools::contract::tool_name::SUBAGENTS)
                    && !code_mode_subagents_enabled
                {
                    None
                } else {
                    crate::subagents::profiles::render_subagent_profiles_prompt(
                        Some(&run_config.paths.prompts),
                        &discovery,
                    )
                    .map_err(|error| error.to_string())?
                };
                let mut sections =
                    crate::agent::build_system_prompt_sections_with_prompt_dir_and_subagents(
                        Some(&run_config.paths.prompts),
                        &instructions,
                        &skills,
                        section.as_deref(),
                        code_mode_setting_enabled
                            && !disabled_tools.contains(crate::code_mode::TOOL_NAME),
                    )
                    .map_err(|error| error.to_string())?;
                if let Some(profile) = primary_agent.as_ref() {
                    sections.push(crate::agent::SystemPromptSection {
                        title: format!("Primary agent · {}", profile.name),
                        content: format!(
                            "\n\n{}",
                            crate::primary_agents::render_primary_agent_prompt_append(profile)
                                .trim()
                        ),
                    });
                }
                let prompt = sections
                    .iter()
                    .map(|section| section.content.as_str())
                    .collect::<String>();
                let provider_id = run_config.provider_id();
                let model = run_config
                    .model
                    .as_deref()
                    .unwrap_or_else(|| crate::providers::default_model_for_provider(provider_id));
                let count = crate::context::project_text_tokens(provider_id, model, &prompt);
                let sections = sections
                    .into_iter()
                    .map(|section| {
                        let tokens = crate::context::project_text_tokens(
                            provider_id,
                            model,
                            &section.content,
                        )
                        .tokens;
                        state::SystemPromptCard {
                            title: section.title,
                            content: section.content,
                            tokens,
                        }
                    })
                    .collect::<Vec<_>>();
                let max_tokens = crate::config::load_context_budget(&run_config)
                    .unwrap_or_default()
                    .max_tokens;
                let percent = count.tokens.saturating_mul(100) / max_tokens.max(1);
                Ok(state::SystemPromptModalState {
                    content: format!(
                        "~{} tokens / {} max ({}%) · {} sections\nEstimates include prompt wrappers. Section estimates may not sum to the total.",
                        count.tokens,
                        max_tokens,
                        percent,
                        sections.len()
                    ),
                    sections,
                })
            },
        );
        match handle {
            Ok(handle) => {
                self.system_prompt.worker = Some(SystemPromptWorker {
                    generation: self.session_generation,
                    handle,
                });
                ui.open_system_prompt_modal("Preparing system prompt…".into());
                ui.status = "preparing system prompt…".into();
            }
            Err(error) => ui.status = format!("failed to start system prompt preparation: {error}"),
        }
    }

    pub(in crate::tui) fn reconcile_system_prompt_worker(
        &mut self,
        ui: &mut state::MissionControlState,
    ) -> bool {
        let Some(worker) = self.system_prompt.worker.as_mut() else {
            return false;
        };
        let result = worker.handle.take_result();
        let generation = worker.generation;
        if worker.handle.ready_to_reap()
            && let Some(worker) = self.system_prompt.worker.take()
        {
            let _ = worker.handle.join();
        }
        let Some(result) = result else {
            return false;
        };
        // A closed modal or changed session must not be revived by a late result.
        if generation != self.session_generation || !ui.system_prompt_modal_visible() {
            return false;
        }
        match result {
            Ok(prompt) => {
                ui.show_system_prompt_sections(prompt);
                ui.status = "showing current computed system prompt".into();
            }
            Err(error) => {
                ui.open_system_prompt_modal(format!("Failed to compute system prompt: {error}"));
                ui.status = "failed to compute system prompt".into();
            }
        }
        true
    }
}