Help: consume CommandNode.help_id — REGISTRY-driven in-app help

Every CommandNode declared a help_id that nothing read; the in-app
`help` body was a single hand-kept catalog block that drifted from
the command set (handoff-12 §2.1).

note_help now iterates the command REGISTRY and translates each
CommandNode's help_id (`help.<id>`), framed by help.intro /
help.dsl_section / help.types_reference. A newly-registered command
appears in `help` automatically — no edit to note_help or a hand-kept
list. Added 20 per-command help entries plus the 3 framing entries;
removed help.in_app_body.

Per-command entries use block scalars: a libyml 0.0.5 scanner bug
panics on long internal space runs in double-quoted scalars, and the
entries are space-aligned.
This commit is contained in:
claude@clouddev1
2026-05-15 22:45:18 +00:00
parent f46606b12e
commit 03dd9003df
4 changed files with 139 additions and 56 deletions
+38 -14
View File
@@ -1595,22 +1595,46 @@ impl App {
}
}
/// Note a flat list of currently-supported app-level
/// commands to the output panel.
/// Note the list of currently-supported commands to the
/// output panel.
///
/// This is the simple Iteration-4 stand-in for a richer
/// help system (H3 in the requirements doc); it gives the
/// user a quick "what can I type?" reference that's
/// always accurate against the build they're running. As
/// new commands land, append them here.
/// Assembled from the command REGISTRY (ADR-0024 §help_id):
/// the framing (`help.intro`, `help.dsl_section`,
/// `help.types_reference`) comes from the catalog, and each
/// command's body is the catalog entry named by its
/// `help_id`. A newly-registered command appears here
/// automatically — no edit to this function or a hand-kept
/// list. Each catalog line becomes its own `OutputLine` so
/// the scroll-position math (one logical line = one display
/// row) stays accurate per the renderer's invariant.
fn note_help(&mut self) {
// Body lives in the i18n catalog (`help.in_app_body`).
// Each YAML line becomes its own `OutputLine` so the
// scroll-position math (one logical line = one display
// row) stays accurate per the renderer's invariant.
let body = crate::t!("help.in_app_body");
for line in body.lines() {
self.note_system(line.to_string());
use crate::dsl::grammar::REGISTRY;
let mut lines: Vec<String> = Vec::new();
lines.push(crate::t!("help.intro"));
// REGISTRY is ordered app-commands first; emit the
// "DSL data commands" sub-header at the first command
// whose help_id leaves the `app.` namespace.
let mut dsl_header_done = false;
for command in REGISTRY {
let Some(help_id) = command.help_id else {
continue;
};
if !dsl_header_done && !help_id.starts_with("app.") {
lines.push(crate::t!("help.dsl_section"));
dsl_header_done = true;
}
let key = format!("help.{help_id}");
let body = crate::friendly::translate(&key, &[]);
lines.extend(body.lines().map(str::to_string));
}
lines.extend(
crate::t!("help.types_reference")
.lines()
.map(str::to_string),
);
for line in lines {
self.note_system(line);
}
}