ClaudeWrapper.CliVersion (ClaudeWrapper v0.8.0)

Copy Markdown View Source

Parse and compare the claude CLI's reported version.

claude --version prints a line like "2.1.71 (Claude Code)". This module turns that into a comparable t/0 struct and provides the two checks a host typically wants:

  • satisfies_minimum?/2 / check_version/2 -- a hard floor. Below the minimum the wrapper is known to misbehave (missing flags, different argument shapes), so the caller should refuse to run.
  • status_within/3 -- a soft "drift" classification against a tested-against [minimum, maximum] window. A version above the maximum hasn't been verified; semantics may have drifted but the wrapper should generally still work, so the caller should warn rather than refuse.

This is the Elixir port of the Rust crate's CliVersion / CliVersionStatus (../claude-wrapper/src/version.rs). Versions are treated as three-part semver (major.minor.patch); any trailing metadata such as the (Claude Code) suffix is ignored during parsing.

Example

iex> {:ok, found} = ClaudeWrapper.CliVersion.parse("2.1.71 (Claude Code)")
iex> ClaudeWrapper.CliVersion.satisfies_minimum?(found, ClaudeWrapper.CliVersion.new(2, 1, 0))
true
iex> ClaudeWrapper.CliVersion.status_within(found, ClaudeWrapper.CliVersion.new(2, 1, 0), ClaudeWrapper.CliVersion.new(2, 1, 999))
:tested

Summary

Types

Classification of a found version against a tested [minimum, maximum] range. Returned by status_within/3.

t()

Functions

Enforce a minimum version as a tagged-tuple result.

Compare two versions, returning :lt, :eq, or :gt.

Build a version from its three components.

Parse a version from the output of claude --version.

True when found is greater than or equal to minimum.

Classify found against a tested-against [minimum, maximum] range (both ends inclusive). See status/0 for the three outcomes.

Render a version as "major.minor.patch".

Types

status()

@type status() ::
  :tested
  | {:newer_untested, found :: t(), tested_max :: t()}
  | {:older_than_minimum, found :: t(), minimum :: t()}

Classification of a found version against a tested [minimum, maximum] range. Returned by status_within/3.

  • :tested -- within the range (inclusive on both ends); safe to run.
  • {:newer_untested, found, tested_max} -- above the maximum. The wrapper hasn't been verified against this CLI; warn but proceed.
  • {:older_than_minimum, found, minimum} -- below the minimum. The wrapper is known to behave incorrectly; refuse to run.

t()

@type t() :: %ClaudeWrapper.CliVersion{
  major: non_neg_integer(),
  minor: non_neg_integer(),
  patch: non_neg_integer()
}

Functions

check_version(found, minimum)

@spec check_version(t(), t()) :: :ok | {:error, ClaudeWrapper.Error.t()}

Enforce a minimum version as a tagged-tuple result.

Returns :ok when found satisfies minimum, otherwise {:error, %ClaudeWrapper.Error{kind: :version_mismatch}} whose :reason is %{found: found, minimum: minimum}.

iex> v = ClaudeWrapper.CliVersion.new(2, 1, 71)
iex> ClaudeWrapper.CliVersion.check_version(v, ClaudeWrapper.CliVersion.new(2, 1, 0))
:ok
iex> ClaudeWrapper.CliVersion.check_version(v, ClaudeWrapper.CliVersion.new(2, 2, 0))
{:error, %ClaudeWrapper.Error{kind: :version_mismatch, reason: %{found: %ClaudeWrapper.CliVersion{major: 2, minor: 1, patch: 71}, minimum: %ClaudeWrapper.CliVersion{major: 2, minor: 2, patch: 0}}}}

compare(a, b)

@spec compare(t(), t()) :: :lt | :eq | :gt

Compare two versions, returning :lt, :eq, or :gt.

Ordering is by major, then minor, then patch. The shape is suitable as the second argument to Enum.sort/2.

iex> Enum.sort(
...>   [ClaudeWrapper.CliVersion.new(2, 1, 71), ClaudeWrapper.CliVersion.new(2, 1, 0)],
...>   ClaudeWrapper.CliVersion
...> )
[%ClaudeWrapper.CliVersion{major: 2, minor: 1, patch: 0}, %ClaudeWrapper.CliVersion{major: 2, minor: 1, patch: 71}]

new(major, minor, patch)

Build a version from its three components.

iex> ClaudeWrapper.CliVersion.new(2, 1, 71)
%ClaudeWrapper.CliVersion{major: 2, minor: 1, patch: 71}

parse(output)

@spec parse(String.t()) :: {:ok, t()} | {:error, ClaudeWrapper.Error.t()}

Parse a version from the output of claude --version.

Accepts a bare "2.1.71", the full "2.1.71 (Claude Code)" form, a leading v ("v2.1.71"), and surrounding whitespace. Only the first whitespace-delimited token is considered, and it must be exactly three dot-separated non-negative integers.

Returns {:error, %ClaudeWrapper.Error{kind: :invalid_version}} for anything else, with the original (untrimmed) string carried in :reason.

iex> ClaudeWrapper.CliVersion.parse("  2.1.71 (Claude Code)\n")
{:ok, %ClaudeWrapper.CliVersion{major: 2, minor: 1, patch: 71}}

iex> ClaudeWrapper.CliVersion.parse("2.1")
{:error, %ClaudeWrapper.Error{kind: :invalid_version, reason: "2.1"}}

satisfies_minimum?(found, minimum)

@spec satisfies_minimum?(t(), t()) :: boolean()

True when found is greater than or equal to minimum.

iex> v = ClaudeWrapper.CliVersion.new(2, 1, 71)
iex> ClaudeWrapper.CliVersion.satisfies_minimum?(v, ClaudeWrapper.CliVersion.new(2, 1, 0))
true
iex> ClaudeWrapper.CliVersion.satisfies_minimum?(v, ClaudeWrapper.CliVersion.new(2, 2, 0))
false

status_within(found, minimum, maximum)

@spec status_within(t(), t(), t()) :: status()

Classify found against a tested-against [minimum, maximum] range (both ends inclusive). See status/0 for the three outcomes.

iex> tested = ClaudeWrapper.CliVersion.status_within(
...>   ClaudeWrapper.CliVersion.new(2, 1, 143),
...>   ClaudeWrapper.CliVersion.new(2, 1, 0),
...>   ClaudeWrapper.CliVersion.new(2, 1, 999)
...> )
iex> tested
:tested

to_string(cli_version)

@spec to_string(t()) :: String.t()

Render a version as "major.minor.patch".

The String.Chars protocol is also implemented, so to_string/1 and string interpolation work too.

iex> ClaudeWrapper.CliVersion.to_string(ClaudeWrapper.CliVersion.new(2, 1, 71))
"2.1.71"