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
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
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
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}"
}
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} ."
}
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)
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" }
]
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
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)