Files
zesdex/crates/zesdex-backend/src/app/subagent/workspace.rs
T
asepharyana 5aaedbf787 docs: tambah doc comment, logging, dan inline comments di semua 255 file
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
2026-07-19 17:05:47 +07:00

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
}