1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::path::PathBuf;
4use std::time::{Duration, SystemTime};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum Capability {
9 CodeGeneration,
11 CodeModification,
13 FileOperations,
15 CommandExecution,
17 TestExecution,
19 QualityValidation,
21 QuestionAnswering,
23 FreeformChat,
25 SpecConversion,
27}
28
29impl std::fmt::Display for Capability {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 Capability::CodeGeneration => write!(f, "CodeGeneration"),
33 Capability::CodeModification => write!(f, "CodeModification"),
34 Capability::FileOperations => write!(f, "FileOperations"),
35 Capability::CommandExecution => write!(f, "CommandExecution"),
36 Capability::TestExecution => write!(f, "TestExecution"),
37 Capability::QualityValidation => write!(f, "QualityValidation"),
38 Capability::QuestionAnswering => write!(f, "QuestionAnswering"),
39 Capability::FreeformChat => write!(f, "FreeformChat"),
40 Capability::SpecConversion => write!(f, "SpecConversion"),
41 }
42 }
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
47pub enum Operation {
48 GenerateCode,
50 ModifyFile,
52 ExecuteCommand,
54 RunTests,
56 ValidateQuality,
58 AnswerQuestion,
60}
61
62impl std::fmt::Display for Operation {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 Operation::GenerateCode => write!(f, "GenerateCode"),
66 Operation::ModifyFile => write!(f, "ModifyFile"),
67 Operation::ExecuteCommand => write!(f, "ExecuteCommand"),
68 Operation::RunTests => write!(f, "RunTests"),
69 Operation::ValidateQuality => write!(f, "ValidateQuality"),
70 Operation::AnswerQuestion => write!(f, "AnswerQuestion"),
71 }
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct ModeConstraints {
78 pub allow_file_operations: bool,
80 pub allow_command_execution: bool,
82 pub allow_code_generation: bool,
84 pub require_specs: bool,
86 pub auto_think_more_threshold: Option<ComplexityLevel>,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
92pub enum ComplexityLevel {
93 Simple,
95 Moderate,
97 Complex,
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ThinkMoreConfig {
104 pub enabled: bool,
106 pub depth: ThinkingDepth,
108 pub timeout: Duration,
110 pub auto_enable: bool,
112}
113
114impl Default for ThinkMoreConfig {
115 fn default() -> Self {
116 Self {
117 enabled: false,
118 depth: ThinkingDepth::Medium,
119 timeout: Duration::from_secs(30),
120 auto_enable: false,
121 }
122 }
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
127pub enum ThinkingDepth {
128 Light,
130 Medium,
132 Deep,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ModeConfig {
139 pub temperature: f32,
141 pub max_tokens: usize,
143 pub system_prompt: String,
145 pub capabilities: Vec<Capability>,
147 pub constraints: ModeConstraints,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct Message {
154 pub role: MessageRole,
156 pub content: String,
158 pub timestamp: SystemTime,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
164pub enum MessageRole {
165 User,
167 Assistant,
169 System,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ModeContext {
176 pub session_id: String,
178 pub project_path: Option<PathBuf>,
180 pub think_more_enabled: bool,
182 pub think_more_config: ThinkMoreConfig,
184 pub conversation_history: Vec<Message>,
186 pub custom: HashMap<String, serde_json::Value>,
188}
189
190impl ModeContext {
191 pub fn new(session_id: String) -> Self {
193 Self {
194 session_id,
195 project_path: None,
196 think_more_enabled: false,
197 think_more_config: ThinkMoreConfig::default(),
198 conversation_history: Vec::new(),
199 custom: HashMap::new(),
200 }
201 }
202
203 pub fn add_message(&mut self, role: MessageRole, content: String) {
205 self.conversation_history.push(Message {
206 role,
207 content,
208 timestamp: SystemTime::now(),
209 });
210 }
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
215pub enum ModeAction {
216 GenerateCode {
218 spec: String,
220 },
221 ModifyFile {
223 path: PathBuf,
225 diff: String,
227 },
228 RunCommand {
230 command: String,
232 },
233 RunTests {
235 paths: Vec<PathBuf>,
237 },
238 ValidateQuality {
240 paths: Vec<PathBuf>,
242 },
243 AskQuestion {
245 question: String,
247 },
248 SuggestMode {
250 mode: String,
252 reason: String,
254 },
255 DisplayThinking {
257 content: String,
259 },
260}
261
262#[derive(Debug, Clone, Default, Serialize, Deserialize)]
264pub struct ChangeSummary {
265 pub files_created: usize,
267 pub files_modified: usize,
269 pub tests_run: usize,
271 pub tests_passed: usize,
273 pub quality_issues: Vec<String>,
275}
276
277#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct ResponseMetadata {
280 pub mode: String,
282 pub think_more_used: bool,
284 pub thinking_content: Option<String>,
286 pub tokens_used: usize,
288 pub duration: Duration,
290 pub changes_summary: Option<ChangeSummary>,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
296pub struct ModeResponse {
297 pub content: String,
299 pub actions: Vec<ModeAction>,
301 pub suggestions: Vec<String>,
303 pub metadata: ResponseMetadata,
305 pub summary: Option<String>,
307}
308
309impl ModeResponse {
310 pub fn new(content: String, mode: String) -> Self {
312 Self {
313 content,
314 actions: Vec::new(),
315 suggestions: Vec::new(),
316 metadata: ResponseMetadata {
317 mode,
318 think_more_used: false,
319 thinking_content: None,
320 tokens_used: 0,
321 duration: Duration::from_secs(0),
322 changes_summary: None,
323 },
324 summary: None,
325 }
326 }
327
328 pub fn add_action(&mut self, action: ModeAction) {
330 self.actions.push(action);
331 }
332
333 pub fn add_suggestion(&mut self, suggestion: String) {
335 self.suggestions.push(suggestion);
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use super::*;
342
343 #[test]
344 fn test_mode_context_creation() {
345 let context = ModeContext::new("test-session".to_string());
346 assert_eq!(context.session_id, "test-session");
347 assert!(context.project_path.is_none());
348 assert!(!context.think_more_enabled);
349 assert!(context.conversation_history.is_empty());
350 }
351
352 #[test]
353 fn test_mode_context_add_message() {
354 let mut context = ModeContext::new("test-session".to_string());
355 context.add_message(MessageRole::User, "Hello".to_string());
356 assert_eq!(context.conversation_history.len(), 1);
357 assert_eq!(context.conversation_history[0].role, MessageRole::User);
358 assert_eq!(context.conversation_history[0].content, "Hello");
359 }
360
361 #[test]
362 fn test_mode_response_creation() {
363 let response = ModeResponse::new("Test response".to_string(), "test-mode".to_string());
364 assert_eq!(response.content, "Test response");
365 assert_eq!(response.metadata.mode, "test-mode");
366 assert!(response.actions.is_empty());
367 assert!(response.suggestions.is_empty());
368 }
369
370 #[test]
371 fn test_mode_response_add_action() {
372 let mut response = ModeResponse::new("Test".to_string(), "test-mode".to_string());
373 response.add_action(ModeAction::AskQuestion {
374 question: "What is this?".to_string(),
375 });
376 assert_eq!(response.actions.len(), 1);
377 }
378
379 #[test]
380 fn test_mode_response_add_suggestion() {
381 let mut response = ModeResponse::new("Test".to_string(), "test-mode".to_string());
382 response.add_suggestion("Try this".to_string());
383 assert_eq!(response.suggestions.len(), 1);
384 assert_eq!(response.suggestions[0], "Try this");
385 }
386
387 #[test]
388 fn test_think_more_config_default() {
389 let config = ThinkMoreConfig::default();
390 assert!(!config.enabled);
391 assert_eq!(config.depth, ThinkingDepth::Medium);
392 assert!(!config.auto_enable);
393 }
394
395 #[test]
396 fn test_mode_constraints_creation() {
397 let constraints = ModeConstraints {
398 allow_file_operations: true,
399 allow_command_execution: false,
400 allow_code_generation: true,
401 require_specs: false,
402 auto_think_more_threshold: Some(ComplexityLevel::Complex),
403 };
404 assert!(constraints.allow_file_operations);
405 assert!(!constraints.allow_command_execution);
406 assert!(constraints.allow_code_generation);
407 }
408
409 #[test]
410 fn test_change_summary_default() {
411 let summary = ChangeSummary::default();
412 assert_eq!(summary.files_created, 0);
413 assert_eq!(summary.files_modified, 0);
414 assert_eq!(summary.tests_run, 0);
415 assert_eq!(summary.tests_passed, 0);
416 assert!(summary.quality_issues.is_empty());
417 }
418
419 #[test]
420 fn test_message_creation() {
421 let msg = Message {
422 role: MessageRole::User,
423 content: "Hello".to_string(),
424 timestamp: SystemTime::now(),
425 };
426 assert_eq!(msg.role, MessageRole::User);
427 assert_eq!(msg.content, "Hello");
428 }
429
430 #[test]
431 fn test_capability_display() {
432 assert_eq!(Capability::CodeGeneration.to_string(), "CodeGeneration");
433 assert_eq!(
434 Capability::QuestionAnswering.to_string(),
435 "QuestionAnswering"
436 );
437 }
438
439 #[test]
440 fn test_operation_display() {
441 assert_eq!(Operation::GenerateCode.to_string(), "GenerateCode");
442 assert_eq!(Operation::AnswerQuestion.to_string(), "AnswerQuestion");
443 }
444
445 #[test]
446 fn test_serialization_mode_context() {
447 let context = ModeContext::new("test-session".to_string());
448 let json = serde_json::to_string(&context).unwrap();
449 let deserialized: ModeContext = serde_json::from_str(&json).unwrap();
450 assert_eq!(deserialized.session_id, context.session_id);
451 }
452
453 #[test]
454 fn test_serialization_mode_response() {
455 let response = ModeResponse::new("Test".to_string(), "test-mode".to_string());
456 let json = serde_json::to_string(&response).unwrap();
457 let deserialized: ModeResponse = serde_json::from_str(&json).unwrap();
458 assert_eq!(deserialized.content, response.content);
459 assert_eq!(deserialized.metadata.mode, response.metadata.mode);
460 }
461}