dev_prune/commands/
setup.rs1use anyhow::Result;
25
26use crate::commands::hook::{self, HookState};
27use crate::config::Registry;
28use crate::daemon;
29use crate::output;
30use crate::setup;
31
32pub fn run(status_only: bool) -> Result<()> {
33 if status_only {
34 return run_status();
35 }
36
37 output::print_header("dev-prune setup");
38 let registry = Registry::load()?;
39 let report = setup::ensure_integrations(®istry);
40 report.print(true);
41 setup::suppress_next_auto_setup();
42
43 println!();
44 if report.needs_attention() {
45 output::print_info("Anything skipped above is optional — dev-prune works without it.");
46 } else {
47 output::print_success("Everything dev-prune needs is in place.");
48 }
49 output::print_info("Remove all of it again with `devp uninstall`.");
50
51 Ok(())
52}
53
54fn run_status() -> Result<()> {
56 output::print_header("dev-prune setup status");
57 let registry = Registry::load()?;
58
59 let alias = std::env::current_exe()
60 .ok()
61 .and_then(|exe| exe.parent().map(|d| d.to_path_buf()))
62 .map(|dir| dir.join(if cfg!(windows) { "devp.exe" } else { "devp" }))
63 .filter(|p| p.exists());
64 println!(
65 " devp alias: {}",
66 alias
67 .as_ref()
68 .map(output::clean_path)
69 .unwrap_or_else(|| "not installed".to_string())
70 );
71
72 let skill = setup::skill_path().ok().filter(|p| p.exists());
73 println!(
74 " SKILL.md: {}",
75 skill
76 .as_ref()
77 .map(output::clean_path)
78 .unwrap_or_else(|| "not exported".to_string())
79 );
80
81 println!(
82 " File icons: {}",
83 if crate::commands::icon::is_registered() {
84 "registered"
85 } else {
86 "not registered"
87 }
88 );
89
90 print!(" Git hooks: ");
91 if !hook::git_available() {
92 println!("git is not on PATH");
93 } else {
94 match hook::state() {
95 Ok(HookState::Active) => println!("active"),
96 Ok(HookState::Absent) => println!("not installed"),
97 Ok(HookState::Chained { previous, drifted }) if drifted.is_empty() => {
98 println!("active, chained to `{previous}`")
99 }
100 Ok(HookState::Chained { previous, drifted }) => println!(
101 "active, chained to `{previous}` — {} not forwarded ({}); run `devp hook install --chain`",
102 drifted.len(),
103 drifted.join(", ")
104 ),
105 Ok(HookState::Foreign(p)) => println!(
106 "core.hooksPath belongs to `{p}` (install in front of it with `devp hook install --chain`)"
107 ),
108 Err(e) => println!("unknown ({e})"),
109 }
110 }
111
112 println!(
113 " Background scheduler: {}",
114 daemon::daemon_status()
115 .map(|s| s.to_string())
116 .unwrap_or_else(|e| format!("unknown ({e})"))
117 );
118
119 println!();
120 println!(" auto_setup = {}", registry.settings.auto_setup);
121 println!(" auto_hooks = {}", registry.settings.auto_hooks);
122 println!(" auto_daemon = {}", registry.settings.auto_daemon);
123
124 if let Some(why) = setup::unattended_environment() {
127 println!();
128 output::print_info(&format!(
129 "Unattended installation is off because {why}. `devp setup` still works when asked."
130 ));
131 }
132
133 println!();
134 if setup::setup_is_due() {
135 output::print_info("A setup pass is due. Run `devp setup`.");
136 } else {
137 output::print_info("Install anything missing with `devp setup`.");
138 }
139
140 Ok(())
141}