agent_shell_parser/
lib.rs1use std::path::Path;
2use std::process::Command;
3
4pub mod hook;
5pub mod parse;
6pub mod path;
7
8#[derive(Debug, thiserror::Error)]
9pub enum Error {
10 #[error("failed to read stdin: {0}")]
11 Stdin(#[from] std::io::Error),
12 #[error("failed to parse JSON input: {0}")]
13 Json(#[from] serde_json::Error),
14 #[error("jj command failed: {0}")]
15 Jj(String),
16}
17
18pub fn is_jj_colocated(cwd: &Path) -> bool {
19 Command::new("jj")
20 .arg("root")
21 .current_dir(cwd)
22 .stdout(std::process::Stdio::null())
23 .stderr(std::process::Stdio::null())
24 .status()
25 .map(|s| s.success())
26 .unwrap_or(false)
27}
28
29pub fn jj_version() -> Option<(u32, u32, u32)> {
30 let output = Command::new("jj").arg("--version").output().ok()?;
31 let text = String::from_utf8_lossy(&output.stdout);
32 let version_str = text.strip_prefix("jj ")?;
33 let mut parts = version_str.trim().split('.');
34 let major = parts.next()?.parse().ok()?;
35 let minor = parts.next()?.parse().ok()?;
36 let patch = parts.next().and_then(|p| p.parse().ok()).unwrap_or(0);
37 Some((major, minor, patch))
38}
39
40pub fn require_jj_version(min_major: u32, min_minor: u32) -> Result<(), String> {
41 match jj_version() {
42 None => Err("jj-cli not found. Install with: cargo install --locked jj-cli".into()),
43 Some((major, minor, _))
44 if major < min_major || (major == min_major && minor < min_minor) =>
45 {
46 Err(format!(
47 "jj-cli {major}.{minor} found, but >= {min_major}.{min_minor} required. \
48 Upgrade with: cargo install --locked jj-cli"
49 ))
50 }
51 Some(_) => Ok(()),
52 }
53}