writer.rs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. //! Markdown output writer.
  2. use std::path::PathBuf;
  3. use crate::models::{CodebaseSummary, FileSummary, Symbol, SymbolKind};
  4. pub const AGENTS_INSTRUCTIONS: &str = r#"# Codebase Navigation Protocol
  5. You have access to codebase_summary.md. This file contains a compressed map of the codebase, including exported signatures and their associated documentation comments.
  6. Protocol:
  7. Consult Summary First: Use the doc-strings in codebase_summary.md to understand the intent and usage of functions without reading the full implementation.
  8. Identify Targets: Identify the specific files containing the logic you need.
  9. Deep Dive: Open the identified files to study the internal logic/edge cases before proposing a solution.
  10. 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
  11. or if you see otherwise necessary
  12. "#;
  13. /// Writer for generating markdown output.
  14. pub struct MarkdownWriter;
  15. impl MarkdownWriter {
  16. pub fn new() -> Self {
  17. Self
  18. }
  19. /// Write the codebase summary to a markdown file.
  20. pub fn write_summary(&self, summary: &CodebaseSummary, output_path: &PathBuf) -> std::io::Result<()> {
  21. let mut content = String::new();
  22. content.push_str("# Codebase Architecture Map\n\n");
  23. // Sort files by path for consistent output
  24. let mut files = summary.files.clone();
  25. files.sort_by(|a, b| a.path.cmp(&b.path));
  26. for file_summary in files {
  27. self.write_file_summary(&mut content, &file_summary);
  28. }
  29. std::fs::write(output_path, content)?;
  30. Ok(())
  31. }
  32. fn write_file_summary(&self, content: &mut String, file_summary: &FileSummary) {
  33. // Only write files that have symbols
  34. if file_summary.symbols.is_empty() {
  35. return;
  36. }
  37. let relative_path = self.get_relative_path(&file_summary.path);
  38. content.push_str(&format!("## File: {}\n", relative_path));
  39. content.push('\n');
  40. for symbol in &file_summary.symbols {
  41. self.write_symbol(content, symbol);
  42. }
  43. content.push('\n');
  44. }
  45. fn write_symbol(&self, content: &mut String, symbol: &Symbol) {
  46. let kind_str = match symbol.kind {
  47. SymbolKind::Function => "func",
  48. SymbolKind::Method => "method",
  49. SymbolKind::Struct => "struct",
  50. SymbolKind::Enum => "enum",
  51. SymbolKind::Class => "class",
  52. SymbolKind::Interface => "interface",
  53. SymbolKind::Trait => "trait",
  54. SymbolKind::TypeAlias => "type",
  55. SymbolKind::Const => "const",
  56. SymbolKind::Static => "static",
  57. SymbolKind::Field => "field",
  58. SymbolKind::Module => "module",
  59. SymbolKind::Import => "import",
  60. };
  61. content.push_str(&format!("### {} {}\n", kind_str, symbol.name));
  62. if let Some(doc) = &symbol.doc {
  63. content.push_str(&format!("**Doc:** \n{}\n", doc));
  64. } else {
  65. content.push_str("**Doc:** \n");
  66. }
  67. content.push('\n');
  68. }
  69. fn get_relative_path(&self, path: &PathBuf) -> String {
  70. path.to_string_lossy().to_string()
  71. }
  72. /// Write the AGENTS.md file if it doesn't exist.
  73. pub fn write_agents_md(&self, output_dir: &PathBuf) -> std::io::Result<bool> {
  74. let agents_path = output_dir.join("AGENTS.md");
  75. if agents_path.exists() {
  76. return Ok(false);
  77. }
  78. std::fs::write(&agents_path, AGENTS_INSTRUCTIONS)?;
  79. Ok(true)
  80. }
  81. }
  82. impl Default for MarkdownWriter {
  83. fn default() -> Self {
  84. Self::new()
  85. }
  86. }