magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::{MissionControlApp, actions::ActionContext, display_reducer};
use crate::tui::{input, state};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

pub(super) fn skill_candidates(
    candidates: &[state::AutocompleteCandidate],
) -> Vec<state::AutocompleteCandidate> {
    candidates
        .iter()
        .filter(|candidate| candidate.kind == state::AutocompleteKind::SkillTag)
        .cloned()
        .collect()
}

impl MissionControlApp {
    pub(super) fn edit_subagent_prompt(
        &mut self,
        key: KeyEvent,
        viewport: input::PromptViewport,
        ui_state: &mut state::MissionControlState,
        context: &mut ActionContext<'_>,
    ) {
        let Some(control) = ui_state
            .selected_subagent_control()
            .filter(|control| control.is_active())
        else {
            return;
        };
        let candidates = skill_candidates(context.autocomplete_candidates);
        let copy = ui_state
            .with_selected_subagent_prompt(|state| {
                if key.code == KeyCode::Enter
                    && key.modifiers == KeyModifiers::NONE
                    && !state.autocomplete_visible()
                {
                    submit_steering(state, &control.steering);
                    return None;
                }
                edit_with_shared_prompt_keys(key, viewport, state, &candidates)
            })
            .flatten();
        if let Some(text) = copy {
            Self::apply_copy_request(
                &text,
                ui_state,
                context.clipboard,
                std::time::Instant::now(),
            );
        }
    }
}

fn edit_with_shared_prompt_keys(
    key: KeyEvent,
    viewport: input::PromptViewport,
    state: &mut state::MissionControlState,
    candidates: &[state::AutocompleteCandidate],
) -> Option<String> {
    let viewer = state.modals.subagent_viewer.take();
    let command = input::classify_key_with_all_viewports(
        key,
        state,
        input::KeyViewports {
            prompt: Some(viewport),
            ..Default::default()
        },
        candidates,
    );
    state.modals.subagent_viewer = viewer;
    let (action, display) = command.into_parts();
    if let Some(display) = display {
        apply_editor_display(display, state, candidates);
    }
    match action {
        input::InputAction::CopySelectedPrompt(text) => Some(text),
        _ => None,
    }
}

// A positive allow-list prevents primary navigation, command dispatch, history,
// shutdown, or bash-mode submission from escaping the shared key classifier.
fn apply_editor_display(
    command: input::DisplayCommand,
    state: &mut state::MissionControlState,
    candidates: &[state::AutocompleteCandidate],
) {
    match command {
        input::DisplayCommand::Batch(commands) => {
            for command in commands {
                apply_editor_display(command, state, candidates);
            }
        }
        input::DisplayCommand::Prompt(input::PromptCommand::TakeForSubmit) => {}
        command @ (input::DisplayCommand::Prompt(_)
        | input::DisplayCommand::RefreshAutocomplete) => {
            display_reducer::apply_display_command(command, state, candidates);
        }
        _ => {}
    }
}

fn submit_steering(
    state: &mut state::MissionControlState,
    steering: &crate::agent::steering::AgentSteering,
) {
    use crate::agent::steering::SteeringRejected;
    let prompt = state.prompt_plain_text();
    if prompt.trim_start().starts_with('/') {
        state.status = "Slash commands are not available for subagents".into();
        return;
    }
    if prompt.trim().is_empty() {
        return;
    }
    // A leading ! is ordinary steering text, never a shell invocation.
    match steering.try_enqueue_nonblocking(prompt) {
        Ok(_) => {
            state.clear_prompt_input();
            state.hide_autocomplete();
            state.status = "Subagent steering queued".into();
        }
        Err(SteeringRejected::Full { capacity }) => {
            state.status = format!("Steering queue full ({capacity} max)");
        }
        Err(SteeringRejected::Closed) => {
            state.status = "Subagent no longer accepts steering".into();
        }
        Err(SteeringRejected::Busy) => {
            state.status = "Steering is being saved; try again".into();
        }
        Err(SteeringRejected::Empty) => {}
    }
}

pub(super) fn cancel_or_recall_selected(state: &mut state::MissionControlState) {
    let Some(control) = state.selected_subagent_control() else {
        return;
    };
    if !recall_selected_steering(state, &control.steering) && control.is_active() {
        control.cancel();
        state.status = "Subagent cancellation requested".into();
    }
    state.set_subagent_prompt_notice(state.status.clone());
}

fn recall_selected_steering(
    state: &mut state::MissionControlState,
    steering: &crate::agent::steering::AgentSteering,
) -> bool {
    let mut recall_rejected = false;
    let recalled = steering.restore_pending_messages(|messages| {
        let restored = state
            .with_selected_subagent_prompt(|state| {
                let draft = state.prompt_plain_text();
                let text = if draft.is_empty() {
                    messages.to_owned()
                } else {
                    format!("{messages}\n{draft}")
                };
                let restored = matches!(
                    state.set_prompt_text(&text, text.len()),
                    state::PromptEditResult::Changed | state::PromptEditResult::Unchanged
                );
                if restored {
                    state.hide_autocomplete();
                }
                restored
            })
            .unwrap_or(false);
        recall_rejected = !restored;
        restored
    });
    state.status = match recalled {
        Ok(_) if recall_rejected => "Subagent draft is too large to recall steering".into(),
        Ok(true) => "Queued steering restored to subagent prompt".into(),
        Ok(false) => return false,
        Err(()) => "Steering is being injected; try Alt-C again".into(),
    };
    true
}