Meliputi: - File-level //! doc comment: tujuan file, alur kerja, komponen utama - Function-level /// doc comment: apa, parameter, return, flow, edge cases - Struct/enum/trait /// doc comment: peran, field docs - Tracing logging (tracing::info!/debug!/trace!/warn!/error!) di setiap fungsi - Inline comments untuk variable dan branching logic penting - Seluruh 8 crates di workspace: zesdex-backend, zesdex-cms, zesdex-entities, zesdex-iam, zesdex-infra, zesdex-ipc, zesdex-middleware, zesdex-utils - Build: 0 errors, 242/242 tests passed
70 lines
2.9 KiB
Rust
70 lines
2.9 KiB
Rust
//! Workspace directory-tree generation for subagent system prompts.
|
|
//!
|
|
//! Build an ASCII tree of the workspace directory structure so the LLM
|
|
//! can see the file layout — this is the same tree shown to the main
|
|
//! agent and gives subagents the same project-awareness.
|
|
//!
|
|
//! Flow: for each workspace root, walk using `ignore::WalkBuilder`
|
|
//! (respecting `.gitignore` and hidden files) → prefix `[DIR]` for
|
|
//! directories → truncate after 1000 entries to keep the prompt
|
|
//! reasonably sized.
|
|
|
|
use std::fmt::Write;
|
|
use tracing;
|
|
|
|
/// Build an ASCII tree of the workspace directory structure for the
|
|
/// system prompt, so the LLM can see the file layout.
|
|
///
|
|
/// Flow: for each root, walk using `ignore::WalkBuilder` (respecting
|
|
/// `.gitignore` and hidden files) → prefix `[DIR]` for directories →
|
|
/// truncate after 1000 entries to keep the system prompt under control.
|
|
///
|
|
/// The tree is appended to the subagent's system prompt so the LLM can
|
|
/// reference file paths without having seen them in conversation.
|
|
///
|
|
/// Return: a multi-line string containing the ASCII tree, or an empty
|
|
/// string preamble + entries if no roots are provided.
|
|
pub(crate) fn generate_workspace_tree(roots: &[std::path::PathBuf]) -> String {
|
|
tracing::debug!("[subagent] generating workspace tree for {} root(s)", roots.len());
|
|
|
|
let mut out = String::new();
|
|
out.push_str("Current Workspace Directory Structure:\n");
|
|
|
|
for root in roots {
|
|
// Print the root path as a section header.
|
|
writeln!(out, "Root: {}", root.display()).unwrap();
|
|
|
|
// Walk the directory tree using ignore::WalkBuilder, which respects
|
|
// .gitignore rules and hidden files by default.
|
|
let walker = ignore::WalkBuilder::new(root)
|
|
.hidden(true)
|
|
.git_ignore(true)
|
|
.build();
|
|
|
|
let mut count = 0;
|
|
for entry in walker.flatten() {
|
|
let path = entry.path();
|
|
if let Ok(rel) = path.strip_prefix(root) {
|
|
// Skip the root entry itself (empty relative path).
|
|
if rel.as_os_str().is_empty() {
|
|
continue;
|
|
}
|
|
// Prefix directories with [DIR] for visual clarity.
|
|
let is_dir = entry.file_type().is_some_and(|ft| ft.is_dir());
|
|
let prefix = if is_dir { "[DIR] " } else { " " };
|
|
writeln!(out, " {}{}", prefix, rel.display()).unwrap();
|
|
count += 1;
|
|
// Hard cap at 1000 entries to avoid blowing up the prompt.
|
|
if count > 1000 {
|
|
tracing::info!("[subagent] workspace tree truncated at 1000 entries for '{}'", root.display());
|
|
out.push_str(" ... (truncated)\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tracing::debug!("[subagent] workspace tree generated ({} entries across {} roots)", out.lines().count(), roots.len());
|
|
out
|
|
}
|