gobby_code/commands/
init.rs1use std::path::Path;
2
3use crate::config;
4use crate::db;
5use crate::index::api;
6use crate::output::{self, Format};
7use crate::project;
8use crate::skill;
9
10pub fn run(project_root: &Path, format: Format, quiet: bool) -> anyhow::Result<()> {
11 let identity =
12 config::resolve_project_identity(project_root, config::MissingIdentity::Generate)?;
13 config::warn_project_identity(&identity, quiet);
14 let (project_id, was_created) = if identity.should_write_gcode_json {
15 project::ensure_gcode_json(project_root)?
16 } else {
17 (identity.project_id.clone(), false)
18 };
19
20 let status = match identity.source {
21 config::ProjectIdentitySource::IsolatedRoot => "isolated",
22 config::ProjectIdentitySource::LinkedWorktree => "linked-worktree",
23 config::ProjectIdentitySource::ProjectJson => "gobby",
24 config::ProjectIdentitySource::Generated if was_created => "initialized",
25 _ => "existing",
26 };
27
28 let mut installed_skills: Vec<String> = Vec::new();
30 if status != "gobby" {
31 for target in skill::supported_targets() {
32 match skill::install_skill(project_root, target) {
33 Ok(path) if !path.is_empty() => {
34 if !quiet {
35 eprintln!(
36 "Installed gcode skill for {} → {}",
37 target.display_name, path
38 );
39 }
40 installed_skills.push(target.display_name.to_string());
41 }
42 Err(e) if !quiet => {
43 eprintln!(
44 "Warning: failed to install skill for {}: {}",
45 target.display_name, e
46 );
47 }
48 _ => {}
49 }
50 }
51 }
52
53 let database_url = db::resolve_database_url()?;
56 let index_ctx = config::Context {
57 database_url,
58 project_root: project_root.to_path_buf(),
59 project_id: project_id.clone(),
60 quiet,
61 falkordb: None,
62 qdrant: None,
63 embedding: None,
64 code_vectors: config::CodeVectorSettings::default(),
65 daemon_url: None,
66 };
67 let index_result = api::index_files(
68 api::IndexRequest {
69 project_root: project_root.to_path_buf(),
70 path_filter: None,
71 explicit_files: Vec::new(),
72 full: false,
73 require_cpp_semantics: false,
74 sync_projections: false,
75 },
76 &index_ctx,
77 )?;
78 if !quiet {
79 eprintln!(
80 "Indexed {} files, {} symbols in {}ms",
81 index_result.indexed_files,
82 index_result.symbols_indexed,
83 index_result.durations.total_ms
84 );
85 }
86
87 match format {
88 Format::Json => {
89 let mut result = serde_json::json!({
90 "project_id": project_id,
91 "project_root": project_root.to_string_lossy(),
92 "status": status,
93 "files_indexed": index_result.indexed_files,
94 "symbols_found": index_result.symbols_indexed,
95 "duration_ms": index_result.durations.total_ms,
96 });
97 if !installed_skills.is_empty() {
98 result["skills_installed"] = serde_json::json!(installed_skills);
99 }
100 output::print_json(&result)
101 }
102 Format::Text => {
103 if !quiet {
104 match status {
105 "initialized" => {
106 eprintln!(
107 "Initialized project at {}\nProject ID: {}",
108 project_root.display(),
109 project_id
110 );
111 }
112 "gobby" => {
113 eprintln!(
114 "Using gobby project: {} ({})",
115 project_id,
116 project_root.display()
117 );
118 }
119 "isolated" | "linked-worktree" => {
120 eprintln!(
121 "Using {} code index: {} ({})",
122 status,
123 project_id,
124 project_root.display()
125 );
126 }
127 _ => {
128 eprintln!(
129 "Already initialized: {} ({})",
130 project_id,
131 project_root.display()
132 );
133 }
134 }
135 }
136 Ok(())
137 }
138 }
139}