storkit: merge 378_story_status_command_shows_work_item_type_story_bug_spike_refactor_next_to_each_item

This commit is contained in:
dave
2026-03-23 18:40:15 +00:00
parent f10ea1ecf2
commit 4a94158ef2

View File

@@ -16,23 +16,36 @@ pub(super) fn handle_status(ctx: &CommandContext) -> Option<String> {
/// 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.
/// Extracts the leading numeric ID and optional type tag from the file stem
/// (e.g. `"293"` and `"story"` from `"293_story_register_all_bot_commands"`)
/// and combines them with the human-readable name from the front matter when
/// available. Known types (`story`, `bug`, `spike`, `refactor`) are shown as
/// bracketed labels; unknown or missing types are omitted silently.
///
/// Examples:
/// - `("293_story_foo", Some("Register all bot commands"))` → `"293 — Register all bot commands"`
/// - `("293_story_foo", None)` → `"293"`
/// - `("293_story_foo", Some("Register all bot commands"))` → `"293 [story] — Register all bot commands"`
/// - `("375_bug_foo", None)` → `"375 [bug]"`
/// - `("293_story_foo", None)` → `"293 [story]"`
/// - `("no_number_here", None)` → `"no_number_here"`
pub(super) 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}"),
let mut parts = stem.splitn(3, '_');
let first = parts.next().unwrap_or(stem);
let (number, type_label) = if !first.is_empty() && first.chars().all(|c| c.is_ascii_digit()) {
let t = parts.next().and_then(|t| match t {
"story" | "bug" | "spike" | "refactor" => Some(t),
_ => None,
});
(first, t)
} else {
(stem, None)
};
let prefix = match type_label {
Some(t) => format!("{number} [{t}]"),
None => number.to_string(),
};
match name {
Some(n) => format!("{prefix}{n}"),
None => prefix,
}
}
@@ -200,13 +213,13 @@ mod tests {
#[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");
assert_eq!(label, "293 [story] — 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");
assert_eq!(label, "297 [story]");
}
#[test]
@@ -224,6 +237,37 @@ mod tests {
);
}
#[test]
fn short_label_shows_bug_type() {
let label = story_short_label("375_bug_default_project_toml", Some("Default project.toml issue"));
assert_eq!(label, "375 [bug] — Default project.toml issue");
}
#[test]
fn short_label_shows_spike_type() {
let label = story_short_label("61_spike_filesystem_watcher_architecture", Some("Filesystem watcher architecture"));
assert_eq!(label, "61 [spike] — Filesystem watcher architecture");
}
#[test]
fn short_label_shows_refactor_type() {
let label = story_short_label("260_refactor_upgrade_libsqlite3_sys", Some("Upgrade libsqlite3-sys"));
assert_eq!(label, "260 [refactor] — Upgrade libsqlite3-sys");
}
#[test]
fn short_label_omits_unknown_type() {
let label = story_short_label("42_task_do_something", Some("Do something"));
assert_eq!(label, "42 — Do something");
}
#[test]
fn short_label_no_type_when_only_id() {
// Stem with only a numeric ID and no type segment
let label = story_short_label("42", Some("Some item"));
assert_eq!(label, "42 — Some item");
}
// -- build_pipeline_status formatting -----------------------------------
#[test]
@@ -248,8 +292,8 @@ mod tests {
"output must not show full filename stem: {output}"
);
assert!(
output.contains("293 — Register all bot commands"),
"output must show number and title: {output}"
output.contains("293 [story] — Register all bot commands"),
"output must show number, type, and title: {output}"
);
}
@@ -288,7 +332,7 @@ mod tests {
let output = build_pipeline_status(tmp.path(), &agents);
assert!(
output.contains("293 — Register all bot commands — $0.29"),
output.contains("293 [story] — Register all bot commands — $0.29"),
"output must show cost next to story: {output}"
);
}
@@ -351,7 +395,7 @@ mod tests {
let output = build_pipeline_status(tmp.path(), &agents);
assert!(
output.contains("293 — Register all bot commands — $0.29"),
output.contains("293 [story] — Register all bot commands — $0.29"),
"output must show aggregated cost: {output}"
);
}