foundry_mcp/cli/commands/
validate_content.rs

1//! Implementation of the validate_content command
2//!
3//! This command validates LLM-provided content against Foundry's validation rules
4//! and provides improvement suggestions.
5
6use crate::cli::args::ValidateContentArgs;
7use crate::core::validation::{parse_content_type, validate_content};
8use crate::types::responses::{FoundryResponse, ValidateContentResponse, ValidationStatus};
9use anyhow::{Context, Result};
10
11/// Validate input arguments for validate_content command
12fn validate_input_args(content_type: &str, content: &str) -> Result<()> {
13    // Check for empty content type
14    if content_type.trim().is_empty() {
15        return Err(anyhow::anyhow!(
16            "Content type cannot be empty. Supported types: vision, tech-stack, summary, spec, notes, tasks"
17        ));
18    }
19
20    // Check for extremely large content
21    const MAX_VALIDATION_SIZE: usize = 100_000; // 100KB for validation
22    if content.len() > MAX_VALIDATION_SIZE {
23        return Err(anyhow::anyhow!(
24            "Content too large for validation ({} characters). Maximum size for validation is {} characters.",
25            content.len(),
26            MAX_VALIDATION_SIZE
27        ));
28    }
29
30    // Check for binary content (basic check)
31    if content.contains('\0') {
32        return Err(anyhow::anyhow!(
33            "Content appears to contain binary data. Only text content can be validated."
34        ));
35    }
36
37    Ok(())
38}
39
40pub async fn execute(
41    args: ValidateContentArgs,
42) -> Result<FoundryResponse<ValidateContentResponse>> {
43    // Enhanced input validation
44    validate_input_args(&args.content_type, &args.content)
45        .with_context(|| "Input validation failed")?;
46
47    // Parse content type with enhanced error handling
48    let content_type = parse_content_type(&args.content_type)
49        .with_context(|| {
50            format!(
51                "Invalid content type '{}'. Supported types are: vision, tech-stack, summary, spec, notes, tasks",
52                args.content_type
53            )
54        })?;
55
56    // Validate content with context
57    let validation_result = validate_content(content_type, &args.content);
58
59    // Prepare enhanced response with additional context
60    let response_data = ValidateContentResponse {
61        content_type: args.content_type.clone(),
62        is_valid: validation_result.is_valid,
63        validation_errors: validation_result.errors.clone(),
64        suggestions: validation_result.suggestions.clone(),
65    };
66
67    // Determine validation status with enhanced logic
68    let validation_status = if validation_result.is_valid {
69        ValidationStatus::Complete // Valid content, regardless of suggestions
70    } else {
71        ValidationStatus::Error
72    };
73
74    // Generate enhanced next steps based on validation results
75    let next_steps = if validation_result.is_valid {
76        let mut steps =
77            vec!["Content validation passed - ready to use in project creation".to_string()];
78
79        if !validation_result.suggestions.is_empty() {
80            steps.push(format!(
81                "Consider incorporating {} suggestions to improve content quality",
82                validation_result.suggestions.len()
83            ));
84        }
85
86        steps.push("Use this content via MCP: {\"name\": \"create_project\", \"arguments\": {\"project_name\": \"<name>\", \"vision\": \"...\", \"tech_stack\": \"...\", \"summary\": \"...\"}} or {\"name\": \"analyze_project\", \"arguments\": {\"project_name\": \"<name>\", \"vision\": \"...\", \"tech_stack\": \"...\", \"summary\": \"...\"}}".to_string());
87        steps
88    } else {
89        let error_count = validation_result.errors.len();
90        let suggestion_count = validation_result.suggestions.len();
91
92        let mut steps = vec![format!(
93            "Fix {} validation error(s) before using this content",
94            error_count
95        )];
96
97        if suggestion_count > 0 {
98            steps.push(format!(
99                "Review {} suggestion(s) for improvement guidance",
100                suggestion_count
101            ));
102        }
103
104        steps.push("Re-run validation after making changes".to_string());
105        steps
106    };
107
108    // Enhanced workflow hints with content-specific guidance
109    let mut workflow_hints = vec![
110        "Use this command to pre-validate content before project operations".to_string(),
111        "Validation helps ensure content meets Foundry's structural requirements".to_string(),
112    ];
113
114    // Add content-type specific hints
115    match args.content_type.as_str() {
116        "vision" => workflow_hints.push(
117            "Vision should describe the problem, target users, and value proposition".to_string(),
118        ),
119        "tech-stack" => workflow_hints.push(
120            "Tech stack should include languages, frameworks, and deployment decisions".to_string(),
121        ),
122        "summary" => workflow_hints
123            .push("Summary should be concise but capture key project insights".to_string()),
124        "spec" => workflow_hints.push(
125            "Spec should include clear requirements and functionality descriptions".to_string(),
126        ),
127        "notes" => workflow_hints
128            .push("Notes provide additional context and implementation considerations".to_string()),
129        "tasks" => workflow_hints
130            .push("Tasks should be actionable items with clear completion criteria".to_string()),
131        _ => workflow_hints
132            .push("Follow the content guidelines for your specific content type".to_string()),
133    }
134
135    workflow_hints
136        .push("Content validation is performed client-side for immediate feedback".to_string());
137
138    Ok(FoundryResponse {
139        data: response_data,
140        next_steps,
141        validation_status,
142        workflow_hints,
143    })
144}