From 6353b12c1df6eeec936785889b1ce81b0cac2160 Mon Sep 17 00:00:00 2001 From: Dave Date: Thu, 19 Mar 2026 09:36:58 +0000 Subject: [PATCH] story-kit: merge 297_story_improve_bot_status_command_formatting --- server/src/matrix/commands.rs | 89 ++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/server/src/matrix/commands.rs b/server/src/matrix/commands.rs index 044ab73..3350c4d 100644 --- a/server/src/matrix/commands.rs +++ b/server/src/matrix/commands.rs @@ -178,6 +178,28 @@ fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { // Pipeline status helpers (moved from bot.rs) // --------------------------------------------------------------------------- +/// Format a short display label for a work item. +/// +/// Extracts the leading numeric ID from the file stem (e.g. `"293"` from +/// `"293_story_register_all_bot_commands"`) and combines it with the human- +/// readable name from the front matter when available. +/// +/// Examples: +/// - `("293_story_foo", Some("Register all bot commands"))` → `"293 — Register all bot commands"` +/// - `("293_story_foo", None)` → `"293"` +/// - `("no_number_here", None)` → `"no_number_here"` +fn story_short_label(stem: &str, name: Option<&str>) -> String { + let number = stem + .split('_') + .next() + .filter(|s| !s.is_empty() && s.chars().all(|c| c.is_ascii_digit())) + .unwrap_or(stem); + match name { + Some(n) => format!("{number} — {n}"), + None => number.to_string(), + } +} + /// Read all story IDs and names from a pipeline stage directory. fn read_stage_items( project_root: &std::path::Path, @@ -243,10 +265,7 @@ pub fn build_pipeline_status(project_root: &std::path::Path, agents: &AgentPool) out.push_str(" *(none)*\n"); } else { for (story_id, name) in &items { - let display = match name { - Some(n) => format!("{story_id} — {n}"), - None => story_id.clone(), - }; + let display = story_short_label(story_id, name.as_deref()); if let Some(agent) = active_map.get(story_id) { let model_str = config .as_ref() @@ -254,8 +273,8 @@ pub fn build_pipeline_status(project_root: &std::path::Path, agents: &AgentPool) .and_then(|ac| ac.model.as_deref()) .unwrap_or("?"); out.push_str(&format!( - " • {display} — {} ({}) [{}]\n", - agent.agent_name, model_str, agent.status + " • {display} — {} ({model_str})\n", + agent.agent_name )); } else { out.push_str(&format!(" • {display}\n")); @@ -667,6 +686,64 @@ mod tests { assert_eq!(strip_prefix_ci("hello", "hello"), Some("")); } + // -- story_short_label -------------------------------------------------- + + #[test] + fn short_label_extracts_number_and_name() { + let label = story_short_label("293_story_register_all_bot_commands", Some("Register all bot commands")); + assert_eq!(label, "293 — Register all bot commands"); + } + + #[test] + fn short_label_number_only_when_no_name() { + let label = story_short_label("297_story_improve_bot_status_command_formatting", None); + assert_eq!(label, "297"); + } + + #[test] + fn short_label_falls_back_to_stem_when_no_numeric_prefix() { + let label = story_short_label("no_number_here", None); + assert_eq!(label, "no_number_here"); + } + + #[test] + fn short_label_does_not_include_underscore_slug() { + let label = story_short_label("293_story_register_all_bot_commands_in_the_command_registry", Some("Register all bot commands")); + assert!( + !label.contains("story_register"), + "label should not contain the slug portion: {label}" + ); + } + + // -- build_pipeline_status formatting ----------------------------------- + + #[test] + fn status_does_not_show_full_filename_stem() { + use std::io::Write; + use tempfile::TempDir; + + let tmp = TempDir::new().unwrap(); + let stage_dir = tmp.path().join(".story_kit/work/2_current"); + std::fs::create_dir_all(&stage_dir).unwrap(); + + // Write a story file with a front-matter name + let story_path = stage_dir.join("293_story_register_all_bot_commands.md"); + let mut f = std::fs::File::create(&story_path).unwrap(); + writeln!(f, "---\nname: Register all bot commands\n---\n").unwrap(); + + let agents = AgentPool::new_test(3000); + let output = build_pipeline_status(tmp.path(), &agents); + + assert!( + !output.contains("293_story_register_all_bot_commands"), + "output must not show full filename stem: {output}" + ); + assert!( + output.contains("293 — Register all bot commands"), + "output must show number and title: {output}" + ); + } + // -- commands registry -------------------------------------------------- #[test]