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 if registry.repositories.is_empty() {
42 println!();
43 output::print_info("No repositories are tracked yet. Register them either way:");
44 println!(
45 " devp init {} # crawl one folder for every Git repo inside it",
46 example_projects_dir()
47 );
48 println!(" devp link . # or, from inside one project, register just that one");
50 }
51
52 Ok(())
53}
54
55fn example_projects_dir() -> &'static str {
61 if cfg!(windows) { "~\\Code" } else { "~/code" }
62}
63
64fn run_status() -> Result<()> {
66 output::print_header("dev-prune setup status");
67 let registry = Registry::load()?;
68
69 let alias = std::env::current_exe()
70 .ok()
71 .and_then(|exe| exe.parent().map(|d| d.to_path_buf()))
72 .map(|dir| dir.join(if cfg!(windows) { "devp.exe" } else { "devp" }))
73 .filter(|p| p.exists());
74 println!(
75 " devp alias: {}",
76 alias
77 .as_ref()
78 .map(output::clean_path)
79 .unwrap_or_else(|| "not installed".to_string())
80 );
81
82 let skill = setup::skill_path().ok().filter(|p| p.exists());
83 println!(
84 " SKILL.md: {}",
85 skill
86 .as_ref()
87 .map(output::clean_path)
88 .unwrap_or_else(|| "not exported".to_string())
89 );
90
91 println!(
92 " File icons: {}",
93 if crate::commands::icon::is_registered() {
94 "registered"
95 } else {
96 "not registered"
97 }
98 );
99
100 print!(" Git hooks: ");
101 if !hook::git_available() {
102 println!("git is not on PATH");
103 } else {
104 match hook::state() {
105 Ok(HookState::Active) => println!("active"),
106 Ok(HookState::Absent) => println!("not installed"),
107 Ok(HookState::Chained { previous, drifted }) if drifted.is_empty() => {
108 println!("active, chained to `{previous}`")
109 }
110 Ok(HookState::Chained { previous, drifted }) => println!(
111 "active, chained to `{previous}` — {} not forwarded ({}); run `devp hook install --chain`",
112 drifted.len(),
113 drifted.join(", ")
114 ),
115 Ok(HookState::Foreign(p)) => println!(
116 "core.hooksPath belongs to `{p}` (install in front of it with `devp hook install --chain`)"
117 ),
118 Err(e) => println!("unknown ({e})"),
119 }
120 }
121
122 println!(
123 " Background scheduler: {}",
124 daemon::daemon_status()
125 .map(|s| s.to_string())
126 .unwrap_or_else(|e| format!("unknown ({e})"))
127 );
128
129 println!();
130 println!(" auto_setup = {}", registry.settings.auto_setup);
131 println!(" auto_hooks = {}", registry.settings.auto_hooks);
132 println!(" auto_daemon = {}", registry.settings.auto_daemon);
133
134 if let Some(why) = setup::unattended_environment() {
137 println!();
138 output::print_info(&format!(
139 "Unattended installation is off because {why}. `devp setup` still works when asked."
140 ));
141 }
142
143 println!();
144 if setup::setup_is_due() {
145 output::print_info("A setup pass is due. Run `devp setup`.");
146 } else {
147 output::print_info("Install anything missing with `devp setup`.");
148 }
149
150 Ok(())
151}