codeprism_mcp/context/
workflow.rs

1//! Workflow context and guidance system
2//!
3//! Provides intelligent tool suggestions and workflow guidance based on
4//! current analysis state, session history, and detected patterns.
5
6use anyhow::Result;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10use super::session::{SessionState, WorkflowStage};
11
12/// Confidence level for suggestions
13#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
14pub enum ConfidenceLevel {
15    Low = 1,
16    Medium = 2,
17    High = 3,
18}
19
20/// Suggestion for a specific tool call
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct ToolSuggestion {
23    /// Name of the suggested tool
24    pub tool_name: String,
25    /// Suggested parameters
26    pub suggested_parameters: HashMap<String, serde_json::Value>,
27    /// Confidence in this suggestion
28    pub confidence: ConfidenceLevel,
29    /// Reason for the suggestion
30    pub reasoning: String,
31    /// Expected outcome
32    pub expected_outcome: String,
33    /// Priority (1 = highest)
34    pub priority: u8,
35}
36
37impl ToolSuggestion {
38    /// Create a new tool suggestion
39    pub fn new(
40        tool_name: String,
41        confidence: ConfidenceLevel,
42        reasoning: String,
43        expected_outcome: String,
44        priority: u8,
45    ) -> Self {
46        Self {
47            tool_name,
48            suggested_parameters: HashMap::new(),
49            confidence,
50            reasoning,
51            expected_outcome,
52            priority,
53        }
54    }
55
56    /// Add a suggested parameter
57    pub fn with_parameter(mut self, key: String, value: serde_json::Value) -> Self {
58        self.suggested_parameters.insert(key, value);
59        self
60    }
61
62    /// Add multiple suggested parameters
63    pub fn with_parameters(mut self, params: HashMap<String, serde_json::Value>) -> Self {
64        self.suggested_parameters.extend(params);
65        self
66    }
67}
68
69/// Workflow guidance and suggestions
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct WorkflowSuggestion {
72    /// Current workflow stage
73    pub current_stage: WorkflowStage,
74    /// Suggested next stage
75    pub next_stage: Option<WorkflowStage>,
76    /// Immediate tool suggestions
77    pub immediate_suggestions: Vec<ToolSuggestion>,
78    /// Alternative approaches
79    pub alternatives: Vec<ToolSuggestion>,
80    /// Overall workflow guidance
81    pub workflow_guidance: String,
82    /// Progress assessment
83    pub progress_assessment: String,
84}
85
86impl WorkflowSuggestion {
87    /// Create workflow suggestion for a given stage
88    pub fn for_stage(stage: WorkflowStage) -> Self {
89        let next_stage = stage.next_stage();
90        let workflow_guidance = Self::get_stage_guidance(&stage);
91        let progress_assessment = Self::assess_stage_progress(&stage);
92
93        Self {
94            current_stage: stage,
95            next_stage,
96            immediate_suggestions: Vec::new(),
97            alternatives: Vec::new(),
98            workflow_guidance,
99            progress_assessment,
100        }
101    }
102
103    /// Get guidance text for a workflow stage
104    fn get_stage_guidance(stage: &WorkflowStage) -> String {
105        match stage {
106            WorkflowStage::Discovery => {
107                "Focus on understanding the overall structure and scope of the codebase. \
108                Start with repository statistics and content exploration."
109                    .to_string()
110            }
111            WorkflowStage::Mapping => {
112                "Map out the relationships between components. Use symbol search and \
113                dependency analysis to understand the architecture."
114                    .to_string()
115            }
116            WorkflowStage::DeepDive => {
117                "Dive deep into specific areas of interest. Explain individual symbols \
118                and analyze complex patterns like inheritance hierarchies."
119                    .to_string()
120            }
121            WorkflowStage::Synthesis => {
122                "Synthesize your findings with quality analysis. Look for complexity issues, \
123                security concerns, and opportunities for improvement."
124                    .to_string()
125            }
126        }
127    }
128
129    /// Assess progress for a workflow stage
130    fn assess_stage_progress(stage: &WorkflowStage) -> String {
131        match stage {
132            WorkflowStage::Discovery => {
133                "Building initial understanding of the codebase structure.".to_string()
134            }
135            WorkflowStage::Mapping => {
136                "Mapping relationships and understanding component interactions.".to_string()
137            }
138            WorkflowStage::DeepDive => {
139                "Analyzing specific components and complex patterns in detail.".to_string()
140            }
141            WorkflowStage::Synthesis => {
142                "Evaluating code quality and identifying improvement opportunities.".to_string()
143            }
144        }
145    }
146
147    /// Add an immediate suggestion
148    pub fn add_suggestion(mut self, suggestion: ToolSuggestion) -> Self {
149        self.immediate_suggestions.push(suggestion);
150        self
151    }
152
153    /// Add an alternative suggestion
154    pub fn add_alternative(mut self, suggestion: ToolSuggestion) -> Self {
155        self.alternatives.push(suggestion);
156        self
157    }
158
159    /// Sort suggestions by priority
160    pub fn sort_by_priority(mut self) -> Self {
161        self.immediate_suggestions.sort_by_key(|s| s.priority);
162        self.alternatives.sort_by_key(|s| s.priority);
163        self
164    }
165}
166
167/// Workflow context that tracks current state and provides guidance
168#[derive(Debug)]
169pub struct WorkflowContext {
170    /// Session state reference
171    session_state: SessionState,
172}
173
174impl WorkflowContext {
175    /// Create new workflow context from session state
176    pub fn new(session_state: SessionState) -> Self {
177        Self { session_state }
178    }
179
180    /// Generate workflow suggestions based on current state
181    pub fn generate_suggestions(&self) -> Result<WorkflowSuggestion> {
182        let stage = &self.session_state.current_stage;
183        let workflow_guidance = match stage {
184            WorkflowStage::Discovery => {
185                "Focus on understanding the overall structure and scope of the codebase."
186            }
187            WorkflowStage::Mapping => {
188                "Map relationships between components and understand architecture."
189            }
190            WorkflowStage::DeepDive => {
191                "Dive deep into specific areas of interest and analyze patterns."
192            }
193            WorkflowStage::Synthesis => {
194                "Synthesize findings with quality analysis and security review."
195            }
196        };
197
198        // Generate basic suggestions based on the workflow stage
199        let immediate_suggestions = match stage {
200            WorkflowStage::Discovery => vec![ToolSuggestion::new(
201                "repository_stats".to_string(),
202                ConfidenceLevel::High,
203                "Get an overview of the repository structure".to_string(),
204                "Understanding of codebase size and organization".to_string(),
205                1,
206            )],
207            WorkflowStage::Mapping => vec![ToolSuggestion::new(
208                "search_symbols".to_string(),
209                ConfidenceLevel::High,
210                "Find key symbols in the codebase".to_string(),
211                "Discovery of main classes and functions".to_string(),
212                1,
213            )],
214            WorkflowStage::DeepDive => vec![ToolSuggestion::new(
215                "explain_symbol".to_string(),
216                ConfidenceLevel::High,
217                "Analyze specific symbols in detail".to_string(),
218                "Deep understanding of symbol implementation".to_string(),
219                1,
220            )],
221            WorkflowStage::Synthesis => vec![ToolSuggestion::new(
222                "analyze_complexity".to_string(),
223                ConfidenceLevel::High,
224                "Evaluate code complexity and quality".to_string(),
225                "Assessment of code maintainability".to_string(),
226                1,
227            )],
228        };
229
230        Ok(WorkflowSuggestion {
231            current_stage: stage.clone(),
232            next_stage: stage.next_stage(),
233            immediate_suggestions,
234            alternatives: vec![],
235            workflow_guidance: workflow_guidance.to_string(),
236            progress_assessment: "Analysis in progress".to_string(),
237        })
238    }
239
240    /// Get suggestions for a specific symbol
241    pub fn suggest_for_symbol(&self, symbol_id: &str) -> Result<Vec<ToolSuggestion>> {
242        let mut suggestions = Vec::new();
243
244        // Always suggest explain_symbol for detailed understanding
245        suggestions.push(
246            ToolSuggestion::new(
247                "explain_symbol".to_string(),
248                ConfidenceLevel::High,
249                format!("Get detailed explanation of symbol {}", symbol_id),
250                "Complete understanding of symbol implementation and context".to_string(),
251                1,
252            )
253            .with_parameter(
254                "symbol_id".to_string(),
255                serde_json::Value::String(symbol_id.to_string()),
256            )
257            .with_parameter(
258                "include_dependencies".to_string(),
259                serde_json::Value::Bool(true),
260            )
261            .with_parameter("include_usages".to_string(), serde_json::Value::Bool(true)),
262        );
263
264        // Suggest finding references
265        suggestions.push(
266            ToolSuggestion::new(
267                "find_references".to_string(),
268                ConfidenceLevel::High,
269                format!("Find all references to symbol {}", symbol_id),
270                "Understanding of how and where the symbol is used".to_string(),
271                2,
272            )
273            .with_parameter(
274                "symbol_id".to_string(),
275                serde_json::Value::String(symbol_id.to_string()),
276            ),
277        );
278
279        // Suggest dependency analysis
280        suggestions.push(
281            ToolSuggestion::new(
282                "find_dependencies".to_string(),
283                ConfidenceLevel::Medium,
284                format!("Analyze dependencies for symbol {}", symbol_id),
285                "Understanding of what the symbol depends on".to_string(),
286                3,
287            )
288            .with_parameter(
289                "target".to_string(),
290                serde_json::Value::String(symbol_id.to_string()),
291            ),
292        );
293
294        Ok(suggestions)
295    }
296
297    /// Get alternative workflow approaches
298    pub fn suggest_alternatives(&self) -> Result<Vec<ToolSuggestion>> {
299        let current_stage = &self.session_state.current_stage;
300
301        match current_stage {
302            WorkflowStage::Discovery => Ok(vec![ToolSuggestion::new(
303                "search_content".to_string(),
304                ConfidenceLevel::Medium,
305                "Search for specific terms or concepts in the codebase".to_string(),
306                "Targeted discovery of relevant code sections".to_string(),
307                1,
308            )]),
309            WorkflowStage::Mapping => Ok(vec![ToolSuggestion::new(
310                "trace_path".to_string(),
311                ConfidenceLevel::Medium,
312                "Find connections between discovered symbols".to_string(),
313                "Understanding of execution paths and symbol relationships".to_string(),
314                1,
315            )]),
316            WorkflowStage::DeepDive => Ok(vec![ToolSuggestion::new(
317                "analyze_transitive_dependencies".to_string(),
318                ConfidenceLevel::Medium,
319                "Analyze complex dependency chains".to_string(),
320                "Understanding of indirect dependencies and potential cycles".to_string(),
321                1,
322            )]),
323            WorkflowStage::Synthesis => Ok(vec![ToolSuggestion::new(
324                "analyze_performance".to_string(),
325                ConfidenceLevel::Medium,
326                "Evaluate performance characteristics of the code".to_string(),
327                "Performance assessment and optimization opportunities".to_string(),
328                1,
329            )]),
330        }
331    }
332
333    /// Suggest batch analysis for current workflow stage
334    pub fn suggest_batch_analysis(&self) -> Result<Vec<String>> {
335        let current_stage = &self.session_state.current_stage;
336
337        match current_stage {
338            WorkflowStage::Discovery => Ok(vec![
339                "repository_stats".to_string(),
340                "search_symbols".to_string(),
341                "content_stats".to_string(),
342            ]),
343            WorkflowStage::Mapping => Ok(vec![
344                "trace_path".to_string(),
345                "find_dependencies".to_string(),
346                "detect_patterns".to_string(),
347            ]),
348            WorkflowStage::DeepDive => Ok(vec![
349                "explain_symbol".to_string(),
350                "trace_data_flow".to_string(),
351                "find_references".to_string(),
352            ]),
353            WorkflowStage::Synthesis => Ok(vec![
354                "analyze_complexity".to_string(),
355                "analyze_security".to_string(),
356                "analyze_performance".to_string(),
357            ]),
358        }
359    }
360
361    /// Assess workflow completion and suggest next stage
362    pub fn assess_stage_completion(&self) -> Result<WorkflowStageAssessment> {
363        let current_stage = &self.session_state.current_stage;
364        let completed_tools = &self.session_state.history;
365
366        // Count relevant tools completed for current stage
367        let stage_tools = self.suggest_batch_analysis()?;
368        let completed_stage_tools = completed_tools
369            .records
370            .iter()
371            .filter(|entry| stage_tools.contains(&entry.tool_name) && entry.success)
372            .count();
373
374        let completion_percentage = if stage_tools.is_empty() {
375            100.0
376        } else {
377            (completed_stage_tools as f64 / stage_tools.len() as f64) * 100.0
378        };
379
380        let is_ready_for_next = completion_percentage >= 60.0;
381        let next_stage = if is_ready_for_next {
382            current_stage.next_stage()
383        } else {
384            None
385        };
386
387        Ok(WorkflowStageAssessment {
388            current_stage: current_stage.clone(),
389            completion_percentage,
390            completed_tools: completed_stage_tools,
391            total_stage_tools: stage_tools.len(),
392            is_ready_for_next_stage: is_ready_for_next,
393            next_stage,
394            missing_tools: stage_tools
395                .into_iter()
396                .filter(|tool| {
397                    !completed_tools
398                        .records
399                        .iter()
400                        .any(|entry| &entry.tool_name == tool && entry.success)
401                })
402                .collect(),
403            recommendations: generate_stage_recommendations(current_stage, completion_percentage),
404        })
405    }
406
407    /// Generate workflow optimization suggestions
408    pub fn suggest_workflow_optimizations(&self) -> Result<Vec<WorkflowOptimization>> {
409        let analysis_history = &self.session_state.history;
410        let mut optimizations = Vec::new();
411
412        // Check for potential parallelization
413        let analysis_tools = analysis_history
414            .records
415            .iter()
416            .filter(|entry| {
417                [
418                    "analyze_complexity",
419                    "analyze_security",
420                    "analyze_performance",
421                ]
422                .contains(&entry.tool_name.as_str())
423            })
424            .collect::<Vec<_>>();
425
426        if analysis_tools.len() > 1 {
427            optimizations.push(WorkflowOptimization {
428                optimization_type: "parallelization".to_string(),
429                description: "Analysis tools can be run in parallel for better performance"
430                    .to_string(),
431                affected_tools: analysis_tools.iter().map(|e| e.tool_name.clone()).collect(),
432                estimated_time_savings: 30.0,
433                implementation: "Use batch_analysis tool with parallel execution strategy"
434                    .to_string(),
435            });
436        }
437
438        // Check for redundant tool calls
439        let mut tool_counts = std::collections::HashMap::new();
440        for entry in &analysis_history.records {
441            *tool_counts.entry(&entry.tool_name).or_insert(0) += 1;
442        }
443
444        for (tool_name, count) in tool_counts {
445            if count > 2 {
446                optimizations.push(WorkflowOptimization {
447                    optimization_type: "deduplication".to_string(),
448                    description: format!(
449                        "{} has been called {} times - consider caching results",
450                        tool_name, count
451                    ),
452                    affected_tools: vec![tool_name.clone()],
453                    estimated_time_savings: 15.0,
454                    implementation: "Enable result caching to avoid redundant analysis".to_string(),
455                });
456            }
457        }
458
459        Ok(optimizations)
460    }
461}
462
463/// Workflow stage completion assessment
464#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct WorkflowStageAssessment {
466    /// Current workflow stage
467    pub current_stage: WorkflowStage,
468    /// Completion percentage (0-100)
469    pub completion_percentage: f64,
470    /// Number of completed tools for this stage
471    pub completed_tools: usize,
472    /// Total tools recommended for this stage
473    pub total_stage_tools: usize,
474    /// Whether ready to progress to next stage
475    pub is_ready_for_next_stage: bool,
476    /// Next recommended stage
477    pub next_stage: Option<WorkflowStage>,
478    /// Tools still missing for stage completion
479    pub missing_tools: Vec<String>,
480    /// Stage-specific recommendations
481    pub recommendations: Vec<String>,
482}
483
484/// Workflow optimization suggestion
485#[derive(Debug, Clone, Serialize, Deserialize)]
486pub struct WorkflowOptimization {
487    /// Type of optimization
488    pub optimization_type: String,
489    /// Description of the optimization
490    pub description: String,
491    /// Tools affected by this optimization
492    pub affected_tools: Vec<String>,
493    /// Estimated time savings percentage
494    pub estimated_time_savings: f64,
495    /// How to implement the optimization
496    pub implementation: String,
497}
498
499/// Generate stage-specific recommendations
500fn generate_stage_recommendations(
501    stage: &WorkflowStage,
502    completion_percentage: f64,
503) -> Vec<String> {
504    let mut recommendations = Vec::new();
505
506    match stage {
507        WorkflowStage::Discovery => {
508            if completion_percentage < 50.0 {
509                recommendations.push("Start with repository_stats for overview".to_string());
510                recommendations.push("Use search_symbols to discover key components".to_string());
511            } else {
512                recommendations.push("Consider moving to Mapping stage".to_string());
513            }
514        }
515        WorkflowStage::Mapping => {
516            if completion_percentage < 50.0 {
517                recommendations.push("Use trace_path to understand relationships".to_string());
518                recommendations
519                    .push("Run detect_patterns to identify architectural patterns".to_string());
520            } else {
521                recommendations.push("Ready for detailed analysis in DeepDive stage".to_string());
522            }
523        }
524        WorkflowStage::DeepDive => {
525            if completion_percentage < 50.0 {
526                recommendations.push("Use explain_symbol for detailed understanding".to_string());
527                recommendations.push("Trace data flow for complex operations".to_string());
528            } else {
529                recommendations.push("Consider quality analysis in Synthesis stage".to_string());
530            }
531        }
532        WorkflowStage::Synthesis => {
533            if completion_percentage < 50.0 {
534                recommendations.push("Run quality analysis tools".to_string());
535                recommendations.push("Perform security and performance analysis".to_string());
536            } else {
537                recommendations.push("Analysis complete - review findings".to_string());
538            }
539        }
540    }
541
542    recommendations
543}
544
545#[cfg(test)]
546mod tests {
547    use super::*;
548    use crate::context::session::SessionState;
549
550    #[test]
551    fn test_tool_suggestion_creation() {
552        let suggestion = ToolSuggestion::new(
553            "test_tool".to_string(),
554            ConfidenceLevel::High,
555            "Test reasoning".to_string(),
556            "Test outcome".to_string(),
557            1,
558        )
559        .with_parameter(
560            "param1".to_string(),
561            serde_json::Value::String("value1".to_string()),
562        );
563
564        assert_eq!(suggestion.tool_name, "test_tool");
565        assert_eq!(suggestion.confidence, ConfidenceLevel::High);
566        assert_eq!(suggestion.priority, 1);
567        assert!(suggestion.suggested_parameters.contains_key("param1"));
568    }
569
570    #[test]
571    fn test_workflow_suggestion_creation() {
572        let suggestion = WorkflowSuggestion::for_stage(WorkflowStage::Discovery);
573        assert_eq!(suggestion.current_stage, WorkflowStage::Discovery);
574        assert_eq!(suggestion.next_stage, Some(WorkflowStage::Mapping));
575        assert!(!suggestion.workflow_guidance.is_empty());
576    }
577
578    #[test]
579    fn test_workflow_context() {
580        let session = SessionState::new();
581        let context = WorkflowContext::new(session);
582
583        let suggestions = context.generate_suggestions().unwrap();
584        assert_eq!(suggestions.current_stage, WorkflowStage::Discovery);
585        assert!(!suggestions.immediate_suggestions.is_empty());
586    }
587
588    #[test]
589    fn test_symbol_suggestions() {
590        let session = SessionState::new();
591        let context = WorkflowContext::new(session);
592
593        let suggestions = context.suggest_for_symbol("test_symbol").unwrap();
594        assert!(!suggestions.is_empty());
595        assert!(suggestions.iter().any(|s| s.tool_name == "explain_symbol"));
596    }
597
598    #[test]
599    fn test_confidence_ordering() {
600        assert!(ConfidenceLevel::High > ConfidenceLevel::Medium);
601        assert!(ConfidenceLevel::Medium > ConfidenceLevel::Low);
602    }
603}