<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ajax</title>
    <description>The latest articles on DEV Community by Ajax (@ajax16).</description>
    <link>https://dev.to/ajax16</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3900465%2Fc6e7fe37-004a-4e92-a881-3778b2aaa645.jpeg</url>
      <title>DEV Community: Ajax</title>
      <link>https://dev.to/ajax16</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ajax16"/>
    <language>en</language>
    <item>
      <title>I built my own release CLI because I wanted full control over my changelogs.</title>
      <dc:creator>Ajax</dc:creator>
      <pubDate>Mon, 27 Apr 2026 13:02:12 +0000</pubDate>
      <link>https://dev.to/ajax16/i-built-my-own-release-cli-because-i-wanted-full-control-over-my-changelogs-2kn1</link>
      <guid>https://dev.to/ajax16/i-built-my-own-release-cli-because-i-wanted-full-control-over-my-changelogs-2kn1</guid>
      <description>&lt;p&gt;I was tired of doing releases by hand. Not the deploy part — the &lt;em&gt;ceremony&lt;/em&gt; around it. Remembering the right git commands, keeping the changelog consistent, not forgetting to tag before pushing.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;VIT&lt;/strong&gt;. A guided CLI that walks you through each step: bump → changelog → commit → tag → push → deploy. Interactive by default, fully headless when you need it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @ajax-16/vit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  It started with the changelog
&lt;/h2&gt;

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

&lt;p&gt;So that's where VIT started — a guided prompt to write changelog entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? 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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Preview, confirm, saved. That's it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Semantic mode when you want it
&lt;/h2&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;? 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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both modes coexist. Toggle per project via config or on the fly with &lt;code&gt;--semantic&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Secrets asked at runtime, never stored
&lt;/h2&gt;

&lt;p&gt;This is the feature I'm most proud of. &lt;code&gt;promptEnv&lt;/code&gt; asks for sensitive values right before the command runs — no &lt;code&gt;.env&lt;/code&gt; files, no hardcoded credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deploy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"promptEnv"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SSH_PASS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SSH password:"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEPLOY_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deploy token:"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sshpass -p ${SSH_PASS} scp -r ./dist user@server:/var/www/${DEPLOY_TOKEN}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even in &lt;code&gt;--yes&lt;/code&gt; headless mode, &lt;code&gt;promptEnv&lt;/code&gt; always stops and waits. Intentionally.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pipeline with &lt;code&gt;captureAs&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Actions can chain commands and pass results forward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node -e &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;process.stdout.write(require('./package.json').version)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"captureAs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VERSION"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker build -t myapp:${VERSION} ."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The version is read from &lt;code&gt;package.json&lt;/code&gt; at runtime. No hardcoding, no shell scripts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rollback that shows you what will happen
&lt;/h2&gt;

&lt;p&gt;Before doing anything, VIT shows a preview:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two strategies: &lt;code&gt;revert&lt;/code&gt; (safe, no force push) and &lt;code&gt;reset&lt;/code&gt; (rewrites history, personal branches only).&lt;/p&gt;




&lt;h2&gt;
  
  
  Monorepo out of the box
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"backend"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./Backend"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"tagPrefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vback"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"frontend"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./Frontend"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tagPrefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vfront"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each project gets its own tags, its own bump, its own release flow. No plugins needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Headless when you need it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vit release &lt;span class="nt"&gt;--bump&lt;/span&gt; patch &lt;span class="nt"&gt;--yes&lt;/span&gt;          &lt;span class="c"&gt;# fully automated&lt;/span&gt;
vit changelog &lt;span class="nt"&gt;--semantic&lt;/span&gt; &lt;span class="nt"&gt;--yes&lt;/span&gt;          &lt;span class="c"&gt;# regenerate from commits, no prompts&lt;/span&gt;
vit rollback &lt;span class="nt"&gt;--tag&lt;/span&gt; v1.0.0 &lt;span class="nt"&gt;--yes&lt;/span&gt;         &lt;span class="c"&gt;# rollback without confirmation&lt;/span&gt;
vit release &lt;span class="nt"&gt;--bump&lt;/span&gt; minor &lt;span class="nt"&gt;--dry-run&lt;/span&gt;      &lt;span class="c"&gt;# simulate, write nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://www.npmjs.com/package/@ajax-16/vit" rel="noopener noreferrer"&gt;npm&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;a href="https://github.com/Ajax-16/VIT" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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?&lt;/p&gt;

</description>
      <category>node</category>
      <category>cli</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
