Skip to content

Commit f2175fc

Browse files
cursoragentseatedro
andcommitted
Add XML output format for improved LLM compatibility
Co-authored-by: rohit <[email protected]>
1 parent 3e7d25d commit f2175fc

File tree

3 files changed

+98
-10
lines changed

3 files changed

+98
-10
lines changed

readme.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ A blazingly fast tool for peeking at codebases. Perfect for loading your codebas
1616
- 🔗 Web content processing with Markdown conversion
1717
- 📦 Git repository support
1818
- 🌐 URL traversal with configurable depth
19+
- 🏷️ XML output format for better LLM compatibility
1920

2021
## Installation
2122

@@ -115,6 +116,9 @@ glimpse --config_path
115116

116117
# Initialize a .glimpse config file in the current directory
117118
glimpse --config
119+
120+
# Output in XML format for better LLM compatibility
121+
glimpse -x /path/to/project
118122
```
119123

120124
## CLI Options
@@ -146,6 +150,7 @@ Options:
146150
--traverse-links Traverse links when processing URLs
147151
--link-depth <DEPTH> Maximum depth to traverse links (default: 1)
148152
--pdf <PATH> Save output as PDF
153+
-x, --xml Output in XML format for better LLM compatibility
149154
-h, --help Print help
150155
-V, --version Print version
151156
```
@@ -179,6 +184,60 @@ default_excludes = [
179184
]
180185
```
181186

187+
## XML Output Format
188+
189+
Glimpse supports XML output format designed for better compatibility with Large Language Models (LLMs) like Claude, GPT, and others. When using the `-x` or `--xml` flag, the output is structured with clear XML tags that help LLMs better understand the context and structure of your codebase.
190+
191+
### XML Structure
192+
193+
The XML output wraps all content in a `<context>` tag with the project name:
194+
195+
```xml
196+
<context name="my_project">
197+
<tree>
198+
└── src/
199+
└── main.rs
200+
</tree>
201+
202+
<files>
203+
<file path="src/main.rs">
204+
================================================
205+
fn main() {
206+
println!("Hello, World!");
207+
}
208+
</file>
209+
</files>
210+
211+
<summary>
212+
Total files: 1
213+
Total size: 45 bytes
214+
</summary>
215+
</context>
216+
```
217+
218+
### Benefits for LLM Usage
219+
220+
- **Clear Context Boundaries**: The `<context>` wrapper helps LLMs understand where your codebase begins and ends
221+
- **Structured Information**: Separate sections for directory tree, file contents, and summary
222+
- **Proper Escaping**: XML-safe content that won't confuse parsers
223+
- **Project Identification**: Automatic project name detection for better context
224+
225+
### Usage Examples
226+
227+
```bash
228+
# Basic XML output
229+
glimpse -x /path/to/project
230+
231+
# XML output with file save
232+
glimpse -x -f project.xml /path/to/project
233+
234+
# XML output to stdout
235+
glimpse -x --print /path/to/project
236+
237+
# XML output with specific includes
238+
glimpse -x -i "*.rs,*.py" /path/to/project
239+
```
240+
182241
## Token Counting
183242

184243
Glimpse supports two tokenizer backends:

src/analyzer.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,14 @@ pub fn process_directory(args: &Cli) -> Result<()> {
6464
fn determine_project_name(paths: &[String]) -> String {
6565
if let Some(first_path) = paths.first() {
6666
let path = std::path::Path::new(first_path);
67-
67+
6868
// If it's a directory, use its name
6969
if path.is_dir() {
7070
if let Some(name) = path.file_name() {
7171
return name.to_string_lossy().to_string();
7272
}
7373
}
74-
74+
7575
// If it's a file, use the parent directory name
7676
if path.is_file() {
7777
if let Some(parent) = path.parent() {
@@ -80,7 +80,7 @@ fn determine_project_name(paths: &[String]) -> String {
8080
}
8181
}
8282
}
83-
83+
8484
// Fallback to just the path itself
8585
first_path.clone()
8686
} else {

src/output.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,20 @@ pub struct FileEntry {
1515
pub size: u64,
1616
}
1717

18-
pub fn generate_output(entries: &[FileEntry], format: OutputFormat, xml_format: bool, project_name: Option<String>) -> Result<String> {
18+
pub fn generate_output(
19+
entries: &[FileEntry],
20+
format: OutputFormat,
21+
xml_format: bool,
22+
project_name: Option<String>,
23+
) -> Result<String> {
1924
let mut output = String::new();
2025

2126
if xml_format {
2227
let project_name = project_name.unwrap_or_else(|| "project".to_string());
23-
output.push_str(&format!("<context name=\"{}\">\n", xml_escape(&project_name)));
28+
output.push_str(&format!(
29+
"<context name=\"{}\">\n",
30+
xml_escape(&project_name)
31+
));
2432
}
2533

2634
match format {
@@ -174,7 +182,10 @@ fn generate_files(entries: &[FileEntry], xml_format: bool) -> Result<String> {
174182

175183
for entry in entries {
176184
if xml_format {
177-
output.push_str(&format!("<file path=\"{}\">\n", xml_escape(entry.path.display().to_string().as_str())));
185+
output.push_str(&format!(
186+
"<file path=\"{}\">\n",
187+
xml_escape(entry.path.display().to_string().as_str())
188+
));
178189
output.push_str(&"=".repeat(48));
179190
output.push('\n');
180191
output.push_str(&entry.content);
@@ -379,9 +390,15 @@ mod tests {
379390
#[test]
380391
fn test_xml_output() {
381392
let entries = create_test_entries();
382-
393+
383394
// Test XML tree format
384-
let xml_tree_output = generate_output(&entries, OutputFormat::Tree, true, Some("test_project".to_string())).unwrap();
395+
let xml_tree_output = generate_output(
396+
&entries,
397+
OutputFormat::Tree,
398+
true,
399+
Some("test_project".to_string()),
400+
)
401+
.unwrap();
385402
assert!(xml_tree_output.contains("<context name=\"test_project\">"));
386403
assert!(xml_tree_output.contains("<tree>"));
387404
assert!(xml_tree_output.contains("</tree>"));
@@ -390,7 +407,13 @@ mod tests {
390407
assert!(xml_tree_output.contains("</context>"));
391408

392409
// Test XML files format
393-
let xml_files_output = generate_output(&entries, OutputFormat::Files, true, Some("test_project".to_string())).unwrap();
410+
let xml_files_output = generate_output(
411+
&entries,
412+
OutputFormat::Files,
413+
true,
414+
Some("test_project".to_string()),
415+
)
416+
.unwrap();
394417
assert!(xml_files_output.contains("<context name=\"test_project\">"));
395418
assert!(xml_files_output.contains("<files>"));
396419
assert!(xml_files_output.contains("<file path=\"src/main.rs\">"));
@@ -399,7 +422,13 @@ mod tests {
399422
assert!(xml_files_output.contains("</context>"));
400423

401424
// Test XML both format
402-
let xml_both_output = generate_output(&entries, OutputFormat::Both, true, Some("test_project".to_string())).unwrap();
425+
let xml_both_output = generate_output(
426+
&entries,
427+
OutputFormat::Both,
428+
true,
429+
Some("test_project".to_string()),
430+
)
431+
.unwrap();
403432
assert!(xml_both_output.contains("<context name=\"test_project\">"));
404433
assert!(xml_both_output.contains("<tree>"));
405434
assert!(xml_both_output.contains("</tree>"));

0 commit comments

Comments
 (0)