magi-code 0.104.0

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

pub(super) fn find_card(input: &ToolCardInput<'_>) -> CardDraft {
    const DEFAULT_LIMIT: usize = 50;
    let detail = input.detail;
    let status = input_status(input);
    let query = find_metadata_str(detail, "query")
        .or_else(|| find_param(detail, "query"))
        .unwrap_or("");
    let path = find_param(detail, "path").unwrap_or(".");
    let kind = find_param(detail, "kind").unwrap_or("files");
    let offset = find_param_usize(detail, "offset").unwrap_or(0);
    let limit = find_param_usize(detail, "limit").unwrap_or(DEFAULT_LIMIT);
    let returned = find_usize(detail, "matches_returned");
    let total = find_usize(detail, "total_matched");
    let incomplete = find_bool(detail, "truncated")
        || offset != 0
        || detail.is_some_and(|detail| {
            detail.metadata["scan_complete"] == false || detail.metadata["timed_out"] == true
        })
        || returned
            .zip(total)
            .is_some_and(|(returned, total)| returned != total);
    let title = match status {
        CardStatus::Success => returned.map_or_else(
            || "Find".to_string(),
            |returned| {
                format!(
                    "Find • {returned} {}{}",
                    plural(returned, "path", "paths"),
                    if incomplete { " returned" } else { "" }
                )
            },
        ),
        CardStatus::Canceled => "Find • canceled".to_string(),
        _ => "Find".to_string(),
    };
    let mut lines = vec![
        find_quoted(query),
        format!("{} in {}", find_kind(kind), sanitized_single_line(path)),
    ];
    if offset != 0 || limit != DEFAULT_LIMIT {
        lines.push(format!("offset {offset} · limit {limit}"));
    }
    match status {
        CardStatus::Running => {}
        CardStatus::Success => {
            let paths = detail
                .map(|detail| find_output_paths(detail.output.as_ref()))
                .unwrap_or_default();
            lines.extend(paths.iter().take(TOOL_PREVIEW_LINES).cloned());
            if paths.len() > TOOL_PREVIEW_LINES {
                lines.push(format!("… preview limited to {TOOL_PREVIEW_LINES} paths"));
            }
            if incomplete {
                if let Some((total, returned)) = total.zip(returned) {
                    if !detail.is_some_and(|detail| detail.metadata["total_matched_exact"] == true)
                    {
                        lines.push(format!(
                            "… {returned} returned; {total} seen; full total unknown"
                        ));
                    } else if returned == 0 || offset >= total {
                        lines.push(format!("… showing 0 of {total} paths"));
                    } else {
                        let start = offset.saturating_add(1);
                        let end = offset.saturating_add(returned).min(total);
                        lines.push(format!("… showing {start}–{end} of {total} paths"));
                    }
                } else {
                    lines.push("… more paths may exist".to_string());
                }
            }
            if detail
                .and_then(|detail| detail.metadata.get("index_ready"))
                .and_then(serde_json::Value::as_bool)
                == Some(false)
            {
                lines.push("… index not ready; results may be incomplete".to_string());
            }
        }
        CardStatus::Failed => lines.push(
            detail
                .and_then(|detail| find_first_non_empty_line(detail.output.as_ref()))
                .or_else(|| find_first_non_empty_line(input.preview))
                .unwrap_or_else(|| "find failed".to_string()),
        ),
        CardStatus::Canceled => lines.push("canceled".to_string()),
        CardStatus::None => {}
    }
    CardDraft {
        role: ToolCardRole::Find,
        status,
        title,
        metadata: Vec::new(),
        body: lines.join("\n"),
        body_lines: Vec::new(),
        activity_rows: Vec::new(),
    }
}

fn find_param<'a>(
    detail: Option<&'a crate::output::ToolActivityDetail>,
    key: &str,
) -> Option<&'a str> {
    detail?
        .params
        .get(key)?
        .as_str()
        .filter(|value| !value.is_empty())
}
fn find_metadata_str<'a>(
    detail: Option<&'a crate::output::ToolActivityDetail>,
    key: &str,
) -> Option<&'a str> {
    detail?
        .metadata
        .get(key)?
        .as_str()
        .filter(|value| !value.is_empty())
}

fn find_param_usize(
    detail: Option<&crate::output::ToolActivityDetail>,
    key: &str,
) -> Option<usize> {
    detail
        .and_then(|detail| detail.params.get(key))
        .and_then(serde_json::Value::as_u64)
        .and_then(|value| usize::try_from(value).ok())
        .or_else(|| find_usize(detail, key))
}
fn find_usize(detail: Option<&crate::output::ToolActivityDetail>, key: &str) -> Option<usize> {
    detail?
        .metadata
        .get(key)?
        .as_u64()
        .and_then(|value| usize::try_from(value).ok())
}
fn find_bool(detail: Option<&crate::output::ToolActivityDetail>, key: &str) -> bool {
    detail
        .and_then(|detail| detail.metadata.get(key))
        .and_then(serde_json::Value::as_bool)
        .unwrap_or(false)
}
fn find_kind(kind: &str) -> &'static str {
    match kind {
        "directories" | "dirs" => "directories",
        "mixed" | "both" | "files_and_directories" => "files and directories",
        _ => "files",
    }
}
fn find_quoted(query: &str) -> String {
    serde_json::to_string(&sanitize_display_text(query))
        .unwrap_or_else(|_| "\"find query\"".to_string())
}
fn find_output_paths(output: &str) -> Vec<String> {
    output
        .lines()
        .filter(|line| !line.is_empty())
        .map(|line| {
            let safe = sanitized_single_line(line);
            truncate_preview_line(&safe, PREVIEW_LINE_MAX_CHARS)
        })
        .collect()
}

fn find_first_non_empty_line(text: &str) -> Option<String> {
    sanitize_display_text(text)
        .lines()
        .map(str::trim)
        .find(|line| !line.is_empty())
        .map(normalize_inline_separators)
}