DEV Community

Cover image for I built my own release CLI because I wanted full control over my changelogs.
Ajax
Ajax

Posted on

I built my own release CLI because I wanted full control over my changelogs.

I was tired of doing releases by hand. Not the deploy part — the ceremony around it. Remembering the right git commands, keeping the changelog consistent, not forgetting to tag before pushing.

So I built VIT. A guided CLI that walks you through each step: bump → changelog → commit → tag → push → deploy. Interactive by default, fully headless when you need it.

npm install -g @ajax-16/vit
Enter fullscreen mode Exit fullscreen mode

It started with the changelog

Most tools auto-generate changelogs from commits. That's fine, but I wanted to write mine. Decide what goes in, how it's worded, what gets highlighted.

So that's where VIT started — a guided prompt to write changelog entries:

? Change type: feat
? Scope (optional): auth
? Change description: add OAuth2 login
? Is this a breaking change? No
  ✔ Entry added
? Add another entry? No

## [1.2.0] - 27/04/2026

### 🚀 Features

- *(auth)* add OAuth2 login
Enter fullscreen mode Exit fullscreen mode

Preview, confirm, saved. That's it.


Semantic mode when you want it

If you'd rather generate the changelog from your git history, flip semantic: true in the config. VIT reads your conventional commits and gives you a checkbox to pick what goes in:

? Select commits to include in v1.2.0:
  ✔ feat(auth): add OAuth2 login
  ✔ fix: correct timeout on slow requests
  ✗ docs: update README   ← deselect whatever you don't want
Enter fullscreen mode Exit fullscreen mode

Both modes coexist. Toggle per project via config or on the fly with --semantic.


Secrets asked at runtime, never stored

This is the feature I'm most proud of. promptEnv asks for sensitive values right before the command runs — no .env files, no hardcoded credentials:

{
  "id": "deploy",
  "promptEnv": [
    { "name": "SSH_PASS", "message": "SSH password:" },
    { "name": "DEPLOY_TOKEN", "message": "Deploy token:" }
  ],
  "command": "sshpass -p ${SSH_PASS} scp -r ./dist user@server:/var/www/${DEPLOY_TOKEN}"
}
Enter fullscreen mode Exit fullscreen mode

Even in --yes headless mode, promptEnv always stops and waits. Intentionally.


Pipeline with captureAs

Actions can chain commands and pass results forward:

{
  "pipeline": [
    {
      "command": "node -e \"process.stdout.write(require('./package.json').version)\"",
      "captureAs": "VERSION"
    }
  ],
  "command": "docker build -t myapp:${VERSION} ."
}
Enter fullscreen mode Exit fullscreen mode

The version is read from package.json at runtime. No hardcoding, no shell scripts.


Rollback that shows you what will happen

Before doing anything, VIT shows a preview:

  Commits that will be rolled back:  (strategy: revert)
  ─────────────────────────────────────────────────────
  · feat: new login screen
  · fix: form bug
  · chore: release v1.1.0

  Strategy  : revert — new commit, history preserved
  Target tag: v1.0.0 (3 commits affected)
Enter fullscreen mode Exit fullscreen mode

Two strategies: revert (safe, no force push) and reset (rewrites history, personal branches only).


Monorepo out of the box

"projects": [
  { "id": "backend",  "path": "./Backend",  "tagPrefix": "vback" },
  { "id": "frontend", "path": "./Frontend", "tagPrefix": "vfront" }
]
Enter fullscreen mode Exit fullscreen mode

Each project gets its own tags, its own bump, its own release flow. No plugins needed.


Headless when you need it

vit release --bump patch --yes          # fully automated
vit changelog --semantic --yes          # regenerate from commits, no prompts
vit rollback --tag v1.0.0 --yes         # rollback without confirmation
vit release --bump minor --dry-run      # simulate, write nothing
Enter fullscreen mode Exit fullscreen mode

I built this for myself, but if you're a solo dev who wants guided releases without giving up control — give it a try.

I'd love to hear feedback — especially from anyone doing monorepo releases or complex deploy pipelines. What's the one thing your current release tool can't do that you wish it could?

Top comments (0)