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 daemon_url: None,
67 index_scope: config::ProjectIndexScope::Single,
68 };
69 let index_result =
70 match index_lock::with_project_lock(&index_ctx, IndexLockPolicy::Wait, || {
71 api::index_files(
72 api::IndexRequest {
73 project_root: project_root.to_path_buf(),
74 path_filter: None,
75 explicit_files: Vec::new(),
76 full: false,
77 require_cpp_semantics: false,
78 sync_projections: false,
79 },
80 &index_ctx,
81 )
82 })? {
83 IndexLockResult::Acquired(outcome) => outcome,
84 IndexLockResult::Busy => unreachable!("wait policy always acquires the index lock"),
85 };
86 if !quiet {
87 eprintln!(
88 "Indexed {} files, {} symbols in {}ms",
89 index_result.indexed_files,
90 index_result.symbols_indexed,
91 index_result.durations.total_ms
92 );
93 }
94
95 match format {
96 Format::Json => {
97 let mut result = serde_json::json!({
98 "project_id": project_id,
99 "project_root": project_root.to_string_lossy(),
100 "status": status,
101 "files_indexed": index_result.indexed_files,
102 "symbols_found": index_result.symbols_indexed,
103 "duration_ms": index_result.durations.total_ms,
104 });
105 if !installed_skills.is_empty() {
106 result["skills_installed"] = serde_json::json!(installed_skills);
107 }
108 output::print_json(&result)
109 }
110 Format::Text => {
111 if !quiet {
112 match status {
113 "initialized" => {
114 eprintln!(
115 "Initialized project at {}\nProject ID: {}",
116 project_root.display(),
117 project_id
118 );
119 }
120 "gobby" => {
121 eprintln!(
122 "Using gobby project: {} ({})",
123 project_id,
124 project_root.display()
125 );
126 }
127 "isolated" | "linked-worktree" => {
128 eprintln!(
129 "Using {} code index: {} ({})",
130 status,
131 project_id,
132 project_root.display()
133 );
134 }
135 _ => {
136 eprintln!(
137 "Already initialized: {} ({})",
138 project_id,
139 project_root.display()
140 );
141 }
142 }
143 }
144 Ok(())
145 }
146 }
147}