dev_prune/commands/
setup.rs1use anyhow::Result;
11
12use crate::commands::hook::{self, HookState};
13use crate::config::Registry;
14use crate::daemon;
15use crate::output;
16use crate::setup;
17
18pub fn run(status_only: bool) -> Result<()> {
19 if status_only {
20 return run_status();
21 }
22
23 output::print_header("dev-prune setup");
24 let registry = Registry::load()?;
25 let report = setup::ensure_integrations(®istry);
26 report.print(true);
27 setup::suppress_next_auto_setup();
28
29 println!();
30 if report.needs_attention() {
31 output::print_info("Anything skipped above is optional — dev-prune works without it.");
32 } else {
33 output::print_success("Everything dev-prune needs is in place.");
34 }
35 output::print_info("Remove all of it again with `devp uninstall`.");
36
37 setup::offer_vscode_extension();
38
39 if registry.repositories.is_empty() {
44 println!();
45 output::print_info("No repositories are tracked yet. Register them either way:");
46 println!(
47 " devp init {} # crawl one folder for every Git repo inside it",
48 example_projects_dir()
49 );
50 println!(" devp link . # or, from inside one project, register just that one");
52 }
53
54 Ok(())
55}
56
57fn example_projects_dir() -> &'static str {
63 if cfg!(windows) { "~\\Code" } else { "~/code" }
64}
65
66fn run_status() -> Result<()> {
68 output::print_header("dev-prune setup status");
69 let registry = Registry::load()?;
70
71 let alias = std::env::current_exe()
72 .ok()
73 .and_then(|exe| exe.parent().map(|d| d.to_path_buf()))
74 .map(|dir| dir.join(if cfg!(windows) { "devp.exe" } else { "devp" }))
75 .filter(|p| p.exists());
76 println!(
77 " devp alias: {}",
78 alias
79 .as_ref()
80 .map(output::clean_path)
81 .unwrap_or_else(|| "not installed".to_string())
82 );
83
84 print!(" Command on PATH: ");
85 match setup::managed_bin_dir() {
86 Ok(dir) if crate::pathenv::is_reachable(&dir) => {
87 println!("{} is on your PATH", output::clean_path(&dir))
88 }
89 Ok(dir) => println!("{} is not on your PATH", output::clean_path(&dir)),
90 Err(_) => println!("unknown (no config directory)"),
91 }
92
93 let skill = setup::skill_path().ok().filter(|p| p.exists());
94 println!(
95 " SKILL.md: {}",
96 skill
97 .as_ref()
98 .map(output::clean_path)
99 .unwrap_or_else(|| "not exported".to_string())
100 );
101
102 let agent_roots = setup::agent_skill_roots();
103 if agent_roots.is_empty() {
104 println!(" AI agent skills: no AI agent detected");
105 } else {
106 for root in &agent_roots {
107 println!(
108 " AI agent skills: {} ({})",
109 output::clean_path(root),
110 if root.join("SKILL.md").is_file() {
111 "installed"
112 } else {
113 "not installed"
114 }
115 );
116 }
117 }
118
119 println!(
120 " File icons: {}",
121 if crate::commands::icon::is_registered() {
122 "registered"
123 } else {
124 "not registered"
125 }
126 );
127
128 print!(" Git hooks: ");
129 if !hook::git_available() {
130 println!("git is not on PATH");
131 } else {
132 match hook::state() {
133 Ok(HookState::Active) => println!("active"),
134 Ok(HookState::Absent) => println!("not installed"),
135 Ok(HookState::Chained { previous, drifted }) if drifted.is_empty() => {
136 println!("active, chained to `{previous}`")
137 }
138 Ok(HookState::Chained { previous, drifted }) => println!(
139 "active, chained to `{previous}` — {} not forwarded ({}); run `devp hook install --chain`",
140 drifted.len(),
141 drifted.join(", ")
142 ),
143 Ok(HookState::Foreign(p)) => println!(
144 "core.hooksPath belongs to `{p}` (install in front of it with `devp hook install --chain`)"
145 ),
146 Err(e) => println!("unknown ({e})"),
147 }
148 }
149
150 println!(
151 " Background scheduler: {}",
152 daemon::daemon_status()
153 .map(|s| s.to_string())
154 .unwrap_or_else(|e| format!("unknown ({e})"))
155 );
156
157 println!();
158 println!(" auto_setup = {}", registry.settings.auto_setup);
159 println!(" auto_hooks = {}", registry.settings.auto_hooks);
160 println!(" auto_daemon = {}", registry.settings.auto_daemon);
161
162 if let Some(why) = setup::unattended_environment() {
165 println!();
166 output::print_info(&format!(
167 "Unattended installation is off because {why}. `devp setup` still works when asked."
168 ));
169 }
170
171 println!();
172 if setup::setup_is_due() {
173 output::print_info("A setup pass is due. Run `devp setup`.");
174 } else {
175 output::print_info("Install anything missing with `devp setup`.");
176 }
177
178 Ok(())
179}