#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FeatureGate {
Memory,
DebugSession,
Setup,
Hooks,
Librarian,
}
pub struct HelpTopic {
pub name: &'static str,
pub one_liner: &'static str,
pub body: &'static str,
pub requires: Option<FeatureGate>,
}
pub static ALL_HELP_TOPICS: &[HelpTopic] = &[
HelpTopic {
name: "observations",
one_liner: "querying, filtering, subscribing to runtime telemetry",
body: include_str!("../tool_descriptions/help/observations.md"),
requires: None,
},
HelpTopic {
name: "envelope",
one_liner: "the standard {result, daemon8, error} response shape",
body: include_str!("../tool_descriptions/help/envelope.md"),
requires: None,
},
HelpTopic {
name: "checkpoint",
one_liner: "bookmarking moments in the observation stream",
body: include_str!("../tool_descriptions/help/checkpoint.md"),
requires: None,
},
HelpTopic {
name: "lens",
one_liner: "buffering matching observations between queries",
body: include_str!("../tool_descriptions/help/lens.md"),
requires: None,
},
HelpTopic {
name: "debug_session",
one_liner: "protocol for opening/closing debugging investigations",
body: include_str!("../tool_descriptions/help/debug_session.md"),
requires: Some(FeatureGate::DebugSession),
},
HelpTopic {
name: "setup",
one_liner: "first-time configuration and provider enrollment",
body: include_str!("../tool_descriptions/help/setup.md"),
requires: Some(FeatureGate::Setup),
},
HelpTopic {
name: "hooks",
one_liner: "managing CLI provider hooks (Claude Code, Codex)",
body: include_str!("../tool_descriptions/help/hooks.md"),
requires: Some(FeatureGate::Hooks),
},
HelpTopic {
name: "memory",
one_liner: "persisting long-lived insights across sessions",
body: include_str!("../tool_descriptions/help/memory.md"),
requires: Some(FeatureGate::Memory),
},
HelpTopic {
name: "librarian",
one_liner: "graph-based reference catalog for documentation, configs, fixes",
body: include_str!("../tool_descriptions/help/librarian.md"),
requires: Some(FeatureGate::Librarian),
},
];
pub fn build_dynamic_index(enabled: &[FeatureGate], librarian_enabled: bool) -> String {
use std::fmt::Write;
let mut out = String::with_capacity(1024);
out.push_str("# daemon8 help\n\n");
out.push_str(
"Documentation is organized as isolated, AI-native context chunks. \
Each topic is a self-contained unit — load only what you need for the current task \
via `daemon8_help(topic=\"<name>\")`. This keeps your context window lean.\n\n",
);
out.push_str("## Topics\n\n");
for topic in ALL_HELP_TOPICS {
if topic_enabled(topic, enabled) {
let _ = writeln!(out, "- `{}` — {}", topic.name, topic.one_liner);
}
}
if librarian_enabled {
out.push_str(
"\n## Knowledge graph\n\n\
The **librarian** extends this help system with a graph-based index of project-specific \
references: documentation URLs, fix recipes, source configs, project protocols. \
These are high-value pointers — the librarian never stores content, only locators to \
where information lives. It complements whatever knowledge system you already use \
(Obsidian, cloud storage, wikis) without replacing it.\n\n\
Use `librarian_lookup` for project knowledge; `daemon8_help` for daemon8 protocol docs.\n",
);
}
out.push_str("\nUnknown topics return this index.\n");
out
}
pub fn find_topic(name: &str, enabled: &[FeatureGate]) -> Option<&'static HelpTopic> {
ALL_HELP_TOPICS
.iter()
.find(|t| t.name == name && topic_enabled(t, enabled))
}
pub fn topic_enabled(topic: &HelpTopic, enabled: &[FeatureGate]) -> bool {
match topic.requires {
None => true,
Some(gate) => enabled.contains(&gate),
}
}