magi-code 0.101.0

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

pub(super) fn bash_card(input: &ToolCardInput<'_>) -> CardDraft {
    let detail = input.detail;
    let mut status = input_status(input);
    if status == CardStatus::Failed && tool_was_canceled(detail) {
        status = CardStatus::Canceled;
    }
    let timed_out = detail
        .and_then(|detail| detail.metadata.get("timed_out"))
        .and_then(serde_json::Value::as_bool)
        .unwrap_or(false);
    let exit_code = detail
        .and_then(|detail| detail.metadata.get("exit_code"))
        .and_then(serde_json::Value::as_i64);
    let command = input
        .bash_command
        .map(sanitize_display_text)
        .unwrap_or_default();
    let mut title = bash_title(status, timed_out, exit_code);
    if detail.is_some_and(|detail| {
        detail
            .params
            .get("background")
            .and_then(serde_json::Value::as_bool)
            == Some(true)
            || detail
                .metadata
                .get("job_id")
                .is_some_and(serde_json::Value::is_string)
    }) {
        title.push_str(" • background");
    }
    let intent = detail
        .and_then(|detail| detail.params.get(crate::tool_contract::arg_key::INTENT))
        .and_then(serde_json::Value::as_str)
        .map(str::trim)
        .map(sanitized_single_line)
        .filter(|intent| !intent.is_empty());
    let mut body_rows = intent.iter().cloned().collect::<Vec<_>>();
    if !command.is_empty() {
        body_rows.push(command);
    }
    append_job_status(&mut body_rows, detail);
    let mut display_lines = Vec::new();
    let notices = super::super::bash_notices::bash_execution_notices(detail);
    body_rows.extend(notices.iter().map(|notice| {
        format!(
            "{} • {}",
            notice.label,
            notice.value.as_deref().unwrap_or("not reported")
        )
    }));
    let status_row = match status {
        CardStatus::Canceled => Some("canceled"),
        CardStatus::Failed if notices.iter().all(|notice| notice.value.is_none()) => {
            Some("command failed")
        }
        _ => None,
    };
    if let Some(status_row) = status_row {
        display_lines.push(TextLine::plain(status_row));
        body_rows.push(status_row.to_owned());
    }
    CardDraft {
        role: ToolCardRole::Bash,
        status,
        title,
        metadata: bash_jev_metadata(detail),
        body: body_rows.join("\n"),
        body_lines: display_lines,
        activity_rows: Vec::new(),
    }
}

fn bash_title(status: CardStatus, timed_out: bool, exit_code: Option<i64>) -> String {
    if timed_out {
        return "Bash • timed out".to_string();
    }
    if matches!(status, CardStatus::Success | CardStatus::Failed)
        && let Some(exit_code) = exit_code
    {
        format!("Bash • exit {exit_code}")
    } else if status == CardStatus::Canceled {
        "Bash • canceled".to_string()
    } else {
        "Bash".to_string()
    }
}

fn append_job_status(rows: &mut Vec<String>, detail: Option<&ToolActivityDetail>) {
    let Some(detail) = detail else {
        return;
    };
    for key in [
        "job_id",
        "status",
        "exit_code",
        "stdout_bytes_dropped",
        "stderr_bytes_dropped",
        "cleanup_warning",
    ] {
        if let Some(value) = detail.metadata.get(key).filter(|value| !value.is_null()) {
            rows.push(sanitized_single_line(&format!("{key}: {value}")));
        }
    }
}

pub(super) fn job_card(input: &ToolCardInput<'_>) -> CardDraft {
    let mut rows = Vec::new();
    if let Some(action) = input
        .detail
        .and_then(|detail| detail.params.get("action"))
        .and_then(serde_json::Value::as_str)
    {
        rows.push(sanitized_single_line(action));
    }
    append_job_status(&mut rows, input.detail);
    let mut card = basic_card(ToolCardRole::Bash, "Bash job", &rows.join("\n"));
    card.status = input_status(input);
    card
}