Skip to main content

cli_engine/cli/
help.rs

1use std::collections::BTreeMap;
2
3/// One module row in root long help.
4#[derive(Clone, Debug, Eq, PartialEq)]
5pub struct ModuleHelpEntry {
6    /// Help category label.
7    pub category: String,
8    /// Top-level module group name.
9    pub name: String,
10    /// One-line module description.
11    pub short: String,
12}
13
14/// Builds the root long help text from module categories and built-in command hints.
15#[must_use]
16pub fn build_root_long(intro: &str, entries: &[ModuleHelpEntry], has_guide: bool) -> String {
17    let mut categories = Vec::<String>::new();
18    let mut by_category = BTreeMap::<String, Vec<&ModuleHelpEntry>>::new();
19    for entry in entries {
20        if !by_category.contains_key(&entry.category) {
21            categories.push(entry.category.clone());
22        }
23        by_category
24            .entry(entry.category.clone())
25            .or_default()
26            .push(entry);
27    }
28
29    let max_width = entries
30        .iter()
31        .map(|entry| entry.name.len())
32        .max()
33        .unwrap_or_default();
34    let mut out = intro.to_owned();
35    for category in categories {
36        out.push_str(&format!("\n\n  {category}:"));
37        if let Some(category_entries) = by_category.get(&category) {
38            for entry in category_entries {
39                out.push_str(&format!(
40                    "\n    {:<width$}  {}",
41                    entry.name,
42                    entry.short,
43                    width = max_width
44                ));
45            }
46        }
47    }
48    out.push_str("\n\n  Find Commands:");
49    out.push_str("\n    --search <keyword>  Search all commands and guides by keyword");
50    out.push_str("\n    tree                Display full command tree");
51    if has_guide {
52        out.push_str("\n    guide               Built-in guides for AI agents and developers");
53    }
54    out
55}