1pub mod brain;
2pub mod client;
3pub mod config;
4pub mod events;
5pub mod github;
6pub mod harness;
7pub mod hooks;
8pub mod lifecycle;
9pub mod plugin;
10pub mod pty;
11pub mod store;
12pub mod tmux;
13pub mod types;
14
15pub use brain::{BrainEntry, BrainIndex, QueryFilters};
16pub use config::{AppConfig, ThemeVariant};
17pub use events::{Engine, Event};
18pub use store::Store;
19pub use types::*;
20
21pub fn slugify(s: &str) -> String {
28 s.to_lowercase()
29 .chars()
30 .map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
31 .collect::<String>()
32 .split('-')
33 .filter(|p| !p.is_empty())
34 .collect::<Vec<_>>()
35 .join("-")
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn slugify_ticket_id() {
44 assert_eq!(slugify("ATH-123"), "ath-123");
45 }
46
47 #[test]
48 fn slugify_spaced_name() {
49 assert_eq!(slugify("auth fix"), "auth-fix");
50 }
51
52 #[test]
53 fn slugify_ticket_with_description() {
54 assert_eq!(slugify("ATH-123 auth fix"), "ath-123-auth-fix");
55 }
56
57 #[test]
58 fn slugify_collapses_consecutive_separators() {
59 assert_eq!(slugify("my feature!"), "my-feature");
60 }
61
62 #[test]
63 fn slugify_already_clean() {
64 assert_eq!(slugify("my-feature"), "my-feature");
65 }
66
67 #[test]
68 fn slugify_empty_returns_empty() {
69 assert_eq!(slugify(""), "");
70 assert_eq!(slugify("---"), "");
71 }
72}