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