AI Tools Compared

Automate semantic versioning with Claude Code by configuring version detection rules that identify version bumps from commit messages and code changes. Claude Code analyzes commits and pull request labels to determine whether changes warrant MAJOR, MINOR, or PATCH version increments according to semantic versioning standards, eliminating manual version bumps and ensuring consistent releases.

Semantic versioning (SemVer) has become the standard for version numbering in modern software development. When combined with Claude Code’s powerful automation capabilities, you can create a version management system that eliminates manual version bumps and ensures consistent releases.

Understanding Semantic Versioning Basics

Semantic versioning follows the format MAJOR.MINOR.PATCH:

Claude Code can help automate the detection of which version component should be bumped based on your commit messages, pull request labels, and code changes.

Setting Up Claude Code for Version Management

The first step in automating semantic versioning with Claude Code is to create a CLAUDE.md file that defines your versioning rules and expectations.

# Project Versioning Rules

## Version File Location
- Version is stored in: `package.json`, `pyproject.toml`, or `version.txt`

## Version Bump Rules
- Commits with `BREAKING CHANGE:` in body → MAJOR bump
- Commits with `feat:` prefix → MINOR bump
- Commits with `fix:`, `perf:`, or `refactor:` → PATCH bump

## Changelog Requirements
- Group changes by: Features, Fixes, Breaking Changes, Dependencies
- Include issue references when available
- Use present tense for descriptions

Automated Version Detection Patterns

Claude Code can analyze your codebase to automatically determine the appropriate version bump. Here’s a pattern for version detection:

# version_detector.py
import re
from pathlib import Path

def detect_version_bump(commit_messages: list[str]) -> str:
    """Determine version bump type from commit messages."""

    has_breaking = any(
        "BREAKING CHANGE" in msg or "BREAKING CHANGES" in msg
        for msg in commit_messages
    )

    has_feature = any(
        msg.startswith("feat:") or msg.startswith("feat(")
        for msg in commit_messages
    )

    has_fix = any(
        msg.startswith("fix:") or
        msg.startswith("perf:") or
        msg.startswith("refactor:")
        for msg in commit_messages
    )

    if has_breaking:
        return "major"
    elif has_feature:
        return "minor"
    elif has_fix:
        return "patch"
    return "none"

Creating a Claude Code Command for Version Bumps

You can create custom Claude Code commands that handle the entire version bump process:

# .claude/commands/bump-version.sh
#!/bin/bash

# Get current version
CURRENT_VERSION=$(node -p "require('./package.json').version")

# Analyze commits since last tag
COMMITS=$(git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"%s")

# Determine bump type
BUMP_TYPE=$(echo "$COMMITS" | grep -E "(BREAKING CHANGE|feat:|fix:)" | head -1 | sed 's/.*\(BREAKING CHANGE\).*/major/; s/.*\(feat:\).*/minor/; s/.*\(fix:\).*/patch/')

# Apply version bump
if [ "$BUMP_TYPE" = "major" ]; then
    npm version major -m "Bump version to %s"
elif [ "$BUMP_TYPE" = "minor" ]; then
    npm version minor -m "Bump version to %s"
else
    npm version patch -m "Bump version to %s"
fi

Integrating with Release Workflows

Here’s how to integrate semantic versioning automation with your release workflow:

GitHub Actions Integration

name: Semantic Version Release

on:
  push:
    branches: [main]

jobs:
  version:
    runs-on: ubuntu-latest
    outputs:
      new_version: ${{ steps.version.outputs.new_version }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Determine Version
        id: version
        run: |
          # Use CLAUDE.md rules to determine version
          echo "new_version=$(node scripts/detect-version.js)" >> $GITHUB_OUTPUT

      - name: Create Release
        if: steps.version.outputs.new_version != ''
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: v${{ steps.version.outputs.new_version }}
          release_name: Release v${{ steps.version.outputs.new_version }}

Automated Changelog Generation

// scripts/generate-changelog.js
function generateChangelog(commits, version) {
  const changes = {
    features: [],
    fixes: [],
    breaking: [],
    other: []
  };

  for (const commit of commits) {
    if (commit.message.includes('BREAKING CHANGE')) {
      changes.breaking.push(commit);
    } else if (commit.message.startsWith('feat:')) {
      changes.features.push(commit);
    } else if (commit.message.startsWith('fix:')) {
      changes.fixes.push(commit);
    } else {
      changes.other.push(commit);
    }
  }

  return formatChangelog(changes, version);
}

Best Practices for Version Automation

When implementing semantic versioning automation with Claude Code, follow these best practices:

  1. Define Clear Commit Conventions: Use Conventional Commits format to make automatic version detection reliable.

  2. Maintain a Single Source of Truth: Store version in one place (package.json, pyproject.toml) and reference it consistently.

  3. Validate Before Bumping: Always verify that the proposed version bump matches your actual changes.

  4. Include Rollback Procedures: Have a plan for reverting version bumps if issues are discovered.

  5. Test Automation Thoroughly: Run your version detection logic against historical commits to ensure accuracy.

Using Claude Code to Enforce Versioning Rules

Claude Code can actively enforce versioning rules during development:

# Development Context for Claude Code

## Versioning Enforcement Rules
- All PRs must follow Conventional Commits format
- Breaking changes require MAJOR version bump + migration guide
- New features require MINOR version bump + documentation update
- Bug fixes require PATCH version bump
- Version files must be updated before merging to main

## Before Submitting PR
1. Verify commit messages follow convention
2. Run `npm run version:dry-run` to preview version bump
3. Ensure changelog entries are added
4. Update version in appropriate files

Multi-Language Version File Synchronization

Projects often have version strings in multiple files: package.json for npm, pyproject.toml for Python packages, Chart.yaml for Helm charts, and potentially a VERSION file for shell scripts to consume. A single version bump must update all of them atomically.

Claude Code can generate the synchronization script from your project layout:

# Prompt Claude Code:
# "Read the repository and identify all files that contain version strings.
#  Generate a script that bumps them all simultaneously from the current
#  version to a new version provided as an argument."

# Generated: scripts/sync-versions.sh
#!/bin/bash
set -euo pipefail

NEW_VERSION="${1:?Usage: sync-versions.sh <new-version>}"

# Validate SemVer format
if ! [[ "$NEW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  echo "Error: version must be in X.Y.Z format" >&2
  exit 1
fi

# Update package.json
jq --arg v "$NEW_VERSION" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json

# Update pyproject.toml
sed -i "s/^version = .*/version = \"$NEW_VERSION\"/" pyproject.toml

# Update Chart.yaml
sed -i "s/^appVersion: .*/appVersion: \"$NEW_VERSION\"/" charts/myapp/Chart.yaml

# Update plain VERSION file
echo "$NEW_VERSION" > VERSION

echo "Version bumped to $NEW_VERSION across all files"
git diff --stat

The key advantage is that Claude Code generates this script from your actual project structure rather than a generic template — it scans for version patterns before writing the sync logic.

Pre-Commit Hooks for Conventional Commits Enforcement

Automated version detection only works if commit messages consistently follow the convention. Use commitlint with a Git pre-commit hook to enforce format before commits land in history:

# Install commitlint
npm install --save-dev @commitlint/config-conventional @commitlint/cli husky

# Configure commitlint
echo "module.exports = { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js

# Initialize husky and add commit-msg hook
npx husky init
echo "npx --no -- commitlint --edit \$1" > .husky/commit-msg

Claude Code can help generate custom commitlint rules for your project if you have domain-specific scopes or prefixes that the conventional config doesn’t cover:

// commitlint.config.js - extended with project-specific scopes
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'scope-enum': [2, 'always', [
      'api', 'auth', 'database', 'ui', 'infra', 'docs', 'deps', 'ci'
    ]],
    'body-max-line-length': [1, 'always', 100],
  }
};

Dry-Run Mode Before Publishing

Before any automated release publishes a package, run a dry-run that previews what will change without making network calls:

# npm dry run — prints what would be published
npm publish --dry-run

# For Python packages using hatch or build + twine:
python -m build --sdist --wheel
twine check dist/*
# Confirm package contents before upload

# Preview GitHub release notes without creating the release:
gh release create v${NEW_VERSION} --draft \
  --title "Release v${NEW_VERSION}" \
  --notes "$(node scripts/generate-changelog.js)"

Claude Code handles dry-run generation well — prompt it with “Add a dry-run mode that previews version changes without modifying files or making network calls” and it correctly adds --dry-run flags or conditional logic that skips write operations.

Built by theluckystrike — More at zovo.one