1use anyhow::{Context, Result};
5use robonix_cli::output;
6use std::path::Path;
7
8fn validate_name(name: &str) -> Result<()> {
10 if name.is_empty() {
11 anyhow::bail!("name must not be empty");
12 }
13 if name.contains('/') || name.contains('\\') || name == ".." || name.starts_with("../") {
14 anyhow::bail!("invalid name '{name}': must not contain path separators or '..' components");
15 }
16 Ok(())
17}
18
19pub async fn execute(name: &str, path: Option<&Path>) -> Result<()> {
20 validate_name(name)?;
21
22 let base = match path {
23 Some(p) => p.to_path_buf(),
24 None => std::env::current_dir()?,
25 };
26 let project_dir = base.join(name);
27
28 if project_dir.exists() {
29 anyhow::bail!("directory '{}' already exists", project_dir.display());
30 }
31
32 output::action("Init", &format!("creating project '{name}'"));
33
34 for sub in ["primitives", "services", "skills"] {
36 std::fs::create_dir_all(project_dir.join(sub))
37 .with_context(|| format!("failed to create {sub}/ directory"))?;
38 }
39
40 let manifest = format!(
42 r#"name: {name}
43
44env:
45 LOG: "INFO"
46
47system:
48 atlas:
49 listen: 127.0.0.1:50051
50 log: info
51 executor:
52 listen: 127.0.0.1:50061
53 log: info
54 pilot:
55 listen: 127.0.0.1:50071
56 log: info
57 vlm:
58 upstream: ${{VLM_BASE_URL}}
59 api_key: ${{VLM_API_KEY}}
60 model: ${{VLM_MODEL}}
61 api_format: openai
62 liaison:
63 listen: 127.0.0.1:50081
64 log: info
65 memory: []
66
67primitive: []
68
69service: []
70
71skill: []
72"#
73 );
74 std::fs::write(project_dir.join("robonix_manifest.yaml"), manifest)
75 .context("failed to write robonix_manifest.yaml")?;
76
77 let gitignore = "\
79rbnx-build/
80rbnx-boot/
81__pycache__/
82*.pyc
83.venv/
84";
85 std::fs::write(project_dir.join(".gitignore"), gitignore)
86 .context("failed to write .gitignore")?;
87
88 output::success(&format!(
89 "Project '{name}' created at {}",
90 project_dir.display()
91 ));
92 output::sub_step("robonix_manifest.yaml");
93 output::sub_step("primitives/");
94 output::sub_step("services/");
95 output::sub_step("skills/");
96 output::sub_step(".gitignore");
97
98 Ok(())
99}