illumitech-forge-cli 1.6.0

Evidence-backed agent execution CLI
Documentation
//! Display-only projection of native activities. No tool output grants acceptance.
use serde_json::Value;
use std::collections::{BTreeMap, VecDeque};

fn text(v: &Value) -> &str {
    v.as_str().unwrap_or("")
}
fn bounded(s: &str) -> String {
    if s.len() <= 24000 {
        return s.into();
    }
    let end = s.floor_char_boundary(24000);
    format!("{}\n… Exibição limitada a 24000 bytes.", &s[..end])
}
pub(super) fn items(events: &VecDeque<Value>) -> BTreeMap<String, Value> {
    let mut items = BTreeMap::<String, Value>::new();
    for event in events {
        let p = &event["params"];
        let method = text(&event["method"]);
        if matches!(method, "item/started" | "item/completed") {
            let id = text(&p["item"]["id"]);
            if id.is_empty() {
                continue;
            }
            let mut value = items.remove(id).unwrap_or_else(|| serde_json::json!({}));
            if let Some(new) = p["item"].as_object() {
                for (key, v) in new {
                    value[key] = v.clone();
                }
            }
            if value["status"].is_null() && method == "item/started" {
                value["status"] = "inProgress".into();
            } else if method == "item/completed" && p["item"]["status"].is_null() {
                value["status"] = Value::Null;
            }
            items.insert(id.into(), value);
        } else if matches!(
            method,
            "item/commandExecution/outputDelta"
                | "item/fileChange/outputDelta"
                | "item/mcpToolCall/progress"
        ) {
            let id = text(&p["itemId"]);
            if id.is_empty() {
                continue;
            }
            let kind = if method.contains("commandExecution") {
                "commandExecution"
            } else if method.contains("fileChange") {
                "fileChange"
            } else {
                "mcpToolCall"
            };
            let item = items
                .entry(id.into())
                .or_insert_with(|| serde_json::json!({"id":id,"type":kind,"status":"inProgress"}));
            let (field, delta) = if kind == "mcpToolCall" {
                ("progress", text(&p["message"]))
            } else {
                ("aggregatedOutput", text(&p["delta"]))
            };
            item[field] = bounded(&format!("{}{delta}", text(&item[field]))).into();
        }
    }
    items
}
pub(super) fn counts(diff: &str) -> Option<(usize, usize)> {
    let (mut hunk, mut observed, mut add, mut remove) = (false, false, 0, 0);
    for line in diff.lines() {
        if line.starts_with("diff ") || line.starts_with("*** ") {
            hunk = false;
        }
        if line.starts_with("@@ ") {
            hunk = true;
            observed = true;
        } else if hunk && line.starts_with('+') {
            add += 1;
        } else if hunk && line.starts_with('-') {
            remove += 1;
        }
    }
    observed.then_some((add, remove))
}
pub(super) fn entry(item: &Value) -> String {
    let mut sections = Vec::new();
    let mut add = |label: &str, value: &Value| {
        if !value.is_null() {
            let value = value
                .as_str()
                .map(str::to_owned)
                .unwrap_or_else(|| serde_json::to_string_pretty(value).unwrap_or_default());
            if !value.is_empty() {
                sections.push(format!("{label}\n{}", bounded(&value)));
            }
        }
    };
    let title = match text(&item["type"]) {
        "mcpToolCall" | "dynamicToolCall" => {
            add("Argumentos", &item["arguments"]);
            add("Resultado", &item["result"]);
            add("Erro", &item["error"]);
            add("Progresso", &item["progress"]);
            add("Conteúdo retornado", &item["contentItems"]);
            let name = [text(&item["server"]), text(&item["tool"])]
                .into_iter()
                .filter(|s| !s.is_empty())
                .collect::<Vec<_>>()
                .join(".");
            format!(
                "Chamada · {}",
                if name.is_empty() { "Ferramenta" } else { &name }
            )
        }
        "commandExecution" => {
            add("Comando", &item["command"]);
            add("Diretório", &item["cwd"]);
            add("Código de saída", &item["exitCode"]);
            add("Saída", &item["aggregatedOutput"]);
            format!(
                "Comando · {}",
                bounded(
                    text(&item["command"])
                        .lines()
                        .next()
                        .unwrap_or("Execução nativa")
                )
            )
        }
        "fileChange" => {
            let files = item["changes"]
                .as_array()
                .map(Vec::as_slice)
                .unwrap_or_default()
                .iter()
                .filter(|f| f.is_object())
                .collect::<Vec<_>>();
            let stats = files
                .iter()
                .map(|f| counts(text(&f["diff"])))
                .collect::<Option<Vec<_>>>();
            let count = stats
                .filter(|_| !files.is_empty())
                .map(|s| s.into_iter().fold((0, 0), |a, b| (a.0 + b.0, a.1 + b.1)));
            for file in &files {
                let suffix = counts(text(&file["diff"]))
                    .map(|(a, r)| format!(" (+{a} −{r})"))
                    .unwrap_or_default();
                add(&format!("{}{suffix}", text(&file["path"])), &file["diff"]);
            }
            add("Saída", &item["aggregatedOutput"]);
            let suffix = count
                .map(|(a, r)| format!(" (+{a} −{r}; hunks recebidos)"))
                .unwrap_or_default();
            let total = item["forgeOriginalFileCount"]
                .as_u64()
                .filter(|n| *n > files.len() as u64)
                .map(|n| format!("{} de {n}", files.len()))
                .unwrap_or_else(|| files.len().to_string());
            format!("Alterações · {total} arquivo(s){suffix}")
        }
        "webSearch" => {
            add("Consulta", &item["query"]);
            add("Ação", &item["action"]);
            "Pesquisa".into()
        }
        _ => {
            add("Dados informados pelo cliente", item);
            "Atividade do cliente".into()
        }
    };
    let status = if !item["error"].is_null()
        || item["result"]["isError"] == true
        || item["success"] == false
        || item["exitCode"].as_i64().is_some_and(|c| c != 0)
    {
        "failed"
    } else {
        text(&item["status"])
    };
    let truncated = item["forgeDisplayTruncated"] == true;
    format!(
        "{}{}|{status}\n{}{}",
        title
            .chars()
            .take(180)
            .collect::<String>()
            .replace('|', "¦")
            .replace('\n', " "),
        if truncated { " · parcial" } else { "" },
        if truncated {
            "Exibição parcial; conteúdo e contagens podem estar incompletos.\n"
        } else {
            ""
        },
        sections.join("\n\n")
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;
    #[test]
    fn only_received_hunks_count() {
        assert_eq!(
            counts("--- a\n+++ b\n@@ -1 +1,2 @@\n-old\n+new\n+++ literal\n"),
            Some((2, 1))
        );
        assert_eq!(counts("+++ b\n-new\n+new"), None);
    }
    #[test]
    fn mcp_payload_and_error_are_preserved() {
        let value = entry(
            &json!({"type":"mcpToolCall","server":"logs","tool":"query","arguments":{"limit":4},"result":{"isError":true,"content":[{"text":"falha"}]},"status":"completed"}),
        );
        assert!(value.starts_with("Chamada · logs.query|failed"));
        assert!(value.contains("Argumentos\n"));
        assert!(value.contains("Resultado\n"));
    }
}