ccc 0.2.0

Unified Rust library and CLI for invoking coding-agent CLIs with shared parsing, planning, and transcript utilities
Documentation
use super::model::{Event, ToolCall, ToolResult, Transcript, Usage};
use crate::invoke::RunnerKind;

pub fn parse_transcript(raw: &str, schema: &str) -> Transcript {
    transcript_from_parsed(crate::json_output::parse_json_output(raw, schema))
}

pub fn parse_transcript_for_runner(raw: &str, runner: RunnerKind) -> Option<Transcript> {
    schema_name_for_runner(runner).map(|schema| parse_transcript(raw, schema))
}

pub fn schema_name_for_runner(runner: RunnerKind) -> Option<&'static str> {
    match runner {
        RunnerKind::OpenCode => Some("opencode"),
        RunnerKind::Claude => Some("claude-code"),
        RunnerKind::Codex => Some("codex"),
        RunnerKind::Kimi => Some("kimi"),
        RunnerKind::Cursor => Some("cursor-agent"),
        RunnerKind::Gemini => Some("gemini"),
        RunnerKind::RooCode | RunnerKind::Crush => None,
    }
}

pub fn transcript_from_parsed(parsed: crate::json_output::ParsedJsonOutput) -> Transcript {
    Transcript {
        events: parsed.events.into_iter().map(convert_event).collect(),
        final_text: parsed.final_text,
        session_id: (!parsed.session_id.is_empty()).then_some(parsed.session_id),
        usage: Usage {
            counts: parsed.usage,
            cost_usd: parsed.cost_usd,
            duration_ms: parsed.duration_ms,
        },
        error: (!parsed.error.is_empty()).then_some(parsed.error),
        unknown_json_lines: parsed.unknown_json_lines,
    }
}

fn convert_event(event: crate::json_output::JsonEvent) -> Event {
    if let Some(tool_call) = event.tool_call {
        return Event::ToolCall(ToolCall {
            id: tool_call.id,
            name: tool_call.name,
            arguments: tool_call.arguments,
        });
    }
    if let Some(tool_result) = event.tool_result {
        return Event::ToolResult(ToolResult {
            tool_call_id: tool_result.tool_call_id,
            content: tool_result.content,
            is_error: tool_result.is_error,
        });
    }
    if !event.thinking.is_empty() {
        return Event::Thinking(event.thinking);
    }
    match event.event_type.as_str() {
        "error" => Event::Error(event.text),
        "raw_unknown_json" => Event::RawUnknownJson(event.text),
        _ => Event::Text(event.text),
    }
}