1use std::process::Command;
3
4use anyhow::Result;
5use repo::Repository;
6use serde::Serialize;
7
8#[cfg(feature = "git-overlay")]
9use crate::cli::style;
10use crate::cli::{should_output_json, Cli};
11
12#[derive(Debug, Serialize)]
13struct VersionOutput {
14 version: &'static str,
15 profile: &'static str,
16 features: Vec<&'static str>,
17 git_version: Option<String>,
18 repository_capability: Option<String>,
19 repository_root: Option<String>,
20}
21
22#[cfg(feature = "git-overlay")]
23pub fn cmd_git_overlay_guide(cli: &Cli) -> Result<()> {
24 if should_output_json(cli, None) {
25 println!(
26 "{}",
27 serde_json::json!({
28 "topic": "git-overlay",
29 "summary": "Use Heddle beside Git: start lightweight, import history when you need it, and isolate risky work in threads.",
30 "steps": [
31 "heddle status",
32 "heddle bridge git import --ref <branch>",
33 "heddle start <name> --path <dir>",
34 "heddle merge <name>",
35 "heddle sync"
36 ]
37 })
38 );
39 return Ok(());
40 }
41
42 println!("{}", style::bold("Git-overlay quick start"));
43 println!("Use Heddle beside Git first. Import deeper history only when a command needs it.");
44 println!();
45 println!("1. Inspect the repo");
46 println!(" {}", style::bold("heddle status"));
47 println!("2. Import the current branch when history-oriented commands ask for it");
48 println!(
49 " {}",
50 style::bold("heddle bridge git import --ref <branch>")
51 );
52 println!("3. Start isolated work without disturbing your Git checkout");
53 println!(
54 " {}",
55 style::bold("heddle start <topic> --path ../<topic>")
56 );
57 println!("4. Merge, resolve, and keep moving");
58 println!(" {}", style::bold("heddle merge <topic>"));
59 println!(" {}", style::bold("heddle continue"));
60 println!("5. Rejoin upstream Git");
61 println!(" {}", style::bold("heddle sync"));
62 println!();
63 println!("When unsure, run {}", style::bold("heddle doctor"));
64 Ok(())
65}
66
67pub fn cmd_version(cli: &Cli, verbose: bool) -> Result<()> {
68 if !verbose {
69 println!("heddle {}", env!("CARGO_PKG_VERSION"));
70 return Ok(());
71 }
72
73 let git_version = Command::new("git")
74 .arg("--version")
75 .output()
76 .ok()
77 .and_then(|output| {
78 output
79 .status
80 .success()
81 .then(|| String::from_utf8_lossy(&output.stdout).trim().to_string())
82 });
83
84 let repo = std::env::current_dir()
85 .ok()
86 .and_then(|cwd| Repository::open(&cwd).ok());
87 let repository_capability = repo
88 .as_ref()
89 .map(|repo| repo.capability_label().to_string());
90 let repository_root = repo.as_ref().map(|repo| repo.root().display().to_string());
91
92 let output = VersionOutput {
93 version: env!("CARGO_PKG_VERSION"),
94 profile: if cfg!(debug_assertions) {
95 "debug"
96 } else {
97 "release"
98 },
99 features: enabled_features(),
100 git_version,
101 repository_capability,
102 repository_root,
103 };
104
105 if should_output_json(cli, None) {
106 println!("{}", serde_json::to_string(&output)?);
107 return Ok(());
108 }
109
110 println!("Heddle {}", output.version);
111 println!("Build profile: {}", output.profile);
112 println!("Features: {}", output.features.join(", "));
113 if let Some(git_version) = &output.git_version {
114 println!("Git: {git_version}");
115 } else {
116 println!("Git: unavailable");
117 }
118 if let Some(capability) = &output.repository_capability {
119 println!("Repository: {capability}");
120 } else {
121 println!("Repository: not inside a Heddle/Git worktree");
122 }
123 if let Some(root) = &output.repository_root {
124 println!("Root: {root}");
125 }
126 Ok(())
127}
128
129#[allow(clippy::vec_init_then_push)]
135fn enabled_features() -> Vec<&'static str> {
136 let mut features = Vec::new();
137 #[cfg(feature = "weft-client")]
138 features.push("hosted-client");
139 #[cfg(feature = "ingest")]
140 features.push("ingest");
141 #[cfg(feature = "local")]
142 features.push("local");
143 #[cfg(feature = "mount")]
144 features.push("mount");
145 #[cfg(feature = "observability")]
146 features.push("observability");
147 #[cfg(feature = "s3")]
148 features.push("s3");
149 #[cfg(feature = "semantic")]
150 features.push("semantic");
151 #[cfg(feature = "semantic-extended")]
152 features.push("semantic-extended");
153 #[cfg(feature = "zstd")]
154 features.push("zstd");
155 if features.is_empty() {
156 features.push("none");
157 }
158 features
159}