1pub mod cli;
2pub mod demo;
3pub mod handlers;
4#[cfg(feature = "pmcp-mcp")]
5pub mod mcp_pmcp;
6pub mod mcp_server;
7pub mod models;
8pub mod services;
9pub mod stateless_server;
10pub mod unified_protocol;
13pub mod utils;
14
15use anyhow::Result;
16use lru::LruCache;
17use std::num::NonZeroUsize;
18use std::sync::Arc;
19use tokio::sync::RwLock;
20use tracing::info;
21
22use crate::models::template::TemplateResource;
23use crate::services::renderer::TemplateRenderer;
24
25type MetadataCache = Arc<RwLock<LruCache<String, Arc<TemplateResource>>>>;
27type ContentCache = Arc<RwLock<LruCache<String, Arc<str>>>>;
28
29pub struct S3Client;
31
32#[async_trait::async_trait]
33pub trait TemplateServerTrait: Send + Sync {
34 async fn get_template_metadata(&self, uri: &str) -> Result<Arc<TemplateResource>>;
35 async fn get_template_content(&self, s3_key: &str) -> Result<Arc<str>>;
36 async fn list_templates(&self, prefix: &str) -> Result<Vec<Arc<TemplateResource>>>;
37 fn get_renderer(&self) -> &TemplateRenderer;
38 fn get_metadata_cache(&self) -> Option<&MetadataCache>;
39 fn get_content_cache(&self) -> Option<&ContentCache>;
40 fn get_s3_client(&self) -> Option<&S3Client>;
41 fn get_bucket_name(&self) -> Option<&str>;
42}
43
44pub struct TemplateServer {
45 pub s3_client: S3Client,
46 pub bucket_name: String,
47 pub metadata_cache: MetadataCache,
48 pub content_cache: ContentCache,
49 pub renderer: TemplateRenderer,
50}
51
52impl TemplateServer {
53 pub async fn new() -> Result<Self> {
54 let cache_size = 1024;
57
58 Ok(Self {
59 s3_client: S3Client,
60 bucket_name: "dummy".to_string(),
61 metadata_cache: Arc::new(RwLock::new(LruCache::new(
62 NonZeroUsize::new(cache_size / 2).unwrap(),
63 ))),
64 content_cache: Arc::new(RwLock::new(LruCache::new(
65 NonZeroUsize::new(cache_size).unwrap(),
66 ))),
67 renderer: TemplateRenderer::new()?,
68 })
69 }
70
71 pub async fn warm_cache(&self) -> Result<()> {
72 let common_templates = vec![
73 "template://makefile/rust/cli",
74 "template://makefile/deno/cli",
75 "template://makefile/python-uv/cli",
76 "template://readme/rust/cli",
77 "template://gitignore/rust/cli",
78 ];
79
80 info!(
81 "Warming cache with {} common templates",
82 common_templates.len()
83 );
84
85 for template_uri in common_templates {
86 match self.get_template_metadata(template_uri).await {
87 Ok(resource) => {
88 let _ = self.get_template_content(&resource.s3_object_key).await;
89 }
90 Err(e) => {
91 info!("Failed to warm cache for {}: {}", template_uri, e);
92 }
93 }
94 }
95
96 Ok(())
97 }
98
99 pub async fn get_template_metadata(&self, _uri: &str) -> Result<Arc<TemplateResource>> {
100 Err(anyhow::anyhow!(
102 "TemplateServer with S3 is deprecated. Use StatelessTemplateServer instead."
103 ))
104 }
105
106 pub async fn get_template_content(&self, _s3_key: &str) -> Result<Arc<str>> {
107 Err(anyhow::anyhow!(
109 "TemplateServer with S3 is deprecated. Use StatelessTemplateServer instead."
110 ))
111 }
112}
113
114#[async_trait::async_trait]
115impl TemplateServerTrait for TemplateServer {
116 async fn get_template_metadata(&self, uri: &str) -> Result<Arc<TemplateResource>> {
117 self.get_template_metadata(uri).await
118 }
119
120 async fn get_template_content(&self, s3_key: &str) -> Result<Arc<str>> {
121 self.get_template_content(s3_key).await
122 }
123
124 async fn list_templates(&self, _prefix: &str) -> Result<Vec<Arc<TemplateResource>>> {
125 Err(anyhow::anyhow!(
127 "TemplateServer with S3 is deprecated. Use StatelessTemplateServer instead."
128 ))
129 }
130
131 fn get_renderer(&self) -> &TemplateRenderer {
132 &self.renderer
133 }
134
135 fn get_metadata_cache(&self) -> Option<&MetadataCache> {
136 Some(&self.metadata_cache)
137 }
138
139 fn get_content_cache(&self) -> Option<&ContentCache> {
140 Some(&self.content_cache)
141 }
142
143 fn get_s3_client(&self) -> Option<&S3Client> {
144 Some(&self.s3_client)
145 }
146
147 fn get_bucket_name(&self) -> Option<&str> {
148 Some(&self.bucket_name)
149 }
150}
151
152pub use models::error::TemplateError;
154pub use models::template::{ParameterSpec, ParameterType};
155pub use services::template_service::{
156 generate_template, list_templates, scaffold_project, search_templates, validate_template,
157};
158
159pub async fn run_mcp_server<T: TemplateServerTrait + 'static>(server: Arc<T>) -> Result<()> {
161 use crate::models::mcp::{McpRequest, McpResponse};
162 use std::io::{self, BufRead, Write};
163 use tracing::{error, info};
164
165 info!("MCP server ready, waiting for requests on stdin...");
166
167 let stdin = io::stdin();
169 let mut stdout = io::stdout();
170
171 for line in stdin.lock().lines() {
172 let line = line?;
173
174 if line.trim().is_empty() {
176 continue;
177 }
178
179 match serde_json::from_str::<McpRequest>(&line) {
181 Ok(request) => {
182 info!(
183 "Received request: method={}, id={:?}",
184 request.method, request.id
185 );
186
187 let response = handlers::handle_request(Arc::clone(&server), request).await;
189
190 let response_json = serde_json::to_string(&response)?;
192 writeln!(stdout, "{response_json}")?;
193 stdout.flush()?;
194 }
195 Err(e) => {
196 error!("Failed to parse JSON-RPC request: {}", e);
197
198 let error_response = McpResponse::error(
200 serde_json::Value::Null,
201 -32700,
202 format!("Parse error: {e}"),
203 );
204
205 let response_json = serde_json::to_string(&error_response)?;
206 writeln!(stdout, "{response_json}")?;
207 stdout.flush()?;
208 }
209 }
210 }
211
212 Ok(())
213}
214
215#[cfg(test)]
216mod tests {
217
218 #[path = "../tests/template_rendering.rs"]
219 mod template_rendering;
220
221 #[path = "../tests/claude_code_e2e.rs"]
222 mod claude_code_e2e;
223
224 #[path = "../tests/mcp_protocol.rs"]
225 mod mcp_protocol;
226
227 #[path = "../tests/template_resources.rs"]
228 mod template_resources;
229
230 #[path = "../tests/helpers.rs"]
231 mod helpers;
232
233 #[path = "../tests/quality_checks_property_tests.rs"]
234 mod quality_checks_property_tests;
235
236 #[path = "../tests/prompts.rs"]
237 mod prompts;
238
239 #[path = "../tests/binary_size.rs"]
240 mod binary_size;
241
242 #[path = "../tests/error.rs"]
243 mod error;
244
245 #[path = "../tests/cache.rs"]
246 mod cache;
247
248 #[path = "../tests/tools.rs"]
249 mod tools;
250
251 #[path = "../tests/analyze_cli_tests.rs"]
252 mod analyze_cli_tests;
253
254 #[path = "../tests/cli_integration_full.rs"]
255 mod cli_integration_full;
256
257 #[path = "../tests/resources.rs"]
258 mod resources;
259
260 #[path = "../tests/lib.rs"]
261 mod lib_tests;
262
263 #[path = "../tests/build_naming_validation.rs"]
264 mod build_naming_validation;
265
266 #[path = "../tests/ast_e2e.rs"]
267 mod ast_e2e;
268
269 #[path = "../tests/cli_tests.rs"]
270 mod cli_tests;
271
272 #[path = "../tests/churn.rs"]
273 mod churn;
274
275 #[path = "../tests/cli_simple_tests.rs"]
276 mod cli_simple_tests;
277
278 #[path = "../tests/deep_context_simplified_tests.rs"]
279 mod deep_context_simplified_tests;
280
281 #[path = "../tests/demo_comprehensive_tests.rs"]
282 mod demo_comprehensive_tests;
283
284 #[path = "../tests/http_adapter_tests.rs"]
285 mod http_adapter_tests;
286
287 #[path = "../tests/cache_comprehensive_tests.rs"]
288 mod cache_comprehensive_tests;
289
290 #[path = "../tests/unified_protocol_tests.rs"]
291 mod unified_protocol_tests;
292
293 #[path = "../tests/models.rs"]
294 mod models_tests;
295
296 #[path = "../tests/e2e_full_coverage.rs"]
297 mod e2e_full_coverage;
298
299 #[path = "../tests/additional_coverage.rs"]
300 mod additional_coverage;
301
302 #[path = "../tests/error_handling.rs"]
303 mod error_handling;
304
305 #[path = "../tests/cli_comprehensive_tests.rs"]
306 mod cli_comprehensive_tests;
307
308 #[path = "../tests/cli_property_tests.rs"]
309 mod cli_property_tests;
310
311 #[path = "../tests/ast_regression_test.rs"]
312 mod ast_regression_test;
313
314 #[path = "../tests/dead_code_verification.rs"]
315 mod dead_code_verification;
316
317 #[path = "../tests/complexity_distribution_verification.rs"]
318 mod complexity_distribution_verification;
319}