VS Code Extensions
VersionER

VersionER

by MadeByAriel

Central project version HUD — detect, display, and sync versions across your project files.

Downloads

1

Rating

(0)

Version

1.2.0

Last updated

Aug 08, 2026

Central project version HUD for VS Code — detect, display, and sync versions across your project files.

One version lives in your head. It lives in five files. VersionER makes them agree.

The VersionER HUD sitting beside the integrated terminal, reading v2.4.0 for a synced Node project

What it does

VersionER detects your project type, finds every file that carries a version number, and shows the authoritative one in a sidebar HUD and the status bar. Bump it once and every file updates together — or get warned when they've drifted apart.

  • Detects automatically. No configuration for the common cases.
  • Edits surgically. Files are patched with targeted replacements, never parsed and re-serialized, so comments, key order, and formatting survive a bump.
  • Flags drift. When a lockfile or a plist disagrees with the source of truth, the status bar turns amber and the offending file is marked.
  • Confirms before multi-file writes. Touching two or more files always asks first.

The HUD's own panel tab for a Flutter project, flagging a drifted android/app/build.gradle and a missing extra file

Supported projects

Type Primary file Also kept in sync
Node package.json package-lock.json, npm-shrinkwrap.json
Rust Cargo.toml ([package])
Python pyproject.toml ([project] or [tool.poetry]) setup.cfg, an unambiguous __version__
Maven pom.xml (project version, not <parent>)
Gradle build.gradle.kts / build.gradle gradle.properties
.NET Directory.Build.props or *.csproj additional *.csproj
PHP composer.json
Flutter / Dart pubspec.yaml Android build.gradle, iOS Info.plist
Go VERSION — (shows latest git tag as a hint)
Generic VERSION or version.txt

When several markers are present the most specific one wins, so a Flutter project with a package.json for tooling is still treated as Flutter.

Commands

Command Description
VersionER: Show HUD Reveal the HUD
VersionER: Display Options… Change location, layout, theme and more from a quick pick
VersionER: Set Version… Enter an exact version
VersionER: Initiate Version… Give a project its first version — writes VERSION when there is nothing to read
VersionER: Bump Patch 1.2.31.2.4
VersionER: Bump Minor 1.2.31.3.0
VersionER: Bump Major 1.2.32.0.0
VersionER: Refresh Re-read all version files
VersionER: Add Synced File… Pick a file to keep at the project version — see below
VersionER: Remove Synced File… Take an entry back out of .versioner.json

Where the HUD lives

The HUD takes one of six positions in the window, and by default it opens automatically when the window does — without taking keyboard focus off your editor.

versioner.hud.location Where that is
terminalRight (default) Beside the terminal inside the panel, on its right
terminalLeft Beside the terminal inside the panel, on its left
bottom Its own tab in the panel, spanning the whole bottom bar
left A slice of the left side bar, under the file tree
right Alone in the right-hand secondary side bar
rightShared A slice of the right-hand secondary side bar, under the chat panel

The HUD as a slice of the left side bar, under the Explorer file tree

The two terminal* positions, left and rightShared work by moving the HUD into a container something else already occupies — the terminal's, the Explorer's, or whichever container is already sitting in the secondary side bar. A container lays its views out side by side, so the HUD takes a share of the space rather than a tab or a whole side bar of its own; that is exactly what dragging the view there by hand does, and vscode.moveViews is that drag in command form. bottom and right use containers this extension contributes.

Which side of the terminal the HUD lands on comes down to which of the two arrives last: a container shows its views in the order they were added, and moveViews only appends. Two views already sharing a container cannot be reordered — a move whose source and destination are the same container does nothing — so the two sides are two different meetings. For terminalRight the HUD goes to the terminal's container. For terminalLeft the terminal comes to VersionER's panel container instead, which means the panel tab reads VersionER rather than Terminal until you switch away; switching away sends the terminal home.

The same appending rule is what puts rightShared under the chat rather than over it. Nothing enumerates the containers in a part, so VersionER finds the one to join by reading installed extensions' manifests: a container reaches the secondary side bar only by being contributed to it. If nothing has, there is nothing to share with and the choice falls back to right; a container you dragged there yourself is not contributed to it either, so reach those with Move view….

The side bar slices open about 400px tall

left and rightShared ask for the share of the side bar that comes out near 400 pixels on a 1080p window — room for the version, the three bump buttons, the Set field and a short file list, and no more. VS Code applies a view's requested size the first time it appears in a container, which is exactly when the position is picked, so switching to either sets the height and any dragging you do afterwards is yours to keep.

Pixels are not on offer: initialSize in the manifest is a weight, and a view's opening height is weight / (sum of the weights of every visible view in the container) of the container's height, with views that name no weight counting as 20. Next to the Explorer's three — Folders, Outline, Timeline, 60 together — VersionER's 40 asks for 40 %, which is ~400px of a ~1000px-tall side bar and scales with the window from there. Raise or lower initialSize on versioner.hud and versioner.hudSecondary to move it.

right and rightShared need a VS Code build that accepts extension containers in the secondary side bar; confirmed on 1.130 and later. On older builds the choice has nothing to reveal, so use left or Whole panel below instead.

Options, in the HUD

The OPT button in the HUD's title bar turns the view into an options screen. Location is picked off a small picture of the VS Code window: each of the six positions is a hotspot drawn where it actually is, the chosen one inverted, and pointing at any of them names it underneath. The remaining settings are rows of segmented switches. DONE or Escape goes back. VersionER: Display Options… offers the same settings as a quick pick, and both routes write the same settings, so nothing can drift out of step.

Moving the panel

Two of those switches place the panel itself:

  • Whole panelbottom, left, or right. Moves the panel to that edge of the window.
  • Panel widthfull (the whole window width), editor (aligned with the editor area, VS Code's default), left, or right. Only applies while the panel is at the bottom, so the row dims when it isn't.

Both run VS Code's own panel commands, which means they move the whole panel — terminal, Problems, Output and all — not just the HUD. That is not a shortcoming of the switch: VS Code exposes panel placement only as a whole-panel command, and has none that sends a single view to an edge. If you want the HUD alone down an edge, use HUD locationleft or right — instead. The window picture always draws the panel along the bottom, and says so in a note when the panel is docked elsewhere.

VS Code keeps view placement with the rest of its window layout rather than in a setting, so the two can part ways: drag the HUD somewhere else and location will no longer describe where it is. Move view… on the options screen is the way back, and reaches any container — it opens VS Code's own list of destinations for the HUD.

Layout

There are two regions, never three: the version readout, and a narrow side column holding one pane at a time — the bump controls or the synced list, whichever tab at its head is chosen. They ask for the same strip and only one of them is ever being read, so they take turns in it, and the readout keeps everything else. The column is sized to the three bump buttons rather than to a share of the view, and takes a little more width while the list is the pane showing, since file paths need more room than three buttons do.

The two regions arrange themselves to the space the view is given, so the same settings work in a tall side bar and a short panel:

Arrangement Chosen when Shape
Row Short — a panel, wide or shared with the terminal Readout beside the side column
Stack Tall — a side bar Readout above it

The version readout is always centred, and scales to the largest size that still fits on one line. When vertical room runs short the HUD drops its secondary chrome — bump previews, the git-tag hint, then Reload, which the palette carries as VersionER: Refresh — rather than overflowing.

Force one arrangement with versioner.hud.layout (auto, row, stack). Turning off versioner.hud.showFiles takes the tabs away with the list and leaves the controls in sole possession of the column.

Settings

Setting Default Description
versioner.hud.location terminalRight terminalRight, terminalLeft, bottom, left, right, or rightShared — see the table above
versioner.hud.panelSide bottom bottom, left, or right — moves the whole panel, terminal included
versioner.hud.panelAlign center justify, center, left, right — bottom panel only
versioner.hud.openOnStartup true Reveal the HUD on window open, without stealing focus
versioner.hud.layout auto auto, row, or stack
versioner.hud.theme auto auto, dark, or light
versioner.hud.showFiles true Offer the synced-files list as the side column's second tab
versioner.hud.scanlines true Retro CRT scanline overlay
versioner.hud.caretBlink true Blink the caret beside the version, or leave it steady

Version handling

Versions are parsed as SemVer, with two conveniences:

  • A leading v is preserved. v1.2.3 bumps to v1.2.4.
  • Pre-release identifiers are cleared on bump. 1.2.3-rc.11.2.4.
  • Flutter-style build metadata is treated as a counter. 1.0.0+5 patch-bumps to 1.0.1+6; a minor or major bump resets it to +1.

For Android and iOS targets the marketing version and build number are split automatically — versionName / CFBundleShortVersionString get the core version, versionCode / CFBundleVersion get the build number.

Syncing extra files

Any file that carries the version — a docs config, a badge, a constants file — can be kept in step with the rest. The list lives in .versioner.json at your workspace root, and there are three ways onto it that all end in the same file:

  • The options screen. The Synced extras section at the bottom lists every entry with a remove button, and Add file… opens a file picker.
  • The palette. VersionER: Add Synced File… and VersionER: Remove Synced File….
  • By hand. Edit .versioner.json directly — a JSON schema ships with the extension, so you get completion and validation while editing it.

Picking a file is usually all it takes: VersionER reads it looking for the current version. A file that contains nothing but the version is added as-is; a file that carries it somewhere inside gets its sync pattern generated from the line it sits on, and if the version appears on several lines you choose which one. Only when the current version is nowhere in the file do you have to write the pattern yourself.

{
  "files": [
    { "path": "VERSION_BADGE.txt" },
    {
      "path": "docs/conf.py",
      "pattern": "(release\\s*=\\s*\")([^\"]+)(\")"
    }
  ]
}

Without a pattern, the file is treated as containing nothing but the version. With one, the regex needs exactly three capture groups — prefix, version, suffix — and only the middle group is rewritten.

Prompt for AI

If an AI assistant works in this project — Copilot, Cursor, Claude, or otherwise — point it here so it sets the version up the way VersionER expects and raises it through VersionER instead of drifting out of sync. Paste this into its instructions file (.github/copilot-instructions.md, .cursorrules, CLAUDE.md, AGENTS.md, or however your tool takes project rules).

The prompt is written to stand on its own, so it still works for an assistant that never opens this README.

VERSIONING — VersionER is this project's source of truth
========================================================

This project's version number is owned by the VersionER VS Code
extension. VersionER detects the project type, knows every file that
carries the version, and rewrites them together. Treat it as the only
authority: read the version from the files it owns, and change it only
in the ways below.

The rule behind all of it: the version lives in several files at once,
and they must always agree. Editing one of them by itself is the exact
failure VersionER exists to prevent.

Which files carry the version
-----------------------------
Work out the project type from what is in the workspace root, most
specific first, and the primary file follows from it:

  Flutter/Dart  pubspec.yaml    also android/app/build.gradle,
                                ios/Runner/Info.plist
  Node          package.json    also package-lock.json,
                                npm-shrinkwrap.json
  Rust          Cargo.toml      [package] version
  Python        pyproject.toml  [project] or [tool.poetry]; also
                                setup.cfg and an unambiguous __version__
  Maven         pom.xml         the project version, NOT <parent>
  Gradle        build.gradle.kts / build.gradle; also gradle.properties
  .NET          Directory.Build.props or *.csproj; also other *.csproj
  PHP           composer.json
  Go            VERSION         (git tags are only a hint, not truth)
  Generic       VERSION or version.txt

A project with several markers is the most specific one — a Flutter app
with a package.json for tooling is Flutter, not Node.

`.versioner.json` at the workspace root, if present, lists extra files
kept at the same version. Always read it; anything in it counts as a
version file.

1. Setting up a project that has no version yet
-----------------------------------------------
If nothing above holds a version — a fresh repo, or a Go/generic project
with no VERSION file — the HUD reads "No signal" and offers an Init
field. Set the first version once, then stop:

  * Preferred: run `VersionER: Initiate Version…` and enter the starting
    version (1.0.0 unless the user asks otherwise; 0.1.0 for something
    pre-release).
  * If you cannot drive the command palette: write the version into the
    project's primary file from the table above. When the project type
    has no primary file of its own (Go, or nothing recognisable), create
    a `VERSION` file at the workspace root containing the version and a
    trailing newline, and nothing else — that is exactly what
    `VersionER: Initiate Version…` does.

Then, and only then, register the other places the version appears —
a README badge, a docs config, a constants file — by adding them to
`.versioner.json` (section 4) so future bumps carry them along. Do not
invent a version to put in them; use the one just set.

2. Reading the current version
------------------------------
Read it from the primary file, or from VersionER's HUD / status bar if
you have editor access. Never guess it, never carry one over from a
changelog, a git tag, or an earlier message in the conversation, and
never assume a bump you asked for has already landed — re-read.

3. Raising the version
----------------------
Prefer VersionER's own commands, which update every file together in one
confirmed edit:

  VersionER: Bump Patch    1.2.3 -> 1.2.4   fixes, no API change
  VersionER: Bump Minor    1.2.3 -> 1.3.0   new behaviour, compatible
  VersionER: Bump Major    1.2.3 -> 2.0.0   breaking change
  VersionER: Set Version…  an exact version you type

Pick the level from what actually changed, and say which you picked and
why. When in doubt between two levels, ask rather than assume; a bump is
cheap to redo but confusing to a reader who already saw it.

If you can only edit files directly, update the primary file AND every
file listed beside it above AND every entry in `.versioner.json` to the
same version in one change, following VersionER's own rules:

  * SemVer.
  * A leading `v` is preserved: v1.2.3 -> v1.2.4.
  * Pre-release identifiers are cleared on bump: 1.2.3-rc.1 -> 1.2.4.
  * Flutter-style build metadata is a counter: 1.0.0+5 patch-bumps to
    1.0.1+6; a minor or major bump resets it to +1.
  * Android and iOS split the two apart — versionName and
    CFBundleShortVersionString take the core version, versionCode and
    CFBundleVersion take the build number.
  * Patch the version string in place. Do not reformat, re-serialise, or
    re-key the file around it; comments and key order must survive.

4. Keeping extra files in sync
------------------------------
Extra files go in `.versioner.json` at the workspace root:

  {
    "files": [
      { "path": "VERSION_BADGE.txt" },
      { "path": "docs/conf.py",
        "pattern": "(release\\s*=\\s*\")([^\"]+)(\")" }
    ]
  }

Paths are relative to the workspace root and use forward slashes. A file
that contains nothing but the version needs no pattern. A file that
carries it somewhere inside needs a regex with exactly three capture
groups — prefix, version, suffix — and only the middle group is
rewritten. Test the regex against the file's real content before writing
it, and prefer `VersionER: Add Synced File…`, which derives the pattern
from the file itself.

5. After any version change
---------------------------
State the old version, the new version, and every file you touched. If
you edited files by hand, run `VersionER: Refresh` — the HUD and status
bar turn amber and flag DRIFT when a file was missed, which is your
check that the change was complete.

Never do
--------
  * Invent or guess a version number.
  * Edit one version file and leave the others behind.
  * Bump the version as a side effect of an unrelated task, or without
    being asked.
  * Reformat a version file while changing the version in it.
  * Treat a git tag, a changelog heading, or a release note as the
    source of truth. The files above are.

Development

Script Description
npm run build Bundle the extension to dist/ with esbuild
npm run watch Rebuild on change
npm test Unit tests (vitest)
npm run check Typecheck without emitting
npm run smoke Run detection and bumping against fixtures/
npm run preview Render the HUD to .preview/ for checking in a browser
npm run package Build a .vsix

npm run preview renders the real HUD markup against sample snapshots — synced, drifting, and no-project — at the sizes VS Code actually gives a view, then writes .preview/index.html as a contact sheet. It stubs the vscode module rather than launching an extension host, so layout and theme changes can be checked in a second. Press F5 to run the extension for real.

Notes

The HUD is styled in monochrome — black, white and gray, blocky borders, monospace type, and an optional CRT scanline overlay. It follows your VS Code light/dark theme unless you pin it with versioner.hud.theme.

Fonts are bundled with the extension, so the HUD renders identically offline and the webview contacts no external hosts.

Related extensions