vtcode-core 0.136.1

Core library for VT Code - a Rust-based terminal coding agent
use super::*;

pub(crate) fn render_topic_file(topic: MemoryTopic, facts: &[GroundedFactRecord]) -> String {
    let mut out = format!("# {}\n\n{}\n", topic.title(), topic.description());
    if facts.is_empty() {
        out.push_str("\n- No saved facts yet.\n");
    } else {
        out.push('\n');
        for f in facts {
            let (_, src) = decode_topic_source(&f.source);
            let _ = writeln!(out, "- [{}] {}", src.trim(), f.fact);
        }
    }
    out
}

pub(crate) fn render_memory_index(
    preferences: &[GroundedFactRecord],
    repository_facts: &[GroundedFactRecord],
    notes: &[MemoryNoteSummary],
    pending_rollouts: usize,
) -> String {
    let mut highlights: Vec<_> = preferences.iter().chain(repository_facts.iter()).cloned().collect();
    let skip = highlights.len().saturating_sub(MEMORY_HIGHLIGHT_LIMIT);
    highlights = highlights.into_iter().skip(skip).collect();
    let mut out = String::from("# VT Code Memory Registry\n\n## Files\n");
    out.push_str("- `memory_summary.md`: Startup-injected summary for future sessions.\n");
    out.push_str("- `preferences.md`: Durable user preferences and workflow notes.\n");
    out.push_str("- `repository-facts.md`: Grounded repository facts and recurring tooling notes.\n");
    out.push_str("- `notes/`: User-authored durable notes available to the native memory tool.\n");
    out.push_str("- `rollout_summaries/`: Per-session evidence summaries.\n");
    let _ = write!(out, "\n## Rollout Status\n- Pending rollout summaries: {pending_rollouts}\n");
    out.push_str("\n## Highlights\n");
    if highlights.is_empty() {
        out.push_str("- No persistent notes yet.\n");
    } else {
        for f in &highlights {
            let (_, src) = decode_topic_source(&f.source);
            let _ = writeln!(out, "- [{}] {}", src.trim(), f.fact);
        }
    }
    if !notes.is_empty() {
        out.push_str("\n## Note Files\n");
        for n in notes {
            let _ = write!(out, "- `{}`", n.relative_path);
            if let Some(first) = n.highlights.first() {
                let _ = write!(out, ": {first}");
            }
            out.push('\n');
        }
    }
    out
}

pub(crate) fn render_memory_summary(
    preferences: &[GroundedFactRecord],
    repository_facts: &[GroundedFactRecord],
    notes: &[MemoryNoteSummary],
) -> String {
    let mut bullets: Vec<_> = preferences
        .iter()
        .chain(repository_facts.iter())
        .map(|f| f.fact.clone())
        .collect();
    bullets.extend(
        notes
            .iter()
            .filter_map(|n| n.highlights.first().map(|h| format!("Note ({}): {}", n.relative_path, h))),
    );
    let skip = bullets.len().saturating_sub(MEMORY_HIGHLIGHT_LIMIT);
    bullets = bullets.into_iter().skip(skip).collect();
    if bullets.is_empty() {
        bullets.push("No durable memory notes have been consolidated yet.".to_string());
    }
    render_memory_summary_bullets(&bullets)
}

pub(crate) fn render_memory_summary_bullets(bullets: &[String]) -> String {
    let mut out = String::from("# VT Code Memory Summary\n");
    for b in bullets {
        let _ = writeln!(out, "- {}", b.trim());
    }
    out
}

pub(crate) fn render_rollout_summary(classified: &ClassifiedFacts) -> String {
    let mut out = format!("# Rollout Summary\n\n- Generated: {}\n", chrono::Utc::now().to_rfc3339());
    if classified.total() == 0 {
        out.push_str("\n- No durable facts captured.\n");
    } else {
        out.push('\n');
        for f in classified.preferences.iter().chain(&classified.repository_facts) {
            let _ = writeln!(out, "- [{}] {}", f.source, f.fact);
        }
    }
    out
}

pub(crate) fn unique_rollout_id() -> String {
    let millis = SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis()).unwrap_or(0);
    format!("rollout-{millis}")
}