synaps_cli/engine/
commands.rs1#[derive(Debug, Clone)]
9pub enum CommandResult {
10 None,
12
13 Output(String),
15
16 Error(String),
18
19 ModelChanged {
21 model: String,
22 },
23
24 ThinkingChanged {
26 level: String,
27 budget: u32,
28 },
29
30 SystemPromptSet {
32 source: String, },
34
35 SystemPromptShow {
37 prompt: String,
38 },
39
40 SessionList {
42 sessions: Vec<SessionSummary>,
43 },
44
45 Cleared,
47
48 Quit,
50
51 Compact,
53
54 Resumed {
56 session_id: String,
57 model: String,
58 },
59
60 Named {
62 name: String,
63 },
64
65 ChainInfo(String),
67
68 OpenModal(ModalRequest),
70
71 Status {
73 text: String,
74 },
75
76 PingStarted,
78
79 KeybindList(String),
81
82 SkillLoaded {
84 skill: std::sync::Arc<crate::skills::LoadedSkill>,
85 arg: String,
86 },
87
88 PluginCommand {
90 command: std::sync::Arc<crate::skills::registry::RegisteredPluginCommand>,
91 arg: String,
92 },
93
94 SidecarToggle { plugin_id: Option<String> },
96 SidecarStatus { plugin_id: Option<String> },
97}
98
99#[derive(Debug, Clone)]
101pub enum ModalRequest {
102 Models,
103 Settings,
104 Plugins,
105 HelpFind { query: String },
106 Extensions { sub: String },
107}
108
109#[derive(Debug, Clone)]
111pub struct SessionSummary {
112 pub id: String,
113 pub model: String,
114 pub title: Option<String>,
115 pub cost: f64,
116 pub message_count: usize,
117 pub is_current: bool,
118}
119
120pub fn parse_command(input: &str) -> Option<(&str, &str)> {
122 let trimmed = input.trim();
123 if !trimmed.starts_with('/') {
124 return None;
125 }
126 let without_slash = &trimmed[1..];
127 let (cmd, arg) = match without_slash.find(char::is_whitespace) {
128 Some(pos) => (&without_slash[..pos], without_slash[pos..].trim()),
129 None => (without_slash, ""),
130 };
131 Some((cmd, arg))
132}
133
134pub fn handle_engine_command(
137 cmd: &str,
138 arg: &str,
139 runtime: &mut crate::Runtime,
140) -> Option<CommandResult> {
141 match cmd {
142 "model" if !arg.is_empty() => {
143 runtime.set_model(arg.to_string());
144 Some(CommandResult::ModelChanged {
145 model: arg.to_string(),
146 })
147 }
148 "thinking" if !arg.is_empty() => {
149 let (level, budget) = match arg {
150 "off" | "none" => ("off".to_string(), 0),
151 "adaptive" => ("adaptive".to_string(), 0),
156 "low" => ("low".to_string(), 2048),
157 "medium" | "med" => ("medium".to_string(), 4096),
158 "high" => ("high".to_string(), 16384),
159 "xhigh" | "max" => ("xhigh".to_string(), 32768),
160 other => {
161 if let Ok(n) = other.parse::<u32>() {
162 (format!("custom({})", n), n)
163 } else {
164 return Some(CommandResult::Error(
165 format!("unknown thinking level: {} (use off/adaptive/low/medium/high/xhigh or a number)", other)
166 ));
167 }
168 }
169 };
170 runtime.set_thinking_budget(budget);
171 Some(CommandResult::ThinkingChanged { level, budget })
172 }
173 "quit" | "exit" => Some(CommandResult::Quit),
174 "compact" => Some(CommandResult::Compact),
175 _ => None, }
177}