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