From 8ee59f5dc1ad8a87aaab9158a59da85e0e753bab Mon Sep 17 00:00:00 2001 From: dave Date: Sat, 28 Mar 2026 18:33:22 +0000 Subject: [PATCH] storkit: merge 439_refactor_unify_story_stuck_states_into_a_single_status_field --- server/src/chat/timer.rs | 33 ++---------------- server/src/chat/transport/matrix/assign.rs | 35 ++----------------- server/src/chat/transport/matrix/delete.rs | 36 ++------------------ server/src/chat/transport/matrix/htop.rs | 37 ++------------------- server/src/chat/transport/matrix/rebuild.rs | 33 ++---------------- server/src/chat/transport/matrix/reset.rs | 33 ++---------------- server/src/chat/transport/matrix/rmtree.rs | 33 ++---------------- server/src/chat/transport/matrix/start.rs | 35 ++----------------- 8 files changed, 17 insertions(+), 258 deletions(-) diff --git a/server/src/chat/timer.rs b/server/src/chat/timer.rs index 61b43ae3..87614662 100644 --- a/server/src/chat/timer.rs +++ b/server/src/chat/timer.rs @@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; +use crate::chat::util::strip_bot_mention; + // ── Data types ───────────────────────────────────────────────────────────── /// A single scheduled timer entry. @@ -256,7 +258,7 @@ pub fn extract_timer_command( bot_name: &str, bot_user_id: &str, ) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped .trim() .trim_start_matches(|c: char| !c.is_alphanumeric()); @@ -466,35 +468,6 @@ fn resolve_story_id(number_or_id: &str, project_root: &Path) -> Option { None } -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // ── Tests ────────────────────────────────────────────────────────────────── #[cfg(test)] diff --git a/server/src/chat/transport/matrix/assign.rs b/server/src/chat/transport/matrix/assign.rs index ce598b32..f3ab9a81 100644 --- a/server/src/chat/transport/matrix/assign.rs +++ b/server/src/chat/transport/matrix/assign.rs @@ -9,6 +9,7 @@ //! that the next `start` invocation picks it up automatically. use crate::agents::{AgentPool, AgentStatus}; +use crate::chat::util::strip_bot_mention; use crate::io::story_metadata::{parse_front_matter, set_front_matter_field}; use std::path::Path; @@ -43,7 +44,7 @@ pub fn extract_assign_command( bot_name: &str, bot_user_id: &str, ) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped .trim() .trim_start_matches(|c: char| !c.is_alphanumeric()); @@ -234,38 +235,6 @@ pub async fn handle_assign( } } -/// Strip the bot mention prefix from a raw Matrix message body. -/// -/// Mirrors the logic in `commands::strip_bot_mention` and `start::strip_mention`. -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- diff --git a/server/src/chat/transport/matrix/delete.rs b/server/src/chat/transport/matrix/delete.rs index 7e1a94ef..906549ff 100644 --- a/server/src/chat/transport/matrix/delete.rs +++ b/server/src/chat/transport/matrix/delete.rs @@ -5,6 +5,7 @@ //! commits the change to git. use crate::agents::{AgentPool, AgentStatus}; +use crate::chat::util::strip_bot_mention; use std::path::Path; /// A parsed delete command from a Matrix message body. @@ -25,7 +26,7 @@ pub fn extract_delete_command( bot_name: &str, bot_user_id: &str, ) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped .trim() .trim_start_matches(|c: char| !c.is_alphanumeric()); @@ -185,39 +186,6 @@ fn stage_display_name(stage: &str) -> &str { } } -/// Strip the bot mention prefix from a raw Matrix message body. -/// -/// Mirrors the logic in `commands::strip_bot_mention` and `htop::strip_mention` -/// so delete detection works without depending on private symbols. -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- diff --git a/server/src/chat/transport/matrix/htop.rs b/server/src/chat/transport/matrix/htop.rs index 98226df9..3d3f131f 100644 --- a/server/src/chat/transport/matrix/htop.rs +++ b/server/src/chat/transport/matrix/htop.rs @@ -13,6 +13,7 @@ use std::time::Duration; use tokio::sync::{Mutex as TokioMutex, watch}; use crate::agents::{AgentPool, AgentStatus}; +use crate::chat::util::strip_bot_mention; use crate::slog; use crate::chat::ChatTransport; @@ -51,7 +52,7 @@ pub type HtopSessions = Arc>>; /// - `htop 10m` → `Start { duration_secs: 600 }` /// - `htop 120` → `Start { duration_secs: 120 }` (bare seconds) pub fn extract_htop_command(message: &str, bot_name: &str, bot_user_id: &str) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped.trim(); // Strip leading punctuation (e.g. the comma in "@timmy, htop") @@ -88,40 +89,6 @@ fn parse_duration(s: &str) -> Option { s.parse::().ok() } -/// Strip the bot mention prefix from a raw Matrix message body. -/// -/// Mirrors the logic in `commands::strip_bot_mention` so htop detection works -/// without depending on private symbols in that module. -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // --------------------------------------------------------------------------- // System stats // --------------------------------------------------------------------------- diff --git a/server/src/chat/transport/matrix/rebuild.rs b/server/src/chat/transport/matrix/rebuild.rs index bec3627a..08826e5b 100644 --- a/server/src/chat/transport/matrix/rebuild.rs +++ b/server/src/chat/transport/matrix/rebuild.rs @@ -6,6 +6,7 @@ //! running. use crate::agents::AgentPool; +use crate::chat::util::strip_bot_mention; use std::path::Path; use std::sync::Arc; @@ -22,7 +23,7 @@ pub fn extract_rebuild_command( bot_name: &str, bot_user_id: &str, ) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped .trim() .trim_start_matches(|c: char| !c.is_alphanumeric()); @@ -56,36 +57,6 @@ pub async fn handle_rebuild( } } -/// Strip the bot mention prefix from a raw Matrix message body. -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- diff --git a/server/src/chat/transport/matrix/reset.rs b/server/src/chat/transport/matrix/reset.rs index cca78812..f8dbc617 100644 --- a/server/src/chat/transport/matrix/reset.rs +++ b/server/src/chat/transport/matrix/reset.rs @@ -6,6 +6,7 @@ //! affected — only the in-memory/persisted conversation state is cleared. use crate::chat::transport::matrix::bot::{ConversationHistory, RoomConversation}; +use crate::chat::util::strip_bot_mention; use matrix_sdk::ruma::OwnedRoomId; use std::path::Path; @@ -22,7 +23,7 @@ pub fn extract_reset_command( bot_name: &str, bot_user_id: &str, ) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped .trim() .trim_start_matches(|c: char| !c.is_alphanumeric()); @@ -58,36 +59,6 @@ pub async fn handle_reset( "Session reset. Starting fresh — previous context has been cleared.".to_string() } -/// Strip the bot mention prefix from a raw Matrix message body. -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- diff --git a/server/src/chat/transport/matrix/rmtree.rs b/server/src/chat/transport/matrix/rmtree.rs index 867dbff5..5096676f 100644 --- a/server/src/chat/transport/matrix/rmtree.rs +++ b/server/src/chat/transport/matrix/rmtree.rs @@ -5,6 +5,7 @@ //! The story file in the pipeline is left untouched. use crate::agents::{AgentPool, AgentStatus}; +use crate::chat::util::strip_bot_mention; use std::path::Path; /// A parsed rmtree command from a Matrix message body. @@ -25,7 +26,7 @@ pub fn extract_rmtree_command( bot_name: &str, bot_user_id: &str, ) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped .trim() .trim_start_matches(|c: char| !c.is_alphanumeric()); @@ -118,36 +119,6 @@ pub async fn handle_rmtree( response } -/// Strip the bot mention prefix from a raw Matrix message body. -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- diff --git a/server/src/chat/transport/matrix/start.rs b/server/src/chat/transport/matrix/start.rs index b2362bdc..d60f3176 100644 --- a/server/src/chat/transport/matrix/start.rs +++ b/server/src/chat/transport/matrix/start.rs @@ -7,6 +7,7 @@ //! name ends with the supplied hint, e.g. `coder-{hint}`). use crate::agents::AgentPool; +use crate::chat::util::strip_bot_mention; use std::path::Path; /// A parsed start command from a Matrix message body. @@ -31,7 +32,7 @@ pub fn extract_start_command( bot_name: &str, bot_user_id: &str, ) -> Option { - let stripped = strip_mention(message, bot_name, bot_user_id); + let stripped = strip_bot_mention(message, bot_name, bot_user_id); let trimmed = stripped .trim() .trim_start_matches(|c: char| !c.is_alphanumeric()); @@ -177,38 +178,6 @@ pub async fn handle_start( } } -/// Strip the bot mention prefix from a raw Matrix message body. -/// -/// Mirrors the logic in `commands::strip_bot_mention` and `delete::strip_mention`. -fn strip_mention<'a>(message: &'a str, bot_name: &str, bot_user_id: &str) -> &'a str { - let trimmed = message.trim(); - if let Some(rest) = strip_prefix_ci(trimmed, bot_user_id) { - return rest; - } - if let Some(localpart) = bot_user_id.split(':').next() - && let Some(rest) = strip_prefix_ci(trimmed, localpart) - { - return rest; - } - if let Some(rest) = strip_prefix_ci(trimmed, bot_name) { - return rest; - } - trimmed -} - -fn strip_prefix_ci<'a>(text: &'a str, prefix: &str) -> Option<&'a str> { - let candidate = text.get(..prefix.len())?; - if !candidate.eq_ignore_ascii_case(prefix) { - return None; - } - let rest = &text[prefix.len()..]; - match rest.chars().next() { - None => Some(rest), - Some(c) if c.is_alphanumeric() || c == '-' || c == '_' => None, - _ => Some(rest), - } -} - // --------------------------------------------------------------------------- // Tests // ---------------------------------------------------------------------------