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