# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

- Install deps: `bundle install`
- Run tests: `bundle exec rspec`
- Run a single test: `bundle exec rspec spec/maven_spec.rb` or `bundle exec rspec spec/maven_spec.rb:42`
- Lint: `bundle exec rubocop`
- Console with the gem loaded: `bin/console`
- CLI entrypoint (during development): `bundle exec exe/semver_dialects <command> ...` (commands: `check_version`, `sort`)
- Verify version sync across `lib/semver_dialects/version.rb`, `Gemfile.lock`, and `CHANGELOG.md`: `ruby scripts/version_verification.rb`

Supported Ruby versions: 3.2–3.4 (CI runs the matrix on amd64 and arm64).

## Releasing

Bumping a version requires three files to stay in sync or `version verification` CI fails:
1. `lib/semver_dialects/version.rb`
2. `CHANGELOG.md` (add a `## vX.Y.Z` heading)
3. `Gemfile.lock` (run `bundle lock` after the version bump)

After merging to `master`, releases are pushed to rubygems via the `publish gem` CI job (stage `deploy`, runs automatically on `v*.*.*` tag pipelines). The job runs `ruby scripts/version_verification.rb "$CI_COMMIT_TAG"` (which asserts the version triple is in sync **and** that the tag matches `SemverDialects::VERSION`), then:

```
gem build -o semver_dialects.gem semver_dialects.gemspec
gem push semver_dialects.gem
```

To cut a release, use the `create_release` skill (in `.claude/skills/create_release/`), which creates the GitLab release (and tag) with `glab release create`. The tag pipeline runs `publish gem` automatically; the skill then watches it with `glab ci trace` and verifies the gem on RubyGems.

## Architecture

The gem parses versions and matches them against constraints across many package ecosystems (maven, npm, go, pypi, nuget, gem, packagist, conan, cargo, swift, pub, apk, deb, rpm). It is organized into two orthogonal layers:

### 1. Version parsing (per-dialect)

Each ecosystem has its own version semantics, so `SemverDialects.parse_version(type, raw)` in `lib/semver_dialects.rb` dispatches to a per-dialect parser:

- `Maven::VersionParser`, `Apk::VersionParser`, `Rpm::VersionParser` — custom parsers for non-semver schemes.
- `Semver2::VersionParser` — used by npm, go, nuget, cargo, swift, pub (with small per-type quirks like `nuget` downcasing or `npm` stripping leading `v`/`=`).
- `SemanticVersion` — a looser semver-ish wrapper used by pypi, packagist, conan.
- `Gem::Version` (stdlib) for `gem`.
- `DebVersion` (from the `deb_version` gem) for `deb`.

All parsed versions are `Comparable`. `InvalidVersionError` (and subclass `IncompleteScanError`) is raised on parse failure; `ArgumentError` from `Gem::Version` is rescued and rethrown as `InvalidVersionError`.

### 2. Constraint / range matching (interval arithmetic)

Constraints are normalized into linear intervals over the version space, regardless of dialect. The pipeline is:

```
raw constraint string
  → IntervalSetParser.translate(type, str)   # split dialect-specific syntax into interval strings
  → IntervalParser.parse(type, interval_str) # build an Interval with two Boundaries
  → IntervalSet                              # disjunction of Intervals
  → .overlaps_with?(Interval.from_version(v))
```

Key types in `lib/semver_dialects/`:
- `Boundary` — a version + open/closed flag, plus ±infinity sentinels.
- `Interval` — pair of boundaries; supports `intersect`, `from_version`.
- `IntervalSet` — union of intervals with `<<`/`add` and `overlaps_with?`.
- `IntervalParser` / `IntervalSetParser` — dialect-aware string-to-interval conversion (one `translate_<type>` method per ecosystem in `interval_set_parser.rb`).

`SemverDialects.version_satisfies?` is the top-level entry point that wires both layers together.

### OS package types are special

`deb`, `rpm`, `apk` do not go through the interval-set pipeline. `version_satisfies?` routes them through `os_pkg_version_satisfies?`, which only accepts a `<X` constraint (this is the only operator currently emitted by the GitLab advisory exporter for OS packages). Other operators raise `InvalidConstraintError`.

### CLI

`exe/semver_dialects` loads `SemverDialects::CLI` (`lib/semver_dialects/cli.rb`), which dispatches to command classes under `lib/semver_dialects/commands/` (`check_version.rb`, `sort_versions.rb`).

## Adding a new package type

1. Add the type to the array in `SemverDialects.supported_package_types`.
2. Add a `when '<type>'` branch in `SemverDialects.parse_version` (reuse `Semver2::VersionParser` if the type is semver-2 compatible).
3. Add `translate_<type>` and `when '<type>'` branches in `IntervalSetParser` and `IntervalParser` if the type has constraint syntax.
4. Add specs alongside (`spec/<type>_spec.rb`) and update `README.md`'s supported-syntaxes table.
