systemprompt_cli/commands/cloud/init/
scaffolding.rs1use anyhow::{Context, Result};
11use std::path::Path;
12use std::process::Command;
13use systemprompt_logging::CliService;
14
15use super::templates::{
16 admin_agent_config, admin_mcp_config, agent_config, ai_config, blog_list_template,
17 blog_post_template, content_config, cookie_policy, page_list_template, page_template,
18 privacy_policy, root_config, scheduler_config, web_config, web_metadata, welcome_blog_post,
19};
20
21const ADMIN_MCP_REPO: &str = "https://github.com/systempromptio/systemprompt-mcp-admin.git";
22
23pub fn generate_services_boilerplate(project_root: &Path, project_name: &str) -> Result<()> {
24 CliService::section("Creating Services Boilerplate");
25
26 let services_dir = project_root.join("services");
27 let logs_dir = project_root.join("logs");
28
29 create_directories(&services_dir, &logs_dir)?;
30 write_config_files(&services_dir, project_name)?;
31 write_template_files(&services_dir)?;
32 write_content_files(&services_dir, project_name)?;
33
34 write_file(&services_dir.join("skills/.gitkeep"), "")?;
35
36 clone_admin_mcp_server(&services_dir)?;
37
38 CliService::success("Services boilerplate created");
39 Ok(())
40}
41
42fn create_directories(services_dir: &Path, logs_dir: &Path) -> Result<()> {
43 create_dir(services_dir)?;
44 create_dir(&services_dir.join("config"))?;
45 create_dir(&services_dir.join("agents"))?;
46 create_dir(&services_dir.join("mcp"))?;
47 create_dir(&services_dir.join("ai"))?;
48 create_dir(&services_dir.join("content"))?;
49 create_dir(&services_dir.join("content/blog"))?;
50 create_dir(&services_dir.join("content/blog/welcome"))?;
51 create_dir(&services_dir.join("content/legal"))?;
52 create_dir(&services_dir.join("skills"))?;
53 create_dir(&services_dir.join("web"))?;
54 create_dir(&services_dir.join("web/templates"))?;
55 create_dir(&services_dir.join("web/assets"))?;
56 create_dir(&services_dir.join("scheduler"))?;
57
58 create_dir(logs_dir)?;
59 write_file(
60 &logs_dir.join(".gitignore"),
61 "# Ignore all log files\n*.log\n*.log.*\n",
62 )?;
63
64 Ok(())
65}
66
67fn write_config_files(services_dir: &Path, project_name: &str) -> Result<()> {
68 write_file(&services_dir.join("config/config.yaml"), &root_config())?;
69 write_file(
70 &services_dir.join("agents/assistant.yaml"),
71 &agent_config(project_name),
72 )?;
73 write_file(
74 &services_dir.join("agents/admin.yaml"),
75 &admin_agent_config(),
76 )?;
77 write_file(
78 &services_dir.join("mcp/systemprompt-admin.yaml"),
79 &admin_mcp_config(),
80 )?;
81 write_file(
82 &services_dir.join("ai/config.yaml"),
83 &ai_config("anthropic"),
84 )?;
85 write_file(&services_dir.join("content/config.yaml"), &content_config())?;
86 write_file(
87 &services_dir.join("web/config.yaml"),
88 &web_config(project_name),
89 )?;
90 write_file(
91 &services_dir.join("web/metadata.yaml"),
92 &web_metadata(project_name),
93 )?;
94 write_file(
95 &services_dir.join("scheduler/config.yaml"),
96 &scheduler_config(),
97 )?;
98
99 Ok(())
100}
101
102fn write_template_files(services_dir: &Path) -> Result<()> {
103 write_file(
104 &services_dir.join("web/templates/page.html"),
105 &page_template(),
106 )?;
107 write_file(
108 &services_dir.join("web/templates/blog-post.html"),
109 &blog_post_template(),
110 )?;
111 write_file(
112 &services_dir.join("web/templates/blog-list.html"),
113 &blog_list_template(),
114 )?;
115 write_file(
116 &services_dir.join("web/templates/page-list.html"),
117 &page_list_template(),
118 )?;
119
120 Ok(())
121}
122
123fn write_content_files(services_dir: &Path, project_name: &str) -> Result<()> {
124 write_file(
125 &services_dir.join("content/blog/welcome/index.md"),
126 &welcome_blog_post(project_name),
127 )?;
128 write_file(
129 &services_dir.join("content/legal/privacy-policy.md"),
130 &privacy_policy(project_name),
131 )?;
132 write_file(
133 &services_dir.join("content/legal/cookie-policy.md"),
134 &cookie_policy(project_name),
135 )?;
136
137 Ok(())
138}
139
140fn clone_admin_mcp_server(services_dir: &Path) -> Result<()> {
141 let mcp_dir = services_dir.join("mcp/systemprompt-admin");
142
143 if mcp_dir.exists() {
144 CliService::info("systemprompt-admin MCP server already exists");
145 return Ok(());
146 }
147
148 let spinner = CliService::spinner("Cloning systemprompt-admin MCP server...");
149
150 let output = Command::new("git")
151 .args(["clone", "--depth", "1", ADMIN_MCP_REPO])
152 .arg(&mcp_dir)
153 .output()
154 .context("Failed to execute git clone")?;
155
156 spinner.finish_and_clear();
157
158 if !output.status.success() {
159 let stderr = String::from_utf8_lossy(&output.stderr);
160 CliService::warning(&format!("Could not clone systemprompt-admin: {}", stderr));
161 return Ok(());
162 }
163
164 let git_dir = mcp_dir.join(".git");
165 if git_dir.exists()
166 && let Err(e) = std::fs::remove_dir_all(&git_dir)
167 {
168 tracing::warn!(path = %git_dir.display(), error = %e, "failed to remove .git dir");
169 }
170
171 CliService::success("Cloned systemprompt-admin MCP server");
172 Ok(())
173}
174
175fn create_dir(path: &Path) -> Result<()> {
176 std::fs::create_dir_all(path)
177 .with_context(|| format!("Failed to create directory: {}", path.display()))
178}
179
180fn write_file(path: &Path, content: &str) -> Result<()> {
181 std::fs::write(path, content)
182 .with_context(|| format!("Failed to write file: {}", path.display()))?;
183 CliService::info(&format!(" Created {}", path.display()));
184 Ok(())
185}