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