Skip to content

Repository files navigation

dsh-semver

Semantic versioning toolbox for DeepSeek Harness (dsh) — parse, compare, range-check, increment, diff, sort, find a range's lowest version and check whether two ranges overlap. Zero runtime dependencies, pure logic, fully deterministic.

Agents are bad at version arithmetic. "Is 2.1.0-rc.1 newer than 2.1.0-beta.11?", "does 1.2.4-beta.1 satisfy ^1.2.3?" and "what does 1.2.4-beta.0 + prerelease become?" are exactly the questions LLMs get wrong — prerelease ordering, tuple-lock, caret/tilde upper bounds and prerelease bump rules are easy to misremember. This plugin turns those questions into deterministic tool calls.

Tools

Tool Description
semver_parse Parse a semver 2.0.0 string into major / minor / patch / prerelease / build, with a precise rejection reason for malformed input (1.2, 1.2.3.4, leading zeros, 1.2.3-01, …)
semver_compare Compare two versions → lt / eq / gt plus a human verdict (2.1.0 > 2.0.3). Build metadata is ignored for precedence (1.0.0+build.1 == 1.0.0)
semver_satisfies Check a version against an npm-style range, with the matched range normalized in the explanation
semver_inc Increment a version following npm semver.inc rules: major / minor / patch / premajor / preminor / prepatch / prerelease, with an optional prerelease identifier
semver_diff Release-type difference between two versions (npm semver.diff): major / minor / patch / premajor / preminor / prepatch / prerelease
semver_sort Sort a list of versions by precedence, ascending or descending, with build metadata as tie-break (npm semver.sort / rsort)
semver_min_version Lowest version a range accepts (npm semver.minVersion): ~1.2.3 → 1.2.3, >1.2 → 1.3.0, <=1.2.3 → 0.0.0; unsatisfiable combinations report satisfiable: false instead of a plausible-looking bound
semver_intersects Whether two ranges can be satisfied by one version (npm semver.intersects) — plus a witness version that satisfies both when they overlap

semver_min_version / semver_intersects — range algebra (v0.3.0)

Two questions that come up whenever an agent has to negotiate versions:

semver_min_version("^1.2.3")            → 1.2.3
semver_min_version(">1.2.3")            → 1.2.4      (> bound with no prerelease bumps patch)
semver_min_version(">1.2")              → 1.3.0      (partial versions follow npm's x-range rule)
semver_min_version("~1.2.3")            → 1.2.3
semver_min_version("<=1.2.3")           → 0.0.0      (no lower bound → the floor)
semver_min_version(">2.0.0 <2.0.1")     → satisfiable: false   (never a guessed bound)

semver_intersects("^1.2.3", "^1.3.0")   → true, witness 1.3.0
semver_intersects("^1.0.0", "^2.0.0")   → false
semver_intersects("^1.0.0 || ^2.0.0", "^2.5.0") → true
  • satisfiable: false is a result, not an error. Unsatisfiable combinations (^0.1.4 ~3.0.2, >=1.0.0 <1.0.0, >2.0.0 <2.0.1) report that no version fits, rather than returning a boundary that looks reasonable but matches nothing.
  • The witness is verified, never guessed. When two ranges overlap, semver_intersects probes the ranges' boundary versions and checks each candidate against both ranges before reporting it — so a reported witness always satisfies both, though it is not claimed to be the lowest one.
  • npm parity is enforced against npm itself. npm semver renders ~1.2.3 as >=1.2.3 <1.3.0; a hand-written expectation would have missed that 1.2.0 used to be accepted. See Testing below.

semver_inc — npm increment semantics (v0.2.0)

  • Plain bumps: 1.2.3 + major → 2.0.0, + minor → 1.3.0, + patch → 1.2.4.
  • Pre bumps with identifier: 1.2.3 + preminor (rc) → 1.3.0-rc.0; without identifier → 1.3.0-0.
  • Prerelease counter: 1.2.4-beta.0 + prerelease → 1.2.4-beta.1; 1.2.4-beta + prerelease → 1.2.4-beta.0; switching identifier restarts the counter (prerelease with rc → 1.2.4-rc.0).
  • npm quirks preserved: a pre-major/pre-minor/pre-patch version releases without incrementing (1.0.0-5 + major → 1.0.0); 1.2.0-5 + prerelease → 1.2.0-6 (no patch bump); build metadata is dropped; dotted identifiers are allowed (premajor with beta.1 → 2.0.0-beta.1.0); identifiers with leading zeros are rejected.

semver_diff — npm difference semantics (v0.2.0)

1.2.3 → 2.0.0 = major; 1.2.3 → 2.0.0-beta.1 = premajor; 1.2.4-beta.1 → 1.2.4-beta.2 = prerelease. npm special cases preserved: 1.2.3 vs 1.2.3-beta.1 → patch, 1.0.0-1 vs 1.0.0 → major. Equal precedence (build metadata ignored) → equal: true with no difference key.

Supported range syntax (npm-compatible subset)

  • Exact: 1.2.3, =1.2.3
  • Comparisons: >1.2.0, >=1.2.0, <2.0.0, <=2.0.0 — partial versions follow npm's x-range rewrite (>1.2 → >=1.3.0, <=1.2 → <1.3.0, <1.2 → <1.2.0, >=1.2 → >=1.2.0; npm compiles >* / <* to a range nothing satisfies)
  • Caret: ^1.2.3 → >=1.2.3 <2.0.0, with the 0.x rules (^0.2.3 → >=0.2.3 <0.3.0, ^0.0.3 → >=0.0.3 <0.0.4)
  • Tilde: ~1.2.3 → >=1.2.3 <1.3.0, ~1.2 → >=1.2.0 <1.3.0, ~1 → >=1.0.0 <2.0.0 (fixed in v0.3.0 — the patch component used to be dropped from the lower bound, so 1.2.0 wrongly matched ~1.2.3)
  • Wildcards: 1.2.x, 1.x, * (bare partials 1.2 / 1 behave the same)
  • Hyphen ranges: 1.2.3 - 2.3.4 (inclusive endpoints; partial upper endpoints like 1.2.3 - 2.3 → <2.4.0)
  • AND / OR: >=1.2.0 <2.0.0, ^1.2.3 || >=3.0.0

Prerelease semantics (npm tuple-lock)

  • A plain range never matches prerelease versions: 1.2.4-beta.1 does not satisfy ^1.2.3.
  • A range with a prerelease lower bound only matches prereleases of the same [major, minor, patch] tuple: 1.2.3-beta.5 satisfies ^1.2.3-beta.2, but 1.2.4-beta.1 does not (releases like 1.2.3 still match).
  • Set includePrerelease: true (tool parameter or plugin config) to relax both rules.

Install

# into the web profile (dsh plugin has no default profile — always pass --profile)
dsh plugin --profile web add github:TYEclipse/dsh-semver

Verify the layer mounted:

dsh --profile web --dump-config | grep '== dsh-semver'

Usage examples

semver_parse("2.1.0-rc.1+build.42")
  → major 2, minor 1, patch 0, prerelease [rc, 1], build [build, 42]

semver_compare("2.1.0", "2.0.3")
  → "2.1.0 > 2.0.3" (gt)

semver_satisfies("1.2.4-beta.1", "^1.2.3")
  → false — prerelease does not match a plain range

semver_inc("1.2.4-beta.0", "prerelease", "beta")
  → 1.2.4-beta.1

semver_inc("1.2.3", "preminor", "rc")
  → 1.3.0-rc.0

semver_diff("1.2.3", "2.0.0-beta.1")
  → premajor

semver_sort(["1.2.3", "1.2.10", "1.0.0"], "desc")
  → ["1.2.10", "1.2.3", "1.0.0"]

semver_min_version(">=1.2.0 <2.0.0")
  → 1.2.0

semver_intersects("^1.2.3", ">=1.3.0 <1.5.0")
  → true — e.g. 1.3.0 satisfies both

Safety

All eight tools are pure and offline: no network, no filesystem access, no shell, no dynamic evaluation. Inputs are validated with strict regexes and numeric checks; malformed versions, ranges and identifiers return explicit reasons instead of throwing.

Testing

pnpm test           # vitest — 132 tests

Tool semantics are anchored to npm's own semver package as an independent oracle, never to hand-written expectations:

test/oracle/range-algebra-oracle.mjs   # regenerates the anchors from npm semver
test/anchors/range-algebra.json        # 54 ranges · 400 range pairs · 864 matcher cases

With npm i semver@7 available, node test/oracle/range-algebra-oracle.mjs rewrites the anchors; the test suite sweeps the plugin's matcher, semver_min_version and semver_intersects against every one of them. The ~1.2.3 lower-bound bug fixed in v0.3.0 was found exactly this way — its v0.2.x expectation had been chosen by hand and sat outside the wrong bound.

Development

pnpm install        # dev dependencies only (esbuild allowBuilds is pre-configured)
pnpm build          # tsc → dist/ (committed; git installs do not run build scripts)
pnpm test           # vitest — 132 tests; anchors generated by npm semver 7.8.5 (independent oracle)
pnpm lint           # oxlint (src + test only)

License

MIT


中文简介

dsh-semver 是 DeepSeek Harness 的语义化版本号工具箱:解析(semver_parse)、比较(semver_compare,遵循 semver 2.0.0 优先级规则,build 元数据不影响比较)、范围匹配(semver_satisfies,支持 ^/~/>=/<=/通配符/连字符区间/||,含 npm 的 prerelease tuple-lock 语义),以及 v0.2.0 新增的版本号递增(semver_inc,npm semver.inc 全规则:major/minor/patch/premajor/preminor/prepatch/prerelease + 可选预发布标识符)、版本差异判定(semver_diff,npm semver.diff 语义,含 prerelease 特殊分支)与版本列表排序(semver_sort,升/降序,build 元数据作次序裁决)。零运行时依赖、纯逻辑、确定性输出——LLM 经常算错的版本号问题(如 1.0.0-beta.11 > 1.0.0-beta.2、1.2.4-beta.0 递增后是 beta.1 而非 beta.01、1.2.4-beta.1 不满足 ^1.2.3)交给工具一次性算对。

About

Semantic versioning toolbox for DeepSeek Harness (dsh): parse, compare, range-check, increment, diff, sort, find a range's lowest version and check whether two ranges overlap (npm semver.inc/diff/sort/minVersion/intersects semantics) — zero runtime dependencies, pure logic

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages