Skip to main content

foundry_mcp/core/ops/
load_project.rs

1//! Core op for loading full project context (tool-agnostic)
2
3use anyhow::Result;
4
5use crate::core::foundry;
6use crate::types::responses::{
7    FoundryResponse, LoadProjectResponse, ProjectContext, ValidationStatus,
8};
9
10#[derive(Debug, Clone)]
11pub struct Input {
12    pub project_name: String,
13}
14
15pub async fn run(input: Input) -> Result<FoundryResponse<LoadProjectResponse>> {
16    let foundry = foundry::get_default_foundry()?;
17
18    validate_project_exists(&foundry, &input.project_name).await?;
19
20    let project = foundry.load_project(&input.project_name).await?;
21    let specs = foundry.list_specs(&input.project_name).await?;
22
23    let project_context = build_project_context(project, specs);
24    let specs_available = project_context.specs_available.clone();
25
26    let response_data = LoadProjectResponse {
27        project: project_context,
28    };
29
30    let validation_status = if specs_available.is_empty() {
31        ValidationStatus::Incomplete
32    } else {
33        ValidationStatus::Complete
34    };
35
36    Ok(FoundryResponse {
37        data: response_data,
38        next_steps: generate_next_steps(&input.project_name, &specs_available),
39        validation_status,
40        workflow_hints: generate_workflow_hints(&specs_available),
41    })
42}
43
44async fn validate_project_exists(
45    foundry: &foundry::Foundry<crate::core::backends::filesystem::FilesystemBackend>,
46    project_name: &str,
47) -> Result<()> {
48    if !foundry.project_exists(project_name).await? {
49        return Err(anyhow::anyhow!(
50            "Project '{}' not found. Use 'mcp_foundry_list_projects' to see available projects.",
51            project_name
52        ));
53    }
54    Ok(())
55}
56
57fn build_project_context(
58    project: crate::types::project::Project,
59    specs: Vec<crate::types::spec::SpecMetadata>,
60) -> ProjectContext {
61    let specs_available = specs.into_iter().map(|s| s.name).collect();
62
63    ProjectContext {
64        name: project.name,
65        vision: project.vision.unwrap_or_default(),
66        tech_stack: project.tech_stack.unwrap_or_default(),
67        summary: project.summary.unwrap_or_default(),
68        specs_available,
69        created_at: project.created_at,
70    }
71}
72
73fn generate_next_steps(project_name: &str, specs_available: &[String]) -> Vec<String> {
74    if specs_available.is_empty() {
75        vec![
76            "Project context loaded successfully - ready for specification creation".to_string(),
77            format!(
78                "You can create your first specification: mcp_foundry_create_spec {} <feature_name>",
79                project_name
80            ),
81            "Your loaded project context provides comprehensive background for development decisions".to_string(),
82        ]
83    } else {
84        vec![
85            format!(
86                "Project context loaded with {} specification(s) available",
87                specs_available.len()
88            ),
89            format!(
90                "You can load a specific spec: mcp_foundry_load_spec {} <spec_name>",
91                project_name
92            ),
93            format!(
94                "You can create a new spec: mcp_foundry_create_spec {} <feature_name>",
95                project_name
96            ),
97        ]
98    }
99}
100
101fn generate_workflow_hints(specs_available: &[String]) -> Vec<String> {
102    let mut hints = vec![
103        "You can use the project summary for quick context in conversations".to_string(),
104        "The full vision provides comprehensive background and goals for your work".to_string(),
105        "Tech stack details guide your implementation decisions and technology choices".to_string(),
106        "You can skip list-projects calls when you know the project name - load_project is more efficient".to_string(),
107    ];
108
109    if specs_available.is_empty() {
110        hints.push(
111            "You can create specifications to track specific features as you identify them"
112                .to_string(),
113        );
114        hints.push(
115            "You can prompt the user about creating specifications to track specific features"
116                .to_string(),
117        );
118    } else {
119        hints.push(format!("Available specs: {}", specs_available.join(", ")));
120        hints.push(
121            "You can load individual specs to see detailed implementation plans and progress"
122                .to_string(),
123        );
124        hints.push("You can update existing specs with progress as work continues".to_string());
125    }
126
127    hints.push(
128        "You can use mcp_foundry_get_foundry_help decision-points to understand tool selection"
129            .to_string(),
130    );
131
132    hints
133}