Skip to main content

aster/agents/specialized/
plan.rs

1//! Plan Agent
2//!
3//! Specialized agent for implementation planning with
4//! requirements analysis, risk assessment, and step generation.
5//!
6//! This module implements Requirements 14.1-14.7 from the design document.
7//! Key feature: operates in read-only mode without modifying files.
8
9use serde::{Deserialize, Serialize};
10use std::path::{Path, PathBuf};
11use thiserror::Error;
12
13use super::{ExploreAgent, ExploreOptions, ThoroughnessLevel};
14
15/// Result type alias for plan operations
16pub type PlanResult<T> = Result<T, PlanError>;
17
18/// Error types for plan operations
19#[derive(Debug, Error)]
20pub enum PlanError {
21    /// Invalid task
22    #[error("Invalid task: {0}")]
23    InvalidTask(String),
24
25    /// File not found
26    #[error("File not found: {0}")]
27    FileNotFound(String),
28
29    /// Analysis error
30    #[error("Analysis error: {0}")]
31    AnalysisError(String),
32
33    /// I/O error
34    #[error("IO error: {0}")]
35    Io(#[from] std::io::Error),
36
37    /// Explore error
38    #[error("Explore error: {0}")]
39    ExploreError(String),
40}
41
42/// Complexity level for implementation
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
44#[serde(rename_all = "camelCase")]
45pub enum Complexity {
46    /// Trivial - simple changes, minimal risk
47    Trivial,
48
49    /// Low - straightforward implementation
50    Low,
51
52    /// Medium - moderate complexity
53    #[default]
54    Medium,
55
56    /// High - complex implementation with multiple components
57    High,
58
59    /// Very High - significant architectural changes
60    VeryHigh,
61}
62
63impl Complexity {
64    /// Get estimated hours multiplier
65    pub fn hours_multiplier(&self) -> f32 {
66        match self {
67            Complexity::Trivial => 0.5,
68            Complexity::Low => 1.0,
69            Complexity::Medium => 2.0,
70            Complexity::High => 4.0,
71            Complexity::VeryHigh => 8.0,
72        }
73    }
74
75    /// Get description
76    pub fn description(&self) -> &'static str {
77        match self {
78            Complexity::Trivial => "Simple changes with minimal risk",
79            Complexity::Low => "Straightforward implementation",
80            Complexity::Medium => "Moderate complexity with some considerations",
81            Complexity::High => "Complex implementation with multiple components",
82            Complexity::VeryHigh => "Significant architectural changes required",
83        }
84    }
85}
86
87/// Risk severity level
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
89#[serde(rename_all = "camelCase")]
90pub enum RiskSeverity {
91    /// Low risk - minimal impact if occurs
92    Low,
93
94    /// Medium risk - moderate impact
95    #[default]
96    Medium,
97
98    /// High risk - significant impact
99    High,
100
101    /// Critical risk - severe impact, must be addressed
102    Critical,
103}
104
105/// Risk category
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
107#[serde(rename_all = "camelCase")]
108pub enum RiskCategory {
109    /// Technical risk - implementation challenges
110    #[default]
111    Technical,
112
113    /// Security risk - potential vulnerabilities
114    Security,
115
116    /// Performance risk - potential performance issues
117    Performance,
118
119    /// Compatibility risk - breaking changes
120    Compatibility,
121
122    /// Dependency risk - external dependency issues
123    Dependency,
124
125    /// Testing risk - testing challenges
126    Testing,
127
128    /// Other risk
129    Other(String),
130}
131
132/// A risk identified during planning
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
134#[serde(rename_all = "camelCase")]
135pub struct Risk {
136    /// Risk identifier
137    pub id: String,
138
139    /// Risk description
140    pub description: String,
141
142    /// Risk category
143    pub category: RiskCategory,
144
145    /// Risk severity
146    pub severity: RiskSeverity,
147
148    /// Likelihood (0.0 - 1.0)
149    pub likelihood: f32,
150
151    /// Impact description
152    pub impact: String,
153
154    /// Mitigation strategies
155    pub mitigation: Vec<String>,
156
157    /// Related files
158    pub related_files: Vec<PathBuf>,
159}
160
161impl Risk {
162    /// Create a new risk
163    pub fn new(
164        id: impl Into<String>,
165        description: impl Into<String>,
166        category: RiskCategory,
167        severity: RiskSeverity,
168    ) -> Self {
169        Self {
170            id: id.into(),
171            description: description.into(),
172            category,
173            severity,
174            likelihood: 0.5,
175            impact: String::new(),
176            mitigation: Vec::new(),
177            related_files: Vec::new(),
178        }
179    }
180
181    /// Set likelihood
182    pub fn with_likelihood(mut self, likelihood: f32) -> Self {
183        self.likelihood = likelihood.clamp(0.0, 1.0);
184        self
185    }
186
187    /// Set impact
188    pub fn with_impact(mut self, impact: impl Into<String>) -> Self {
189        self.impact = impact.into();
190        self
191    }
192
193    /// Add mitigation strategy
194    pub fn with_mitigation(mut self, mitigation: Vec<String>) -> Self {
195        self.mitigation = mitigation;
196        self
197    }
198
199    /// Add related files
200    pub fn with_related_files(mut self, files: Vec<PathBuf>) -> Self {
201        self.related_files = files;
202        self
203    }
204}
205
206/// A critical file identified for implementation
207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
208#[serde(rename_all = "camelCase")]
209pub struct CriticalFile {
210    /// File path
211    pub path: PathBuf,
212
213    /// Reason why this file is critical
214    pub reason: String,
215
216    /// Type of modification needed
217    pub modification_type: ModificationType,
218
219    /// Priority (1-10, higher is more important)
220    pub priority: u8,
221
222    /// Dependencies on other files
223    pub dependencies: Vec<PathBuf>,
224
225    /// Estimated lines of change
226    pub estimated_changes: Option<usize>,
227}
228
229impl CriticalFile {
230    /// Create a new critical file
231    pub fn new(
232        path: impl Into<PathBuf>,
233        reason: impl Into<String>,
234        modification_type: ModificationType,
235    ) -> Self {
236        Self {
237            path: path.into(),
238            reason: reason.into(),
239            modification_type,
240            priority: 5,
241            dependencies: Vec::new(),
242            estimated_changes: None,
243        }
244    }
245
246    /// Set priority
247    pub fn with_priority(mut self, priority: u8) -> Self {
248        self.priority = priority.min(10);
249        self
250    }
251
252    /// Set dependencies
253    pub fn with_dependencies(mut self, deps: Vec<PathBuf>) -> Self {
254        self.dependencies = deps;
255        self
256    }
257
258    /// Set estimated changes
259    pub fn with_estimated_changes(mut self, changes: usize) -> Self {
260        self.estimated_changes = Some(changes);
261        self
262    }
263}
264
265/// Type of modification needed for a file
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
267#[serde(rename_all = "camelCase")]
268pub enum ModificationType {
269    /// Create new file
270    Create,
271
272    /// Modify existing file
273    #[default]
274    Modify,
275
276    /// Delete file
277    Delete,
278
279    /// Rename file
280    Rename,
281
282    /// Review only (no changes)
283    Review,
284}
285
286/// An implementation step in the plan
287#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
288#[serde(rename_all = "camelCase")]
289pub struct PlanStep {
290    /// Step number
291    pub step_number: usize,
292
293    /// Step title
294    pub title: String,
295
296    /// Detailed description
297    pub description: String,
298
299    /// Files involved
300    pub files: Vec<PathBuf>,
301
302    /// Dependencies on other steps (by step number)
303    pub dependencies: Vec<usize>,
304
305    /// Estimated duration in hours
306    pub estimated_hours: Option<f32>,
307
308    /// Whether this step is optional
309    pub optional: bool,
310
311    /// Verification criteria
312    pub verification: Vec<String>,
313}
314
315impl PlanStep {
316    /// Create a new plan step
317    pub fn new(
318        step_number: usize,
319        title: impl Into<String>,
320        description: impl Into<String>,
321    ) -> Self {
322        Self {
323            step_number,
324            title: title.into(),
325            description: description.into(),
326            files: Vec::new(),
327            dependencies: Vec::new(),
328            estimated_hours: None,
329            optional: false,
330            verification: Vec::new(),
331        }
332    }
333
334    /// Add files
335    pub fn with_files(mut self, files: Vec<PathBuf>) -> Self {
336        self.files = files;
337        self
338    }
339
340    /// Add dependencies
341    pub fn with_dependencies(mut self, deps: Vec<usize>) -> Self {
342        self.dependencies = deps;
343        self
344    }
345
346    /// Set estimated hours
347    pub fn with_estimated_hours(mut self, hours: f32) -> Self {
348        self.estimated_hours = Some(hours);
349        self
350    }
351
352    /// Mark as optional
353    pub fn as_optional(mut self) -> Self {
354        self.optional = true;
355        self
356    }
357
358    /// Add verification criteria
359    pub fn with_verification(mut self, criteria: Vec<String>) -> Self {
360        self.verification = criteria;
361        self
362    }
363}
364
365/// An alternative implementation approach
366#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
367#[serde(rename_all = "camelCase")]
368pub struct Alternative {
369    /// Alternative identifier
370    pub id: String,
371
372    /// Alternative name
373    pub name: String,
374
375    /// Description of the approach
376    pub description: String,
377
378    /// Pros of this approach
379    pub pros: Vec<String>,
380
381    /// Cons of this approach
382    pub cons: Vec<String>,
383
384    /// Estimated complexity
385    pub complexity: Complexity,
386
387    /// Estimated hours
388    pub estimated_hours: Option<f32>,
389
390    /// Whether this is the recommended approach
391    pub recommended: bool,
392}
393
394impl Alternative {
395    /// Create a new alternative
396    pub fn new(
397        id: impl Into<String>,
398        name: impl Into<String>,
399        description: impl Into<String>,
400    ) -> Self {
401        Self {
402            id: id.into(),
403            name: name.into(),
404            description: description.into(),
405            pros: Vec::new(),
406            cons: Vec::new(),
407            complexity: Complexity::Medium,
408            estimated_hours: None,
409            recommended: false,
410        }
411    }
412
413    /// Add pros
414    pub fn with_pros(mut self, pros: Vec<String>) -> Self {
415        self.pros = pros;
416        self
417    }
418
419    /// Add cons
420    pub fn with_cons(mut self, cons: Vec<String>) -> Self {
421        self.cons = cons;
422        self
423    }
424
425    /// Set complexity
426    pub fn with_complexity(mut self, complexity: Complexity) -> Self {
427        self.complexity = complexity;
428        self
429    }
430
431    /// Set estimated hours
432    pub fn with_estimated_hours(mut self, hours: f32) -> Self {
433        self.estimated_hours = Some(hours);
434        self
435    }
436
437    /// Mark as recommended
438    pub fn as_recommended(mut self) -> Self {
439        self.recommended = true;
440        self
441    }
442}
443
444/// An architectural decision
445#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
446#[serde(rename_all = "camelCase")]
447pub struct ArchitecturalDecision {
448    /// Decision identifier
449    pub id: String,
450
451    /// Decision title
452    pub title: String,
453
454    /// Context/background
455    pub context: String,
456
457    /// The decision made
458    pub decision: String,
459
460    /// Rationale for the decision
461    pub rationale: String,
462
463    /// Consequences of the decision
464    pub consequences: Vec<String>,
465
466    /// Related decisions
467    pub related_decisions: Vec<String>,
468}
469
470impl ArchitecturalDecision {
471    /// Create a new architectural decision
472    pub fn new(
473        id: impl Into<String>,
474        title: impl Into<String>,
475        decision: impl Into<String>,
476    ) -> Self {
477        Self {
478            id: id.into(),
479            title: title.into(),
480            context: String::new(),
481            decision: decision.into(),
482            rationale: String::new(),
483            consequences: Vec::new(),
484            related_decisions: Vec::new(),
485        }
486    }
487
488    /// Set context
489    pub fn with_context(mut self, context: impl Into<String>) -> Self {
490        self.context = context.into();
491        self
492    }
493
494    /// Set rationale
495    pub fn with_rationale(mut self, rationale: impl Into<String>) -> Self {
496        self.rationale = rationale.into();
497        self
498    }
499
500    /// Add consequences
501    pub fn with_consequences(mut self, consequences: Vec<String>) -> Self {
502        self.consequences = consequences;
503        self
504    }
505
506    /// Add related decisions
507    pub fn with_related_decisions(mut self, related: Vec<String>) -> Self {
508        self.related_decisions = related;
509        self
510    }
511}
512
513/// Requirements analysis result
514#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
515#[serde(rename_all = "camelCase")]
516pub struct RequirementsAnalysis {
517    /// Original task/requirements
518    pub original_task: String,
519
520    /// Parsed functional requirements
521    pub functional_requirements: Vec<String>,
522
523    /// Parsed non-functional requirements
524    pub non_functional_requirements: Vec<String>,
525
526    /// Assumptions made
527    pub assumptions: Vec<String>,
528
529    /// Questions/clarifications needed
530    pub questions: Vec<String>,
531
532    /// Scope boundaries
533    pub scope: ScopeDefinition,
534}
535
536impl RequirementsAnalysis {
537    /// Create a new requirements analysis
538    pub fn new(task: impl Into<String>) -> Self {
539        Self {
540            original_task: task.into(),
541            ..Default::default()
542        }
543    }
544
545    /// Add functional requirements
546    pub fn with_functional_requirements(mut self, reqs: Vec<String>) -> Self {
547        self.functional_requirements = reqs;
548        self
549    }
550
551    /// Add non-functional requirements
552    pub fn with_non_functional_requirements(mut self, reqs: Vec<String>) -> Self {
553        self.non_functional_requirements = reqs;
554        self
555    }
556
557    /// Add assumptions
558    pub fn with_assumptions(mut self, assumptions: Vec<String>) -> Self {
559        self.assumptions = assumptions;
560        self
561    }
562
563    /// Add questions
564    pub fn with_questions(mut self, questions: Vec<String>) -> Self {
565        self.questions = questions;
566        self
567    }
568
569    /// Set scope
570    pub fn with_scope(mut self, scope: ScopeDefinition) -> Self {
571        self.scope = scope;
572        self
573    }
574}
575
576/// Scope definition for the implementation
577#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
578#[serde(rename_all = "camelCase")]
579pub struct ScopeDefinition {
580    /// What is included in scope
581    pub in_scope: Vec<String>,
582
583    /// What is explicitly out of scope
584    pub out_of_scope: Vec<String>,
585
586    /// Future considerations
587    pub future_considerations: Vec<String>,
588}
589
590impl ScopeDefinition {
591    /// Create a new scope definition
592    pub fn new() -> Self {
593        Self::default()
594    }
595
596    /// Set in-scope items
597    pub fn with_in_scope(mut self, items: Vec<String>) -> Self {
598        self.in_scope = items;
599        self
600    }
601
602    /// Set out-of-scope items
603    pub fn with_out_of_scope(mut self, items: Vec<String>) -> Self {
604        self.out_of_scope = items;
605        self
606    }
607
608    /// Set future considerations
609    pub fn with_future_considerations(mut self, items: Vec<String>) -> Self {
610        self.future_considerations = items;
611        self
612    }
613}
614
615/// Options for plan operations
616#[derive(Debug, Clone, Serialize, Deserialize)]
617#[serde(rename_all = "camelCase")]
618pub struct PlanOptions {
619    /// The task or feature to plan
620    pub task: String,
621
622    /// Additional context for planning
623    pub context: Option<String>,
624
625    /// Constraints to consider
626    pub constraints: Option<Vec<String>>,
627
628    /// Existing code paths to consider
629    pub existing_code: Option<Vec<PathBuf>>,
630
631    /// Perspective for planning (e.g., "security", "performance")
632    pub perspective: Option<String>,
633
634    /// Thoroughness level for analysis
635    pub thoroughness: ThoroughnessLevel,
636
637    /// Working directory (for read-only file access)
638    pub working_directory: Option<PathBuf>,
639}
640
641impl Default for PlanOptions {
642    fn default() -> Self {
643        Self {
644            task: String::new(),
645            context: None,
646            constraints: None,
647            existing_code: None,
648            perspective: None,
649            thoroughness: ThoroughnessLevel::Medium,
650            working_directory: None,
651        }
652    }
653}
654
655impl PlanOptions {
656    /// Create new plan options with a task
657    pub fn new(task: impl Into<String>) -> Self {
658        Self {
659            task: task.into(),
660            ..Default::default()
661        }
662    }
663
664    /// Set additional context
665    pub fn with_context(mut self, context: impl Into<String>) -> Self {
666        self.context = Some(context.into());
667        self
668    }
669
670    /// Set constraints
671    pub fn with_constraints(mut self, constraints: Vec<String>) -> Self {
672        self.constraints = Some(constraints);
673        self
674    }
675
676    /// Set existing code paths
677    pub fn with_existing_code(mut self, paths: Vec<PathBuf>) -> Self {
678        self.existing_code = Some(paths);
679        self
680    }
681
682    /// Set perspective
683    pub fn with_perspective(mut self, perspective: impl Into<String>) -> Self {
684        self.perspective = Some(perspective.into());
685        self
686    }
687
688    /// Set thoroughness level
689    pub fn with_thoroughness(mut self, level: ThoroughnessLevel) -> Self {
690        self.thoroughness = level;
691        self
692    }
693
694    /// Set working directory
695    pub fn with_working_directory(mut self, dir: impl Into<PathBuf>) -> Self {
696        self.working_directory = Some(dir.into());
697        self
698    }
699}
700
701/// Result of a planning operation
702#[derive(Debug, Clone, Serialize, Deserialize)]
703#[serde(rename_all = "camelCase")]
704pub struct PlanResultData {
705    /// Summary of the plan
706    pub summary: String,
707
708    /// Requirements analysis
709    pub requirements_analysis: RequirementsAnalysis,
710
711    /// Architectural decisions
712    pub architectural_decisions: Vec<ArchitecturalDecision>,
713
714    /// Implementation steps
715    pub steps: Vec<PlanStep>,
716
717    /// Critical files identified
718    pub critical_files: Vec<CriticalFile>,
719
720    /// Identified risks
721    pub risks: Vec<Risk>,
722
723    /// Alternative approaches
724    pub alternatives: Vec<Alternative>,
725
726    /// Estimated complexity
727    pub estimated_complexity: Complexity,
728
729    /// Estimated hours for implementation
730    pub estimated_hours: Option<f32>,
731
732    /// Additional recommendations
733    pub recommendations: Option<Vec<String>>,
734}
735
736impl Default for PlanResultData {
737    fn default() -> Self {
738        Self {
739            summary: String::new(),
740            requirements_analysis: RequirementsAnalysis::default(),
741            architectural_decisions: Vec::new(),
742            steps: Vec::new(),
743            critical_files: Vec::new(),
744            risks: Vec::new(),
745            alternatives: Vec::new(),
746            estimated_complexity: Complexity::Medium,
747            estimated_hours: None,
748            recommendations: None,
749        }
750    }
751}
752
753impl PlanResultData {
754    /// Create a new plan result
755    pub fn new() -> Self {
756        Self::default()
757    }
758
759    /// Set summary
760    pub fn with_summary(mut self, summary: impl Into<String>) -> Self {
761        self.summary = summary.into();
762        self
763    }
764
765    /// Set requirements analysis
766    pub fn with_requirements_analysis(mut self, analysis: RequirementsAnalysis) -> Self {
767        self.requirements_analysis = analysis;
768        self
769    }
770
771    /// Set architectural decisions
772    pub fn with_architectural_decisions(mut self, decisions: Vec<ArchitecturalDecision>) -> Self {
773        self.architectural_decisions = decisions;
774        self
775    }
776
777    /// Set implementation steps
778    pub fn with_steps(mut self, steps: Vec<PlanStep>) -> Self {
779        self.steps = steps;
780        self
781    }
782
783    /// Set critical files
784    pub fn with_critical_files(mut self, files: Vec<CriticalFile>) -> Self {
785        self.critical_files = files;
786        self
787    }
788
789    /// Set risks
790    pub fn with_risks(mut self, risks: Vec<Risk>) -> Self {
791        self.risks = risks;
792        self
793    }
794
795    /// Set alternatives
796    pub fn with_alternatives(mut self, alternatives: Vec<Alternative>) -> Self {
797        self.alternatives = alternatives;
798        self
799    }
800
801    /// Set estimated complexity
802    pub fn with_estimated_complexity(mut self, complexity: Complexity) -> Self {
803        self.estimated_complexity = complexity;
804        self
805    }
806
807    /// Set estimated hours
808    pub fn with_estimated_hours(mut self, hours: f32) -> Self {
809        self.estimated_hours = Some(hours);
810        self
811    }
812
813    /// Set recommendations
814    pub fn with_recommendations(mut self, recommendations: Vec<String>) -> Self {
815        self.recommendations = Some(recommendations);
816        self
817    }
818
819    /// Calculate total estimated hours from steps
820    pub fn calculate_total_hours(&self) -> f32 {
821        self.steps.iter().filter_map(|s| s.estimated_hours).sum()
822    }
823}
824
825/// Plan Agent for implementation planning
826///
827/// Provides functionality for:
828/// - Requirements analysis
829/// - Risk assessment
830/// - Implementation step generation
831/// - Alternative approach generation
832///
833/// IMPORTANT: This agent operates in READ-ONLY mode.
834/// It does not modify any files in the target directory.
835pub struct PlanAgent {
836    options: PlanOptions,
837    /// Track files that were read (for verification)
838    files_read: std::cell::RefCell<Vec<PathBuf>>,
839}
840
841impl PlanAgent {
842    /// Create a new plan agent with options
843    pub fn new(options: PlanOptions) -> Self {
844        Self {
845            options,
846            files_read: std::cell::RefCell::new(Vec::new()),
847        }
848    }
849
850    /// Get the options
851    pub fn options(&self) -> &PlanOptions {
852        &self.options
853    }
854
855    /// Get the effective working directory
856    fn working_directory(&self) -> PathBuf {
857        self.options
858            .working_directory
859            .clone()
860            .unwrap_or_else(|| PathBuf::from("."))
861    }
862
863    /// Get list of files that were read (for testing read-only mode)
864    pub fn files_read(&self) -> Vec<PathBuf> {
865        self.files_read.borrow().clone()
866    }
867
868    /// Record a file read operation
869    fn record_file_read(&self, path: &Path) {
870        self.files_read.borrow_mut().push(path.to_path_buf());
871    }
872
873    /// Read file content (read-only operation)
874    fn read_file_content(&self, path: &Path) -> PlanResult<String> {
875        if !path.exists() {
876            return Err(PlanError::FileNotFound(path.display().to_string()));
877        }
878        self.record_file_read(path);
879        std::fs::read_to_string(path).map_err(PlanError::from)
880    }
881
882    /// Create a comprehensive implementation plan
883    pub async fn create_plan(&self) -> PlanResult<PlanResultData> {
884        if self.options.task.trim().is_empty() {
885            return Err(PlanError::InvalidTask("Task cannot be empty".to_string()));
886        }
887
888        // Analyze requirements
889        let requirements_analysis = self.analyze_requirements().await?;
890
891        // Identify critical files
892        let critical_files = self.identify_files().await?;
893
894        // Assess risks
895        let risks = self.assess_risks().await?;
896
897        // Generate alternatives
898        let alternatives = self.generate_alternatives().await?;
899
900        // Generate implementation steps
901        let steps = self.generate_steps(&critical_files, &risks);
902
903        // Calculate complexity and time estimates
904        let estimated_complexity = self.estimate_complexity(&critical_files, &risks);
905        let estimated_hours = self.estimate_hours(&steps, &estimated_complexity);
906
907        // Generate architectural decisions
908        let architectural_decisions = self.generate_architectural_decisions(&requirements_analysis);
909
910        // Generate summary
911        let summary = self.generate_summary(
912            &requirements_analysis,
913            &critical_files,
914            &risks,
915            &estimated_complexity,
916        );
917
918        // Generate recommendations
919        let recommendations = self.generate_recommendations(&risks, &alternatives);
920
921        Ok(PlanResultData::new()
922            .with_summary(summary)
923            .with_requirements_analysis(requirements_analysis)
924            .with_architectural_decisions(architectural_decisions)
925            .with_steps(steps)
926            .with_critical_files(critical_files)
927            .with_risks(risks)
928            .with_alternatives(alternatives)
929            .with_estimated_complexity(estimated_complexity)
930            .with_estimated_hours(estimated_hours)
931            .with_recommendations(recommendations))
932    }
933
934    /// Analyze requirements from the task description
935    pub async fn analyze_requirements(&self) -> PlanResult<RequirementsAnalysis> {
936        let task = &self.options.task;
937
938        // Parse functional requirements from task
939        let functional_requirements = self.extract_functional_requirements(task);
940
941        // Parse non-functional requirements
942        let non_functional_requirements = self.extract_non_functional_requirements(task);
943
944        // Generate assumptions
945        let assumptions = self.generate_assumptions(task);
946
947        // Generate questions
948        let questions = self.generate_questions(task);
949
950        // Define scope
951        let scope = self.define_scope(task);
952
953        Ok(RequirementsAnalysis::new(task)
954            .with_functional_requirements(functional_requirements)
955            .with_non_functional_requirements(non_functional_requirements)
956            .with_assumptions(assumptions)
957            .with_questions(questions)
958            .with_scope(scope))
959    }
960
961    /// Extract functional requirements from task description
962    fn extract_functional_requirements(&self, task: &str) -> Vec<String> {
963        let mut requirements = Vec::new();
964        let task_lower = task.to_lowercase();
965
966        // Look for action verbs that indicate functional requirements
967        let action_patterns = [
968            ("implement", "Implement"),
969            ("create", "Create"),
970            ("add", "Add"),
971            ("build", "Build"),
972            ("develop", "Develop"),
973            ("support", "Support"),
974            ("enable", "Enable"),
975            ("allow", "Allow"),
976            ("provide", "Provide"),
977        ];
978
979        for (pattern, prefix) in action_patterns {
980            if task_lower.contains(pattern) {
981                requirements.push(format!("{} the requested functionality", prefix));
982                break;
983            }
984        }
985
986        // Add the main task as a requirement
987        if requirements.is_empty() {
988            requirements.push(format!("Complete: {}", task));
989        }
990
991        // Add context-based requirements
992        if let Some(context) = &self.options.context {
993            requirements.push(format!("Consider context: {}", context));
994        }
995
996        requirements
997    }
998
999    /// Extract non-functional requirements
1000    fn extract_non_functional_requirements(&self, task: &str) -> Vec<String> {
1001        let mut requirements = Vec::new();
1002        let task_lower = task.to_lowercase();
1003
1004        // Check for performance requirements
1005        if task_lower.contains("fast")
1006            || task_lower.contains("performance")
1007            || task_lower.contains("efficient")
1008        {
1009            requirements.push("Ensure optimal performance".to_string());
1010        }
1011
1012        // Check for security requirements
1013        if task_lower.contains("secure")
1014            || task_lower.contains("security")
1015            || task_lower.contains("auth")
1016        {
1017            requirements.push("Implement security best practices".to_string());
1018        }
1019
1020        // Check for scalability requirements
1021        if task_lower.contains("scale") || task_lower.contains("scalable") {
1022            requirements.push("Design for scalability".to_string());
1023        }
1024
1025        // Check for testing requirements
1026        if task_lower.contains("test") || task_lower.contains("testing") {
1027            requirements.push("Include comprehensive tests".to_string());
1028        }
1029
1030        // Add perspective-based requirements
1031        if let Some(perspective) = &self.options.perspective {
1032            requirements.push(format!("Focus on {} aspects", perspective));
1033        }
1034
1035        // Add constraint-based requirements
1036        if let Some(constraints) = &self.options.constraints {
1037            for constraint in constraints {
1038                requirements.push(format!("Constraint: {}", constraint));
1039            }
1040        }
1041
1042        requirements
1043    }
1044
1045    /// Generate assumptions based on task
1046    fn generate_assumptions(&self, task: &str) -> Vec<String> {
1047        let mut assumptions = Vec::new();
1048
1049        // Basic assumptions
1050        assumptions.push("Existing codebase follows established patterns".to_string());
1051        assumptions.push("Required dependencies are available".to_string());
1052
1053        // Task-specific assumptions
1054        if task.to_lowercase().contains("api") {
1055            assumptions.push("API follows RESTful conventions".to_string());
1056        }
1057
1058        if task.to_lowercase().contains("database") || task.to_lowercase().contains("db") {
1059            assumptions.push("Database schema can be modified if needed".to_string());
1060        }
1061
1062        assumptions
1063    }
1064
1065    /// Generate questions for clarification
1066    fn generate_questions(&self, task: &str) -> Vec<String> {
1067        let mut questions = Vec::new();
1068
1069        // Generic questions based on thoroughness
1070        match self.options.thoroughness {
1071            ThoroughnessLevel::VeryThorough => {
1072                questions.push("What are the expected performance requirements?".to_string());
1073                questions.push("Are there any specific security considerations?".to_string());
1074                questions.push("What is the expected timeline for completion?".to_string());
1075            }
1076            ThoroughnessLevel::Medium => {
1077                questions.push("Are there any specific constraints to consider?".to_string());
1078            }
1079            ThoroughnessLevel::Quick => {}
1080        }
1081
1082        // Task-specific questions
1083        if task.to_lowercase().contains("integration") {
1084            questions.push("What external systems need to be integrated?".to_string());
1085        }
1086
1087        questions
1088    }
1089
1090    /// Define scope boundaries
1091    fn define_scope(&self, task: &str) -> ScopeDefinition {
1092        let mut in_scope = vec![task.to_string()];
1093        let mut out_of_scope = Vec::new();
1094        let mut future_considerations = Vec::new();
1095
1096        // Add context to scope
1097        if let Some(context) = &self.options.context {
1098            in_scope.push(context.clone());
1099        }
1100
1101        // Common out-of-scope items
1102        out_of_scope.push("Major architectural changes unless required".to_string());
1103        out_of_scope.push("Unrelated feature modifications".to_string());
1104
1105        // Future considerations
1106        future_considerations.push("Performance optimization opportunities".to_string());
1107        future_considerations.push("Additional feature enhancements".to_string());
1108
1109        ScopeDefinition::new()
1110            .with_in_scope(in_scope)
1111            .with_out_of_scope(out_of_scope)
1112            .with_future_considerations(future_considerations)
1113    }
1114}
1115
1116impl PlanAgent {
1117    /// Identify critical files for implementation
1118    pub async fn identify_files(&self) -> PlanResult<Vec<CriticalFile>> {
1119        let mut critical_files = Vec::new();
1120        let working_dir = self.working_directory();
1121
1122        // If existing code paths are provided, analyze them
1123        if let Some(existing_paths) = &self.options.existing_code {
1124            for path in existing_paths {
1125                let full_path = if path.is_absolute() {
1126                    path.clone()
1127                } else {
1128                    working_dir.join(path)
1129                };
1130
1131                if full_path.exists() {
1132                    // Read file to analyze (read-only)
1133                    if let Ok(_content) = self.read_file_content(&full_path) {
1134                        let file = CriticalFile::new(
1135                            path.clone(),
1136                            "Specified in existing code paths",
1137                            ModificationType::Modify,
1138                        )
1139                        .with_priority(8);
1140                        critical_files.push(file);
1141                    }
1142                }
1143            }
1144        }
1145
1146        // Use explore agent to find relevant files based on task keywords
1147        let keywords = self.extract_keywords(&self.options.task);
1148        if !keywords.is_empty() && working_dir.exists() {
1149            let explore_options = ExploreOptions::new(keywords.join(" "))
1150                .with_target_path(&working_dir)
1151                .with_thoroughness(self.options.thoroughness)
1152                .with_max_results(self.options.thoroughness.max_files() / 4);
1153
1154            let explore_agent = ExploreAgent::new(explore_options);
1155            if let Ok(result) = explore_agent.explore().await {
1156                for file_path in result.files.iter().take(10) {
1157                    // Record that we read this file
1158                    self.record_file_read(file_path);
1159
1160                    let file = CriticalFile::new(
1161                        file_path.clone(),
1162                        "Found via keyword search",
1163                        ModificationType::Review,
1164                    )
1165                    .with_priority(5);
1166
1167                    // Avoid duplicates
1168                    if !critical_files.iter().any(|f| f.path == file.path) {
1169                        critical_files.push(file);
1170                    }
1171                }
1172            }
1173        }
1174
1175        // Sort by priority
1176        critical_files.sort_by(|a, b| b.priority.cmp(&a.priority));
1177
1178        Ok(critical_files)
1179    }
1180
1181    /// Extract keywords from task description
1182    fn extract_keywords(&self, task: &str) -> Vec<String> {
1183        let stop_words = [
1184            "the", "a", "an", "is", "are", "was", "were", "be", "been", "being", "have", "has",
1185            "had", "do", "does", "did", "will", "would", "could", "should", "may", "might", "must",
1186            "shall", "can", "need", "to", "of", "in", "for", "on", "with", "at", "by", "from",
1187            "as", "into", "through", "and", "or", "but", "if", "then", "else", "when", "where",
1188            "why", "how", "all", "each", "every", "both", "few", "more", "most", "other", "some",
1189            "such", "no", "not", "only", "own", "same", "so", "than", "too", "very", "just",
1190            "also", "now", "here", "there", "this", "that", "these", "those",
1191        ];
1192
1193        task.split_whitespace()
1194            .map(|w| w.to_lowercase())
1195            .map(|w| w.trim_matches(|c: char| !c.is_alphanumeric()).to_string())
1196            .filter(|w| w.len() > 2 && !stop_words.contains(&w.as_str()))
1197            .take(5)
1198            .collect()
1199    }
1200
1201    /// Assess risks for the implementation
1202    pub async fn assess_risks(&self) -> PlanResult<Vec<Risk>> {
1203        let mut risks = Vec::new();
1204        let task_lower = self.options.task.to_lowercase();
1205
1206        // Technical risks
1207        if task_lower.contains("refactor") || task_lower.contains("rewrite") {
1208            risks.push(
1209                Risk::new(
1210                    "R001",
1211                    "Refactoring may introduce regressions",
1212                    RiskCategory::Technical,
1213                    RiskSeverity::Medium,
1214                )
1215                .with_likelihood(0.4)
1216                .with_impact("Existing functionality may break")
1217                .with_mitigation(vec![
1218                    "Write comprehensive tests before refactoring".to_string(),
1219                    "Refactor in small, incremental steps".to_string(),
1220                    "Use feature flags for gradual rollout".to_string(),
1221                ]),
1222            );
1223        }
1224
1225        // Security risks
1226        if task_lower.contains("auth")
1227            || task_lower.contains("security")
1228            || task_lower.contains("password")
1229        {
1230            risks.push(
1231                Risk::new(
1232                    "R002",
1233                    "Security implementation may have vulnerabilities",
1234                    RiskCategory::Security,
1235                    RiskSeverity::High,
1236                )
1237                .with_likelihood(0.3)
1238                .with_impact("Potential security breach")
1239                .with_mitigation(vec![
1240                    "Follow security best practices".to_string(),
1241                    "Conduct security review".to_string(),
1242                    "Use established security libraries".to_string(),
1243                ]),
1244            );
1245        }
1246
1247        // Performance risks
1248        if task_lower.contains("performance")
1249            || task_lower.contains("optimize")
1250            || task_lower.contains("scale")
1251        {
1252            risks.push(
1253                Risk::new(
1254                    "R003",
1255                    "Performance improvements may not meet targets",
1256                    RiskCategory::Performance,
1257                    RiskSeverity::Medium,
1258                )
1259                .with_likelihood(0.3)
1260                .with_impact("System may not meet performance requirements")
1261                .with_mitigation(vec![
1262                    "Establish baseline metrics".to_string(),
1263                    "Profile before and after changes".to_string(),
1264                    "Set clear performance targets".to_string(),
1265                ]),
1266            );
1267        }
1268
1269        // Compatibility risks
1270        if task_lower.contains("api")
1271            || task_lower.contains("interface")
1272            || task_lower.contains("breaking")
1273        {
1274            risks.push(
1275                Risk::new(
1276                    "R004",
1277                    "API changes may break existing clients",
1278                    RiskCategory::Compatibility,
1279                    RiskSeverity::High,
1280                )
1281                .with_likelihood(0.4)
1282                .with_impact("Existing integrations may fail")
1283                .with_mitigation(vec![
1284                    "Version the API".to_string(),
1285                    "Provide migration guide".to_string(),
1286                    "Maintain backward compatibility where possible".to_string(),
1287                ]),
1288            );
1289        }
1290
1291        // Dependency risks
1292        if task_lower.contains("dependency")
1293            || task_lower.contains("upgrade")
1294            || task_lower.contains("library")
1295        {
1296            risks.push(
1297                Risk::new(
1298                    "R005",
1299                    "Dependency changes may cause conflicts",
1300                    RiskCategory::Dependency,
1301                    RiskSeverity::Medium,
1302                )
1303                .with_likelihood(0.3)
1304                .with_impact("Build or runtime failures")
1305                .with_mitigation(vec![
1306                    "Test dependency updates in isolation".to_string(),
1307                    "Review changelogs for breaking changes".to_string(),
1308                    "Pin dependency versions".to_string(),
1309                ]),
1310            );
1311        }
1312
1313        // Testing risks
1314        if task_lower.contains("test") || task_lower.contains("coverage") {
1315            risks.push(
1316                Risk::new(
1317                    "R006",
1318                    "Test coverage may be insufficient",
1319                    RiskCategory::Testing,
1320                    RiskSeverity::Low,
1321                )
1322                .with_likelihood(0.2)
1323                .with_impact("Bugs may go undetected")
1324                .with_mitigation(vec![
1325                    "Set coverage targets".to_string(),
1326                    "Include edge cases in tests".to_string(),
1327                    "Use property-based testing".to_string(),
1328                ]),
1329            );
1330        }
1331
1332        // Add a general risk if no specific risks identified
1333        if risks.is_empty() {
1334            risks.push(
1335                Risk::new(
1336                    "R000",
1337                    "General implementation risk",
1338                    RiskCategory::Technical,
1339                    RiskSeverity::Low,
1340                )
1341                .with_likelihood(0.2)
1342                .with_impact("Minor issues during implementation")
1343                .with_mitigation(vec![
1344                    "Follow coding standards".to_string(),
1345                    "Review code before merging".to_string(),
1346                ]),
1347            );
1348        }
1349
1350        // Sort by severity
1351        risks.sort_by(|a, b| {
1352            let severity_order = |s: &RiskSeverity| match s {
1353                RiskSeverity::Critical => 0,
1354                RiskSeverity::High => 1,
1355                RiskSeverity::Medium => 2,
1356                RiskSeverity::Low => 3,
1357            };
1358            severity_order(&a.severity).cmp(&severity_order(&b.severity))
1359        });
1360
1361        Ok(risks)
1362    }
1363}
1364
1365impl PlanAgent {
1366    /// Generate alternative implementation approaches
1367    pub async fn generate_alternatives(&self) -> PlanResult<Vec<Alternative>> {
1368        let mut alternatives = Vec::new();
1369        let task_lower = self.options.task.to_lowercase();
1370
1371        // Standard approach
1372        let standard = Alternative::new(
1373            "ALT001",
1374            "Standard Implementation",
1375            "Implement the feature using conventional patterns and practices",
1376        )
1377        .with_pros(vec![
1378            "Well-understood approach".to_string(),
1379            "Easier to maintain".to_string(),
1380            "Lower risk".to_string(),
1381        ])
1382        .with_cons(vec![
1383            "May not be optimal for all cases".to_string(),
1384            "Could be slower to implement".to_string(),
1385        ])
1386        .with_complexity(Complexity::Medium)
1387        .as_recommended();
1388
1389        alternatives.push(standard);
1390
1391        // Incremental approach
1392        if task_lower.contains("refactor")
1393            || task_lower.contains("migrate")
1394            || task_lower.contains("upgrade")
1395        {
1396            let incremental = Alternative::new(
1397                "ALT002",
1398                "Incremental Migration",
1399                "Implement changes gradually with feature flags and parallel systems",
1400            )
1401            .with_pros(vec![
1402                "Lower risk of breaking changes".to_string(),
1403                "Easier rollback".to_string(),
1404                "Can validate at each step".to_string(),
1405            ])
1406            .with_cons(vec![
1407                "Takes longer to complete".to_string(),
1408                "Temporary complexity during transition".to_string(),
1409            ])
1410            .with_complexity(Complexity::High);
1411
1412            alternatives.push(incremental);
1413        }
1414
1415        // Performance-focused approach
1416        if task_lower.contains("performance")
1417            || task_lower.contains("optimize")
1418            || task_lower.contains("fast")
1419        {
1420            let performance = Alternative::new(
1421                "ALT003",
1422                "Performance-Optimized",
1423                "Focus on performance from the start with optimized data structures and algorithms",
1424            )
1425            .with_pros(vec![
1426                "Better performance outcomes".to_string(),
1427                "Scalable from the start".to_string(),
1428            ])
1429            .with_cons(vec![
1430                "More complex implementation".to_string(),
1431                "May be premature optimization".to_string(),
1432            ])
1433            .with_complexity(Complexity::High);
1434
1435            alternatives.push(performance);
1436        }
1437
1438        // Minimal viable approach
1439        let minimal = Alternative::new(
1440            "ALT004",
1441            "Minimal Viable Implementation",
1442            "Implement only the core functionality with minimal features",
1443        )
1444        .with_pros(vec![
1445            "Fastest to implement".to_string(),
1446            "Lower initial complexity".to_string(),
1447            "Quick feedback loop".to_string(),
1448        ])
1449        .with_cons(vec![
1450            "May need significant expansion later".to_string(),
1451            "Could accumulate technical debt".to_string(),
1452        ])
1453        .with_complexity(Complexity::Low);
1454
1455        alternatives.push(minimal);
1456
1457        Ok(alternatives)
1458    }
1459
1460    /// Generate implementation steps
1461    fn generate_steps(&self, critical_files: &[CriticalFile], risks: &[Risk]) -> Vec<PlanStep> {
1462        let mut steps = Vec::new();
1463        let mut step_number = 1;
1464
1465        // Step 1: Analysis and preparation
1466        steps.push(
1467            PlanStep::new(
1468                step_number,
1469                "Analysis and Preparation",
1470                "Review existing code and understand the current implementation",
1471            )
1472            .with_files(critical_files.iter().map(|f| f.path.clone()).collect())
1473            .with_estimated_hours(1.0)
1474            .with_verification(vec![
1475                "Understand current architecture".to_string(),
1476                "Identify integration points".to_string(),
1477            ]),
1478        );
1479        step_number += 1;
1480
1481        // Step 2: Design
1482        steps.push(
1483            PlanStep::new(
1484                step_number,
1485                "Design",
1486                "Create detailed design for the implementation",
1487            )
1488            .with_dependencies(vec![1])
1489            .with_estimated_hours(2.0)
1490            .with_verification(vec![
1491                "Design document reviewed".to_string(),
1492                "Edge cases identified".to_string(),
1493            ]),
1494        );
1495        step_number += 1;
1496
1497        // Step 3: Core implementation
1498        let core_files: Vec<PathBuf> = critical_files
1499            .iter()
1500            .filter(|f| f.modification_type != ModificationType::Review)
1501            .map(|f| f.path.clone())
1502            .collect();
1503
1504        steps.push(
1505            PlanStep::new(
1506                step_number,
1507                "Core Implementation",
1508                "Implement the main functionality",
1509            )
1510            .with_files(core_files)
1511            .with_dependencies(vec![2])
1512            .with_estimated_hours(4.0)
1513            .with_verification(vec![
1514                "Core functionality works".to_string(),
1515                "Code compiles without errors".to_string(),
1516            ]),
1517        );
1518        step_number += 1;
1519
1520        // Step 4: Testing
1521        steps.push(
1522            PlanStep::new(
1523                step_number,
1524                "Testing",
1525                "Write and run tests for the implementation",
1526            )
1527            .with_dependencies(vec![3])
1528            .with_estimated_hours(2.0)
1529            .with_verification(vec![
1530                "Unit tests pass".to_string(),
1531                "Integration tests pass".to_string(),
1532                "Edge cases covered".to_string(),
1533            ]),
1534        );
1535        step_number += 1;
1536
1537        // Step 5: Risk mitigation (if high-severity risks exist)
1538        let high_risks: Vec<&Risk> = risks
1539            .iter()
1540            .filter(|r| matches!(r.severity, RiskSeverity::High | RiskSeverity::Critical))
1541            .collect();
1542
1543        if !high_risks.is_empty() {
1544            let mitigation_desc = high_risks
1545                .iter()
1546                .map(|r| format!("- {}: {}", r.id, r.description))
1547                .collect::<Vec<_>>()
1548                .join("\n");
1549
1550            steps.push(
1551                PlanStep::new(
1552                    step_number,
1553                    "Risk Mitigation",
1554                    format!(
1555                        "Address identified high-severity risks:\n{}",
1556                        mitigation_desc
1557                    ),
1558                )
1559                .with_dependencies(vec![3])
1560                .with_estimated_hours(2.0)
1561                .with_verification(
1562                    high_risks
1563                        .iter()
1564                        .map(|r| format!("Risk {} mitigated", r.id))
1565                        .collect(),
1566                ),
1567            );
1568            step_number += 1;
1569        }
1570
1571        // Step 6: Documentation
1572        steps.push(
1573            PlanStep::new(
1574                step_number,
1575                "Documentation",
1576                "Update documentation and add code comments",
1577            )
1578            .with_dependencies(vec![step_number - 1])
1579            .with_estimated_hours(1.0)
1580            .as_optional()
1581            .with_verification(vec![
1582                "README updated".to_string(),
1583                "API documentation complete".to_string(),
1584            ]),
1585        );
1586        step_number += 1;
1587
1588        // Step 7: Review and finalization
1589        steps.push(
1590            PlanStep::new(
1591                step_number,
1592                "Review and Finalization",
1593                "Code review and final adjustments",
1594            )
1595            .with_dependencies(vec![step_number - 1])
1596            .with_estimated_hours(1.0)
1597            .with_verification(vec![
1598                "Code review completed".to_string(),
1599                "All feedback addressed".to_string(),
1600            ]),
1601        );
1602
1603        steps
1604    }
1605
1606    /// Estimate complexity based on files and risks
1607    fn estimate_complexity(&self, critical_files: &[CriticalFile], risks: &[Risk]) -> Complexity {
1608        let file_count = critical_files.len();
1609        let high_risk_count = risks
1610            .iter()
1611            .filter(|r| matches!(r.severity, RiskSeverity::High | RiskSeverity::Critical))
1612            .count();
1613
1614        // Calculate complexity score
1615        let mut score = 0;
1616
1617        // File count contribution
1618        score += match file_count {
1619            0..=2 => 1,
1620            3..=5 => 2,
1621            6..=10 => 3,
1622            11..=20 => 4,
1623            _ => 5,
1624        };
1625
1626        // Risk contribution
1627        score += match high_risk_count {
1628            0 => 0,
1629            1 => 1,
1630            2..=3 => 2,
1631            _ => 3,
1632        };
1633
1634        // Thoroughness contribution
1635        score += match self.options.thoroughness {
1636            ThoroughnessLevel::Quick => 0,
1637            ThoroughnessLevel::Medium => 1,
1638            ThoroughnessLevel::VeryThorough => 2,
1639        };
1640
1641        // Map score to complexity
1642        match score {
1643            0..=2 => Complexity::Trivial,
1644            3..=4 => Complexity::Low,
1645            5..=6 => Complexity::Medium,
1646            7..=8 => Complexity::High,
1647            _ => Complexity::VeryHigh,
1648        }
1649    }
1650
1651    /// Estimate hours based on steps and complexity
1652    fn estimate_hours(&self, steps: &[PlanStep], complexity: &Complexity) -> f32 {
1653        let base_hours: f32 = steps.iter().filter_map(|s| s.estimated_hours).sum();
1654        base_hours * complexity.hours_multiplier()
1655    }
1656
1657    /// Generate architectural decisions
1658    fn generate_architectural_decisions(
1659        &self,
1660        requirements: &RequirementsAnalysis,
1661    ) -> Vec<ArchitecturalDecision> {
1662        let mut decisions = Vec::new();
1663
1664        // Decision based on task type
1665        if !requirements.functional_requirements.is_empty() {
1666            decisions.push(
1667                ArchitecturalDecision::new(
1668                    "AD001",
1669                    "Implementation Approach",
1670                    "Use modular design with clear separation of concerns",
1671                )
1672                .with_context("Need to implement new functionality while maintaining code quality")
1673                .with_rationale("Modular design allows for easier testing and maintenance")
1674                .with_consequences(vec![
1675                    "Code will be more maintainable".to_string(),
1676                    "May require additional abstraction layers".to_string(),
1677                ]),
1678            );
1679        }
1680
1681        // Decision based on constraints
1682        if let Some(constraints) = &self.options.constraints {
1683            if !constraints.is_empty() {
1684                decisions.push(
1685                    ArchitecturalDecision::new(
1686                        "AD002",
1687                        "Constraint Handling",
1688                        format!(
1689                            "Design to accommodate constraints: {}",
1690                            constraints.join(", ")
1691                        ),
1692                    )
1693                    .with_context("Implementation must work within specified constraints")
1694                    .with_rationale("Constraints define the boundaries of acceptable solutions"),
1695                );
1696            }
1697        }
1698
1699        decisions
1700    }
1701
1702    /// Generate summary
1703    fn generate_summary(
1704        &self,
1705        requirements: &RequirementsAnalysis,
1706        critical_files: &[CriticalFile],
1707        risks: &[Risk],
1708        complexity: &Complexity,
1709    ) -> String {
1710        let mut summary = String::new();
1711
1712        summary.push_str(&format!("# Implementation Plan: {}\n\n", self.options.task));
1713
1714        summary.push_str(&format!(
1715            "## Overview\n\nThis plan addresses {} functional requirements and {} non-functional requirements.\n\n",
1716            requirements.functional_requirements.len(),
1717            requirements.non_functional_requirements.len()
1718        ));
1719
1720        summary.push_str(&format!(
1721            "## Scope\n\n- {} critical files identified\n- {} risks assessed\n- Complexity: {:?} ({})\n\n",
1722            critical_files.len(),
1723            risks.len(),
1724            complexity,
1725            complexity.description()
1726        ));
1727
1728        if !risks.is_empty() {
1729            let high_risks = risks
1730                .iter()
1731                .filter(|r| matches!(r.severity, RiskSeverity::High | RiskSeverity::Critical))
1732                .count();
1733            if high_risks > 0 {
1734                summary.push_str(&format!(
1735                    "## Risk Summary\n\n⚠️ {} high-severity risks identified that require attention.\n\n",
1736                    high_risks
1737                ));
1738            }
1739        }
1740
1741        summary
1742    }
1743
1744    /// Generate recommendations
1745    fn generate_recommendations(
1746        &self,
1747        risks: &[Risk],
1748        alternatives: &[Alternative],
1749    ) -> Vec<String> {
1750        let mut recommendations = Vec::new();
1751
1752        // Risk-based recommendations
1753        for risk in risks
1754            .iter()
1755            .filter(|r| matches!(r.severity, RiskSeverity::High | RiskSeverity::Critical))
1756        {
1757            recommendations.push(format!(
1758                "Address {} before proceeding: {}",
1759                risk.id, risk.description
1760            ));
1761        }
1762
1763        // Alternative-based recommendations
1764        if let Some(recommended) = alternatives.iter().find(|a| a.recommended) {
1765            recommendations.push(format!(
1766                "Consider using '{}' approach: {}",
1767                recommended.name, recommended.description
1768            ));
1769        }
1770
1771        // General recommendations
1772        recommendations.push("Review the plan with stakeholders before implementation".to_string());
1773        recommendations.push("Set up monitoring for the implementation progress".to_string());
1774
1775        recommendations
1776    }
1777}
1778
1779#[cfg(test)]
1780mod tests {
1781    use super::*;
1782    use std::fs;
1783    use tempfile::TempDir;
1784
1785    #[test]
1786    fn test_complexity_hours_multiplier() {
1787        assert_eq!(Complexity::Trivial.hours_multiplier(), 0.5);
1788        assert_eq!(Complexity::Low.hours_multiplier(), 1.0);
1789        assert_eq!(Complexity::Medium.hours_multiplier(), 2.0);
1790        assert_eq!(Complexity::High.hours_multiplier(), 4.0);
1791        assert_eq!(Complexity::VeryHigh.hours_multiplier(), 8.0);
1792    }
1793
1794    #[test]
1795    fn test_risk_creation() {
1796        let risk = Risk::new(
1797            "R001",
1798            "Test risk",
1799            RiskCategory::Technical,
1800            RiskSeverity::High,
1801        )
1802        .with_likelihood(0.7)
1803        .with_impact("High impact")
1804        .with_mitigation(vec!["Mitigation 1".to_string()]);
1805
1806        assert_eq!(risk.id, "R001");
1807        assert_eq!(risk.description, "Test risk");
1808        assert_eq!(risk.likelihood, 0.7);
1809        assert_eq!(risk.impact, "High impact");
1810        assert_eq!(risk.mitigation.len(), 1);
1811    }
1812
1813    #[test]
1814    fn test_risk_likelihood_clamping() {
1815        let risk = Risk::new("R001", "Test", RiskCategory::Technical, RiskSeverity::Low)
1816            .with_likelihood(1.5);
1817        assert_eq!(risk.likelihood, 1.0);
1818
1819        let risk = Risk::new("R002", "Test", RiskCategory::Technical, RiskSeverity::Low)
1820            .with_likelihood(-0.5);
1821        assert_eq!(risk.likelihood, 0.0);
1822    }
1823
1824    #[test]
1825    fn test_critical_file_creation() {
1826        let file = CriticalFile::new("src/main.rs", "Main entry point", ModificationType::Modify)
1827            .with_priority(9)
1828            .with_estimated_changes(50);
1829
1830        assert_eq!(file.path, PathBuf::from("src/main.rs"));
1831        assert_eq!(file.reason, "Main entry point");
1832        assert_eq!(file.priority, 9);
1833        assert_eq!(file.estimated_changes, Some(50));
1834    }
1835
1836    #[test]
1837    fn test_critical_file_priority_clamping() {
1838        let file = CriticalFile::new("test.rs", "Test", ModificationType::Create).with_priority(15);
1839        assert_eq!(file.priority, 10);
1840    }
1841
1842    #[test]
1843    fn test_plan_step_creation() {
1844        let step = PlanStep::new(1, "Step 1", "Description")
1845            .with_files(vec![PathBuf::from("file.rs")])
1846            .with_dependencies(vec![])
1847            .with_estimated_hours(2.0)
1848            .with_verification(vec!["Test passes".to_string()]);
1849
1850        assert_eq!(step.step_number, 1);
1851        assert_eq!(step.title, "Step 1");
1852        assert_eq!(step.files.len(), 1);
1853        assert_eq!(step.estimated_hours, Some(2.0));
1854        assert!(!step.optional);
1855    }
1856
1857    #[test]
1858    fn test_plan_step_optional() {
1859        let step = PlanStep::new(1, "Optional Step", "Description").as_optional();
1860        assert!(step.optional);
1861    }
1862
1863    #[test]
1864    fn test_alternative_creation() {
1865        let alt = Alternative::new("ALT001", "Standard", "Standard approach")
1866            .with_pros(vec!["Pro 1".to_string()])
1867            .with_cons(vec!["Con 1".to_string()])
1868            .with_complexity(Complexity::Low)
1869            .as_recommended();
1870
1871        assert_eq!(alt.id, "ALT001");
1872        assert!(alt.recommended);
1873        assert_eq!(alt.complexity, Complexity::Low);
1874    }
1875
1876    #[test]
1877    fn test_architectural_decision_creation() {
1878        let decision = ArchitecturalDecision::new("AD001", "Title", "Decision")
1879            .with_context("Context")
1880            .with_rationale("Rationale")
1881            .with_consequences(vec!["Consequence".to_string()]);
1882
1883        assert_eq!(decision.id, "AD001");
1884        assert_eq!(decision.context, "Context");
1885        assert_eq!(decision.rationale, "Rationale");
1886    }
1887
1888    #[test]
1889    fn test_requirements_analysis_creation() {
1890        let analysis = RequirementsAnalysis::new("Test task")
1891            .with_functional_requirements(vec!["FR1".to_string()])
1892            .with_non_functional_requirements(vec!["NFR1".to_string()])
1893            .with_assumptions(vec!["Assumption".to_string()]);
1894
1895        assert_eq!(analysis.original_task, "Test task");
1896        assert_eq!(analysis.functional_requirements.len(), 1);
1897        assert_eq!(analysis.non_functional_requirements.len(), 1);
1898    }
1899
1900    #[test]
1901    fn test_scope_definition() {
1902        let scope = ScopeDefinition::new()
1903            .with_in_scope(vec!["In scope".to_string()])
1904            .with_out_of_scope(vec!["Out of scope".to_string()])
1905            .with_future_considerations(vec!["Future".to_string()]);
1906
1907        assert_eq!(scope.in_scope.len(), 1);
1908        assert_eq!(scope.out_of_scope.len(), 1);
1909        assert_eq!(scope.future_considerations.len(), 1);
1910    }
1911
1912    #[test]
1913    fn test_plan_options_builder() {
1914        let options = PlanOptions::new("Test task")
1915            .with_context("Context")
1916            .with_constraints(vec!["Constraint".to_string()])
1917            .with_perspective("security")
1918            .with_thoroughness(ThoroughnessLevel::VeryThorough);
1919
1920        assert_eq!(options.task, "Test task");
1921        assert_eq!(options.context, Some("Context".to_string()));
1922        assert_eq!(options.perspective, Some("security".to_string()));
1923        assert_eq!(options.thoroughness, ThoroughnessLevel::VeryThorough);
1924    }
1925
1926    #[test]
1927    fn test_plan_result_data_builder() {
1928        let result = PlanResultData::new()
1929            .with_summary("Summary")
1930            .with_estimated_complexity(Complexity::High)
1931            .with_estimated_hours(10.0);
1932
1933        assert_eq!(result.summary, "Summary");
1934        assert_eq!(result.estimated_complexity, Complexity::High);
1935        assert_eq!(result.estimated_hours, Some(10.0));
1936    }
1937
1938    #[test]
1939    fn test_plan_result_calculate_total_hours() {
1940        let mut result = PlanResultData::new();
1941        result.steps = vec![
1942            PlanStep::new(1, "Step 1", "Desc").with_estimated_hours(2.0),
1943            PlanStep::new(2, "Step 2", "Desc").with_estimated_hours(3.0),
1944            PlanStep::new(3, "Step 3", "Desc"), // No hours
1945        ];
1946
1947        assert_eq!(result.calculate_total_hours(), 5.0);
1948    }
1949
1950    #[test]
1951    fn test_plan_agent_creation() {
1952        let options = PlanOptions::new("Test task");
1953        let agent = PlanAgent::new(options);
1954
1955        assert_eq!(agent.options().task, "Test task");
1956        assert!(agent.files_read().is_empty());
1957    }
1958
1959    #[test]
1960    fn test_extract_keywords() {
1961        let options = PlanOptions::new("Implement user authentication with JWT tokens");
1962        let agent = PlanAgent::new(options);
1963
1964        let keywords = agent.extract_keywords("Implement user authentication with JWT tokens");
1965
1966        assert!(!keywords.is_empty());
1967        assert!(keywords.iter().any(|k| k == "implement"
1968            || k == "user"
1969            || k == "authentication"
1970            || k == "jwt"
1971            || k == "tokens"));
1972    }
1973
1974    #[tokio::test]
1975    async fn test_analyze_requirements() {
1976        let options = PlanOptions::new("Implement secure API endpoint")
1977            .with_context("REST API")
1978            .with_constraints(vec!["Must use HTTPS".to_string()]);
1979
1980        let agent = PlanAgent::new(options);
1981        let analysis = agent.analyze_requirements().await.unwrap();
1982
1983        assert_eq!(analysis.original_task, "Implement secure API endpoint");
1984        assert!(!analysis.functional_requirements.is_empty());
1985        assert!(!analysis.non_functional_requirements.is_empty());
1986    }
1987
1988    #[tokio::test]
1989    async fn test_assess_risks_security() {
1990        let options = PlanOptions::new("Implement authentication system");
1991        let agent = PlanAgent::new(options);
1992        let risks = agent.assess_risks().await.unwrap();
1993
1994        assert!(!risks.is_empty());
1995        assert!(risks
1996            .iter()
1997            .any(|r| matches!(r.category, RiskCategory::Security)));
1998    }
1999
2000    #[tokio::test]
2001    async fn test_assess_risks_performance() {
2002        let options = PlanOptions::new("Optimize database performance");
2003        let agent = PlanAgent::new(options);
2004        let risks = agent.assess_risks().await.unwrap();
2005
2006        assert!(!risks.is_empty());
2007        assert!(risks
2008            .iter()
2009            .any(|r| matches!(r.category, RiskCategory::Performance)));
2010    }
2011
2012    #[tokio::test]
2013    async fn test_generate_alternatives() {
2014        let options = PlanOptions::new("Refactor legacy code");
2015        let agent = PlanAgent::new(options);
2016        let alternatives = agent.generate_alternatives().await.unwrap();
2017
2018        assert!(!alternatives.is_empty());
2019        assert!(alternatives.iter().any(|a| a.recommended));
2020        // Should have incremental approach for refactoring
2021        assert!(alternatives.iter().any(|a| a.name.contains("Incremental")));
2022    }
2023
2024    #[tokio::test]
2025    async fn test_create_plan_empty_task() {
2026        let options = PlanOptions::new("");
2027        let agent = PlanAgent::new(options);
2028        let result = agent.create_plan().await;
2029
2030        assert!(result.is_err());
2031        assert!(matches!(result.unwrap_err(), PlanError::InvalidTask(_)));
2032    }
2033
2034    #[tokio::test]
2035    async fn test_create_plan_success() {
2036        let temp_dir = TempDir::new().unwrap();
2037        fs::write(temp_dir.path().join("main.rs"), "fn main() {}").unwrap();
2038
2039        let options = PlanOptions::new("Add logging to the application")
2040            .with_working_directory(temp_dir.path())
2041            .with_thoroughness(ThoroughnessLevel::Quick);
2042
2043        let agent = PlanAgent::new(options);
2044        let result = agent.create_plan().await.unwrap();
2045
2046        assert!(!result.summary.is_empty());
2047        assert!(!result.steps.is_empty());
2048        assert!(!result.alternatives.is_empty());
2049    }
2050
2051    #[tokio::test]
2052    async fn test_identify_files_with_existing_code() {
2053        let temp_dir = TempDir::new().unwrap();
2054        let file_path = temp_dir.path().join("test.rs");
2055        fs::write(&file_path, "// test file").unwrap();
2056
2057        let options = PlanOptions::new("Test task")
2058            .with_working_directory(temp_dir.path())
2059            .with_existing_code(vec![PathBuf::from("test.rs")]);
2060
2061        let agent = PlanAgent::new(options);
2062        let files = agent.identify_files().await.unwrap();
2063
2064        assert!(!files.is_empty());
2065        assert!(files
2066            .iter()
2067            .any(|f| f.path.to_string_lossy().contains("test.rs")));
2068    }
2069
2070    #[tokio::test]
2071    async fn test_read_only_mode() {
2072        let temp_dir = TempDir::new().unwrap();
2073        let file_path = temp_dir.path().join("readonly.rs");
2074        let original_content = "// original content";
2075        fs::write(&file_path, original_content).unwrap();
2076
2077        let options = PlanOptions::new("Analyze the code")
2078            .with_working_directory(temp_dir.path())
2079            .with_existing_code(vec![PathBuf::from("readonly.rs")]);
2080
2081        let agent = PlanAgent::new(options);
2082        let _ = agent.create_plan().await.unwrap();
2083
2084        // Verify file was not modified
2085        let content = fs::read_to_string(&file_path).unwrap();
2086        assert_eq!(content, original_content);
2087
2088        // Verify file was read
2089        let files_read = agent.files_read();
2090        assert!(!files_read.is_empty());
2091    }
2092
2093    #[test]
2094    fn test_estimate_complexity_low() {
2095        let options = PlanOptions::new("Simple task").with_thoroughness(ThoroughnessLevel::Quick);
2096        let agent = PlanAgent::new(options);
2097
2098        let files = vec![CriticalFile::new(
2099            "file1.rs",
2100            "Test",
2101            ModificationType::Modify,
2102        )];
2103        let risks = vec![];
2104
2105        let complexity = agent.estimate_complexity(&files, &risks);
2106        assert!(matches!(complexity, Complexity::Trivial | Complexity::Low));
2107    }
2108
2109    #[test]
2110    fn test_estimate_complexity_high() {
2111        let options =
2112            PlanOptions::new("Complex task").with_thoroughness(ThoroughnessLevel::VeryThorough);
2113        let agent = PlanAgent::new(options);
2114
2115        let files: Vec<CriticalFile> = (0..15)
2116            .map(|i| CriticalFile::new(format!("file{}.rs", i), "Test", ModificationType::Modify))
2117            .collect();
2118
2119        let risks = vec![
2120            Risk::new(
2121                "R1",
2122                "Risk 1",
2123                RiskCategory::Security,
2124                RiskSeverity::Critical,
2125            ),
2126            Risk::new("R2", "Risk 2", RiskCategory::Technical, RiskSeverity::High),
2127        ];
2128
2129        let complexity = agent.estimate_complexity(&files, &risks);
2130        assert!(matches!(
2131            complexity,
2132            Complexity::High | Complexity::VeryHigh
2133        ));
2134    }
2135
2136    #[test]
2137    fn test_generate_steps() {
2138        let options = PlanOptions::new("Test task");
2139        let agent = PlanAgent::new(options);
2140
2141        let files = vec![CriticalFile::new(
2142            "file1.rs",
2143            "Test",
2144            ModificationType::Modify,
2145        )];
2146        let risks = vec![Risk::new(
2147            "R1",
2148            "High risk",
2149            RiskCategory::Security,
2150            RiskSeverity::High,
2151        )];
2152
2153        let steps = agent.generate_steps(&files, &risks);
2154
2155        assert!(!steps.is_empty());
2156        // Should have analysis, design, implementation, testing, risk mitigation, docs, review
2157        assert!(steps.len() >= 5);
2158        // Steps should be numbered sequentially
2159        for (i, step) in steps.iter().enumerate() {
2160            assert_eq!(step.step_number, i + 1);
2161        }
2162    }
2163
2164    #[test]
2165    fn test_modification_type_default() {
2166        assert_eq!(ModificationType::default(), ModificationType::Modify);
2167    }
2168
2169    #[test]
2170    fn test_risk_category_default() {
2171        assert_eq!(RiskCategory::default(), RiskCategory::Technical);
2172    }
2173
2174    #[test]
2175    fn test_risk_severity_default() {
2176        assert_eq!(RiskSeverity::default(), RiskSeverity::Medium);
2177    }
2178
2179    #[test]
2180    fn test_complexity_default() {
2181        assert_eq!(Complexity::default(), Complexity::Medium);
2182    }
2183}