Skip to main content

claude_wrapper/
slash.rs

1//! Helpers for building Claude Code's slash-command strings.
2//!
3//! The interactive CLI exposes commands like `/compact`, `/clear`,
4//! `/model` that the user types mid-conversation. In stream-json
5//! input mode the same `/foo args` strings are passed through as
6//! turn prompts -- the CLI interprets them, the model doesn't see
7//! them as user prompts.
8//!
9//! Hosts wrapping a duplex session as a chat (MCP servers, IDE
10//! backends, agent runtimes) need to construct these strings
11//! safely, especially when forwarding optional user instructions.
12//! These helpers keep the CLI's slash syntax in one place rather
13//! than having every caller hand-roll `format!("/compact {}", ...)`.
14//!
15//! # Example
16//!
17//! ```
18//! use claude_wrapper::slash;
19//!
20//! assert_eq!(slash::compact(None), "/compact");
21//! assert_eq!(slash::compact(Some("focus on auth bug")), "/compact focus on auth bug");
22//! ```
23
24/// Build the prompt string for `/compact`.
25///
26/// Pass `Some(instructions)` to mirror the CLI's
27/// `/compact <instructions>` form (the trailing text steers the
28/// summarizer); pass `None` for a default compaction. Empty or
29/// whitespace-only `instructions` collapse to the bare form.
30pub fn compact(instructions: Option<&str>) -> String {
31    match instructions {
32        Some(s) => {
33            let trimmed = s.trim();
34            if trimmed.is_empty() {
35                "/compact".to_string()
36            } else {
37                format!("/compact {trimmed}")
38            }
39        }
40        None => "/compact".to_string(),
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn compact_without_instructions_is_bare() {
50        assert_eq!(compact(None), "/compact");
51    }
52
53    #[test]
54    fn compact_with_instructions_appends_them() {
55        assert_eq!(
56            compact(Some("focus on the auth bug")),
57            "/compact focus on the auth bug"
58        );
59    }
60
61    #[test]
62    fn compact_trims_surrounding_whitespace() {
63        assert_eq!(compact(Some("   keep tests   ")), "/compact keep tests");
64    }
65
66    #[test]
67    fn compact_empty_string_collapses_to_bare() {
68        assert_eq!(compact(Some("")), "/compact");
69        assert_eq!(compact(Some("   ")), "/compact");
70    }
71}