claude_code_agent_sdk/
version.rs

1//! Version information for the Claude Agent SDK
2
3use std::sync::OnceLock;
4
5/// The version of this SDK
6pub const SDK_VERSION: &str = env!("CARGO_PKG_VERSION");
7
8/// Minimum required Claude Code CLI version
9pub const MIN_CLI_VERSION: &str = "2.0.0";
10
11/// Environment variable to skip version check
12pub const SKIP_VERSION_CHECK_ENV: &str = "CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK";
13
14/// Entrypoint identifier for subprocess
15pub const ENTRYPOINT: &str = "sdk-rs";
16
17/// Parse a semantic version string into (major, minor, patch)
18pub fn parse_version(version: &str) -> Option<(u32, u32, u32)> {
19    let parts: Vec<&str> = version.trim_start_matches('v').split('.').collect();
20    if parts.len() < 3 {
21        return None;
22    }
23
24    let major = parts[0].parse().ok()?;
25    let minor = parts[1].parse().ok()?;
26    let patch = parts[2].parse().ok()?;
27
28    Some((major, minor, patch))
29}
30
31/// Check if the CLI version meets the minimum requirement
32pub fn check_version(cli_version: &str) -> bool {
33    let Some((cli_maj, cli_min, cli_patch)) = parse_version(cli_version) else {
34        return false;
35    };
36
37    let Some((req_maj, req_min, req_patch)) = parse_version(MIN_CLI_VERSION) else {
38        return false;
39    };
40
41    if cli_maj > req_maj {
42        return true;
43    }
44    if cli_maj < req_maj {
45        return false;
46    }
47
48    // Major versions are equal
49    if cli_min > req_min {
50        return true;
51    }
52    if cli_min < req_min {
53        return false;
54    }
55
56    // Major and minor are equal
57    cli_patch >= req_patch
58}
59
60/// Cached Claude Code CLI version
61static CLAUDE_CODE_VERSION: OnceLock<Option<String>> = OnceLock::new();
62
63/// Get Claude Code CLI version
64///
65/// This function uses OnceLock to cache the result, so the CLI is only called once.
66/// Returns None if CLI is not found or version cannot be determined.
67pub fn get_claude_code_version() -> Option<&'static str> {
68    CLAUDE_CODE_VERSION
69        .get_or_init(|| {
70            std::process::Command::new("claude")
71                .arg("--version")
72                .output()
73                .ok()
74                .filter(|output| output.status.success())
75                .and_then(|output| {
76                    let version_output = String::from_utf8_lossy(&output.stdout);
77                    version_output
78                        .lines()
79                        .next()
80                        .and_then(|line| line.split_whitespace().next())
81                        .map(|v| v.trim().to_string())
82                })
83        })
84        .as_deref()
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_parse_version() {
93        assert_eq!(parse_version("1.2.3"), Some((1, 2, 3)));
94        assert_eq!(parse_version("v1.2.3"), Some((1, 2, 3)));
95        assert_eq!(parse_version("10.20.30"), Some((10, 20, 30)));
96        assert_eq!(parse_version("1.2"), None);
97        assert_eq!(parse_version("invalid"), None);
98    }
99
100    #[test]
101    fn test_check_version() {
102        assert!(check_version("2.0.0"));
103        assert!(check_version("2.0.1"));
104        assert!(check_version("2.1.0"));
105        assert!(check_version("3.0.0"));
106        assert!(!check_version("1.9.9"));
107        assert!(!check_version("1.99.99"));
108    }
109}