mise-versioning
Drop a uniform version / version:bump-* task interface onto any repo, regardless of stack.
All bump logic lives in one installed file (.mise/scripts/versioning.sh); mise tasks are thin
wrappers. Every version-bearing file in the repo is kept at the same semver — the highest wins.
The contract this skill guarantees
After init, the repo exposes exactly these mise tasks (and nothing the user must learn beyond them):
| task |
effect |
version |
print current version as vX.Y.Z (the highest across all tracked files) |
version:bump / version:bump-patch |
vX.Y.Z → vX.Y.(Z+1) (alias — same task) |
version:bump-minor |
vX.Y.Z → vX.(Y+1).0 |
version:bump-major |
vX.Y.Z → v(X+1).0.0 |
version:check |
exit nonzero + list drift if files disagree |
version:sync |
force every file up to the highest version |
Every bump rewrites all tracked files in their native format and (if tracked) creates a
vX.Y.Z git tag, so a frontend package.json and a backend pyproject.toml never diverge.
Running init
init is the single entry point. It is idempotent — safe to re-run.
bash scripts/init.sh # run from inside the target repo (cwd anywhere within it)
# flags: --force (re-discover the manifest) --seed X.Y.Z (initial version) --no-git-tag
What it does, in order:
- Ensure mise. If
mise.toml is absent, create a minimal one.
- Install the engine. Copy
assets/versioning.sh → .mise/scripts/versioning.sh (chmod +x).
- Discover version files → write
.mise/version-files.conf (a <type> <path> manifest).
Skipped if a manifest already exists unless --force.
- Merge the tasks into
mise.toml inside a managed # >>> mise-versioning >>> block
(replaced wholesale on re-run, never duplicated).
- Resolve parity. Run
version:check; if files disagree, version:sync to the highest.
- Wrap the build. If
mise.toml has a [tasks.build], add depends = ["version:bump-patch"].
- Flag hardcoded self-reported versions. Scan source for semver literals fed to
--version-style surfaces (.version("X.Y.Z"), __version__ = …, version: "X.Y.Z") and
warn — bumps never rewrite these, so the artifact would lie about itself. Fixing is yours
(see below); init only detects.
After running, review .mise/version-files.conf — it is hand-editable and authoritative.
Add or remove entries the auto-discovery missed before relying on bumps.
After init: judgment the script leaves to you
init handles the mechanical 90%. Finish these by hand when they apply:
How parity & "highest wins" work
The canonical version is computed fresh on every call as the max semver across all manifest
entries. There is no separate "source of truth" file — any tracked file can carry the version, and
they are reconciled upward. This is what lets init adopt a repo whose files already disagree:
version:sync pulls the laggards up to the leader. Bumps always start from the canonical value, so
a stray un-bumped file can never silently lower the version.
Supported file types
json · toml · cargo · csproj · gradle · plain · gittag. Read/write rules, the
column-0 anchoring that protects dependency pins, and how to add a new type are in
references/file-types.md.
Troubleshooting
When a bump touches the wrong line, check flags drift after init, the build doesn't bump, or
current reads v0.0.0 in a versioned repo — see references/gotchas.md.
Out of scope
- Changelogs / release notes. This skill moves version numbers; it does not write release
prose. Use the
product-changelog skill.
- Publishing / releasing. No
npm publish, uv publish, cargo publish, GitHub Releases, or
git push --tags. Bumps and tags stay local; wire release steps separately.
- Deriving versions from commit history (Conventional Commits, semantic-release, release-please).
This workflow is explicit-bump, not commit-inferred. If the user wants commit-driven versioning,
that's a different tool — don't bolt it on here.
- Non-version mise configuration (tool pinning, env, unrelated tasks). init touches only its
managed block and an existing
build task.
1---2name: mise-versioning3description: Provision any repo with a stack-agnostic semantic-versioning workflow on mise tasks. `init` installs `.mise/scripts/versioning.sh` and wires `version`, `version:bump[-patch|-minor|-major]`, `version:check`, `version:sync`. Discovers every version-bearing file (package.json, pyproject.toml, Cargo.toml, *.csproj, build.gradle, VERSION, git tags) and keeps them in parity — highest semver wins. Wraps build tasks to bump-patch first, and flags hardcoded self-reported version literals (commander `.version("1.0.0")`, `__version__`, MCP server info) so `--version` can't lie after bumps — derive from the manifest at runtime instead. Use when the user says "add versioning", "set up version bumping", "init versioning", "version this repo", "mise version tasks", "semver workflow", "keep versions in sync", "bump the version across all files", or "--version reports the wrong/stale version". Do NOT use for changelogs/release notes (use product-changelog), publishing to npm/PyPI/crates, or unrelated mise config.4---56# mise-versioning78Drop a uniform `version` / `version:bump-*` task interface onto any repo, regardless of stack.9All bump logic lives in one installed file (`.mise/scripts/versioning.sh`); mise tasks are thin10wrappers. Every version-bearing file in the repo is kept at the same semver — the highest wins.1112## The contract this skill guarantees1314After `init`, the repo exposes exactly these mise tasks (and nothing the user must learn beyond them):1516| task | effect |17|---|---|18| `version` | print current version as `vX.Y.Z` (the highest across all tracked files) |19| `version:bump` / `version:bump-patch` | `vX.Y.Z` → `vX.Y.(Z+1)` (alias — same task) |20| `version:bump-minor` | `vX.Y.Z` → `vX.(Y+1).0` |21| `version:bump-major` | `vX.Y.Z` → `v(X+1).0.0` |22| `version:check` | exit nonzero + list drift if files disagree |23| `version:sync` | force every file up to the highest version |2425Every bump rewrites **all** tracked files in their native format and (if tracked) creates a26`vX.Y.Z` git tag, so a frontend `package.json` and a backend `pyproject.toml` never diverge.2728## Running `init`2930`init` is the single entry point. It is idempotent — safe to re-run.3132```bash33bash scripts/init.sh # run from inside the target repo (cwd anywhere within it)34# flags: --force (re-discover the manifest) --seed X.Y.Z (initial version) --no-git-tag35```3637What it does, in order:38391. **Ensure mise.** If `mise.toml` is absent, create a minimal one.402. **Install the engine.** Copy `assets/versioning.sh` → `.mise/scripts/versioning.sh` (chmod +x).413. **Discover version files** → write `.mise/version-files.conf` (a `<type> <path>` manifest).42 Skipped if a manifest already exists unless `--force`.434. **Merge the tasks** into `mise.toml` inside a managed `# >>> mise-versioning >>>` block44 (replaced wholesale on re-run, never duplicated).455. **Resolve parity.** Run `version:check`; if files disagree, `version:sync` to the highest.466. **Wrap the build.** If `mise.toml` has a `[tasks.build]`, add `depends = ["version:bump-patch"]`.477. **Flag hardcoded self-reported versions.** Scan source for semver literals fed to48 `--version`-style surfaces (`.version("X.Y.Z")`, `__version__ = …`, `version: "X.Y.Z"`) and49 warn — bumps never rewrite these, so the artifact would lie about itself. Fixing is yours50 (see below); init only detects.5152After running, **review `.mise/version-files.conf`** — it is hand-editable and authoritative.53Add or remove entries the auto-discovery missed before relying on bumps.5455## After init: judgment the script leaves to you5657`init` handles the mechanical 90%. Finish these by hand when they apply:5859- **Foreign build runners.** init only auto-wires an existing mise `[tasks.build]`. If the repo60 builds via `npm run build`, a Makefile, `cargo build`, etc., wrap it so the version bumps first:61 ```toml62 [tasks.build]63 depends = ["version:bump-patch"]64 run = "pnpm build"65 ```66 Invoke `mise run build` thereafter, not the underlying tool.67- **Build task that already had `depends`.** init injects a `# NOTE:` rather than guessing merge68 order. Combine into one array with the bump first: `depends = ["version:bump-patch", "…"]`.69- **Unusual version locations.** If a config file's version isn't matched (indented TOML, a70 non-standard property), prefer a canonical `plain` `VERSION` file over fighting the matcher.71 See [references/file-types.md](./references/file-types.md).72- **Self-reported versions — the artifact must derive, not duplicate.** A literal like73 commander's `.version("1.0.0")` or `McpServer({ version: "1.0.0" })` is invisible to the74 manifest and lies a little more every bump (real incident: a CLI shipped as 1.1.3 while75 `--version` said 1.0.0). Make the program read its version from the tracked manifest at76 runtime (Node walk-up to `package.json`, Python `importlib.metadata`, Rust77 `env!("CARGO_PKG_VERSION")`, Go ldflags) — recipes and a verify ritual in78 [references/self-reported-version.md](./references/self-reported-version.md). Then prove it:79 `<artifact> --version` must equal `mise run version`. Never "fix" this by regex-tracking the80 source file in the conf.8182## How parity & "highest wins" work8384The canonical version is computed fresh on every call as the max semver across all manifest85entries. There is no separate "source of truth" file — any tracked file can carry the version, and86they are reconciled upward. This is what lets `init` adopt a repo whose files already disagree:87`version:sync` pulls the laggards up to the leader. Bumps always start from the canonical value, so88a stray un-bumped file can never silently lower the version.8990## Supported file types9192`json` · `toml` · `cargo` · `csproj` · `gradle` · `plain` · `gittag`. Read/write rules, the93column-0 anchoring that protects dependency pins, and how to add a new type are in94[references/file-types.md](./references/file-types.md).9596## Troubleshooting9798When a bump touches the wrong line, `check` flags drift after init, the build doesn't bump, or99`current` reads `v0.0.0` in a versioned repo — see [references/gotchas.md](./references/gotchas.md).100101## Out of scope102103- **Changelogs / release notes.** This skill moves version numbers; it does not write release104 prose. Use the `product-changelog` skill.105- **Publishing / releasing.** No `npm publish`, `uv publish`, `cargo publish`, GitHub Releases, or106 `git push --tags`. Bumps and tags stay local; wire release steps separately.107- **Deriving versions from commit history** (Conventional Commits, semantic-release, release-please).108 This workflow is explicit-bump, not commit-inferred. If the user wants commit-driven versioning,109 that's a different tool — don't bolt it on here.110- **Non-version mise configuration** (tool pinning, env, unrelated tasks). init touches only its111 managed block and an existing `build` task.