//! Markdown output writer. use std::path::PathBuf; use crate::models::{CodebaseSummary, FileSummary, Symbol, SymbolKind}; pub const AGENTS_INSTRUCTIONS: &str = r#"# Codebase Navigation Protocol You have access to codebase_summary.md. This file contains a compressed map of the codebase, including exported signatures and their associated documentation comments. Protocol: Consult Summary First: Use the doc-strings in codebase_summary.md to understand the intent and usage of functions without reading the full implementation. Identify Targets: Identify the specific files containing the logic you need. Deep Dive: Open the identified files to study the internal logic/edge cases before proposing a solution. After making changes: run the codebase_summarizer tool to update the codebase_summary.md again if you changed the directory structure, added or removed code files, changed function signatures, changed comments or if you see otherwise necessary "#; /// Writer for generating markdown output. pub struct MarkdownWriter; impl MarkdownWriter { pub fn new() -> Self { Self } /// Write the codebase summary to a markdown file. pub fn write_summary(&self, summary: &CodebaseSummary, output_path: &PathBuf) -> std::io::Result<()> { let mut content = String::new(); content.push_str("# Codebase Architecture Map\n\n"); // Sort files by path for consistent output let mut files = summary.files.clone(); files.sort_by(|a, b| a.path.cmp(&b.path)); for file_summary in files { self.write_file_summary(&mut content, &file_summary); } std::fs::write(output_path, content)?; Ok(()) } fn write_file_summary(&self, content: &mut String, file_summary: &FileSummary) { // Only write files that have symbols if file_summary.symbols.is_empty() { return; } let relative_path = self.get_relative_path(&file_summary.path); content.push_str(&format!("## File: {}\n", relative_path)); content.push('\n'); for symbol in &file_summary.symbols { self.write_symbol(content, symbol); } content.push('\n'); } fn write_symbol(&self, content: &mut String, symbol: &Symbol) { let kind_str = match symbol.kind { SymbolKind::Function => "func", SymbolKind::Method => "method", SymbolKind::Struct => "struct", SymbolKind::Enum => "enum", SymbolKind::Class => "class", SymbolKind::Interface => "interface", SymbolKind::Trait => "trait", SymbolKind::TypeAlias => "type", SymbolKind::Const => "const", SymbolKind::Static => "static", SymbolKind::Field => "field", SymbolKind::Module => "module", SymbolKind::Import => "import", }; content.push_str(&format!("### {} {}\n", kind_str, symbol.name)); if let Some(doc) = &symbol.doc { content.push_str(&format!("**Doc:** \n{}\n", doc)); } else { content.push_str("**Doc:** \n"); } content.push('\n'); } fn get_relative_path(&self, path: &PathBuf) -> String { path.to_string_lossy().to_string() } /// Write the AGENTS.md file if it doesn't exist. pub fn write_agents_md(&self, output_dir: &PathBuf) -> std::io::Result { let agents_path = output_dir.join("AGENTS.md"); if agents_path.exists() { return Ok(false); } std::fs::write(&agents_path, AGENTS_INSTRUCTIONS)?; Ok(true) } } impl Default for MarkdownWriter { fn default() -> Self { Self::new() } }