foundry_mcp/core/ops/
load_project.rs1use 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}