oxicode/cli/commands/
doctor.rs1use anyhow::Result;
14use oxicode_catalog::oxi_home;
15use std::path::PathBuf;
16
17use super::reset::display_path;
18
19fn journal_status(journal_path: Option<PathBuf>) -> String {
21 let Some(path) = journal_path.filter(|p| p.exists()) else {
22 return "absent".to_string();
23 };
24 match crate::home_migrate::MigrationJournal::load(&path) {
25 Some(j) if j.is_in_progress() => "in_progress".to_string(),
26 Some(j) if j.status == "complete" => "complete".to_string(),
27 Some(_) => "present (unexpected status)".to_string(),
28 None => "present (unreadable)".to_string(),
29 }
30}
31
32pub fn handle_doctor() -> Result<()> {
34 println!("Oxi home layout");
35 println!("---------------");
36
37 let oxi = oxi_home::oxi_home();
38 let canonical = oxi_home::oxicode_home();
39 let legacy = oxi_home::legacy_home_dir();
40 let journal_path = oxi_home::migration_journal_path();
41
42 println!(
43 " oxi home: {}",
44 oxi.as_ref()
45 .map(|p| display_path(p))
46 .unwrap_or_else(|| "<unresolvable>".to_string())
47 );
48 println!(
49 " oxicode home: {} (canonical, owned by oxicode)",
50 canonical
51 .as_ref()
52 .map(|p| display_path(p))
53 .unwrap_or_else(|| "<unresolvable>".to_string())
54 );
55 if let Some(env_val) = std::env::var_os("OXICODE_HOME") {
56 println!(
57 " override: OXICODE_HOME={}",
58 env_val.to_string_lossy()
59 );
60 } else if let Some(env_val) = std::env::var_os("OXI_HOME") {
61 println!(" override: OXI_HOME={}", env_val.to_string_lossy());
62 }
63
64 match &legacy {
65 Some(legacy_dir) => {
66 println!(
67 " legacy home: {} (present, read-only)",
68 display_path(legacy_dir)
69 );
70 let complete = journal_status(journal_path.clone()) == "complete";
71 if canonical.as_ref().is_some_and(|c| c.exists()) && complete {
72 println!(" migration: complete");
73 } else {
74 println!(" migration: pending (run `oxicode migrate home`)");
75 }
76 }
77 None => {
78 println!(" legacy home: absent");
79 println!(" migration: nothing to do");
80 }
81 }
82 println!(
83 " journal: {}",
84 journal_status(journal_path).replace(
85 "in_progress",
86 "in_progress (rerun `oxicode migrate home` to resume)"
87 )
88 );
89
90 if let Some(home) = canonical {
92 println!();
93 println!("Owned paths");
94 println!("-----------");
95 for (label, rel) in [
96 ("settings", "settings.json"),
97 ("auth", "auth.json"),
98 ("sessions", "sessions"),
99 ("skills", "skills"),
100 ("extensions", "extensions"),
101 ("packages", "packages"),
102 ("catalog overrides", "catalog/overrides.toml"),
103 ("models.dev cache", "cache/models-dev.json"),
104 ] {
105 println!(
106 " {:<18} {}",
107 format!("{label}:"),
108 display_path(&home.join(rel))
109 );
110 }
111 }
112
113 if let Ok(cwd) = std::env::current_dir() {
116 let project = cwd.join(".oxicode");
117 if project.is_dir() {
118 println!();
119 println!(
120 " note: project-local .oxicode/ found at {} (separate namespace, not migrated)",
121 display_path(&project)
122 );
123 }
124 }
125
126 Ok(())
127}