sley
Version orchestrator for semantic versioning
A command-line tool for managing SemVer 2.0.0 versions using a simple .version file. Works with any language or stack, integrates with CI/CD pipelines, and extends via built-in plugins for git tagging, changelog generation, and version validation.
sley - named for the weaving tool that arranges threads in precise order.
Quick Start
# 1. Install (choose your method from Installation section below)
brew install indaco/tap/sley
# 2. Initialize your project
sley init --yes
# => Created .version with version 0.1.0
# => Created .sley.yaml with default plugins
# 3. Verify your setup
sley show
# => 0.1.0
# 4. Make your first version bump
sley bump patch
# => 0.1.1
# 5. Check the result
cat .version
# => 0.1.1
You're ready! Continue to Usage for common workflows, or Installation to get started.
Table of Contents
Features
- Lightweight
.version file - SemVer 2.0.0 compliant
init, bump, set, show, validate - intuitive version control
- Pre-release support with auto-increment (
alpha, beta.1, rc.2, --inc)
- Built-in plugins - git tagging, changelog generation, version policy enforcement, commit parsing
- Extension system - hook external scripts into the version lifecycle
- Monorepo/multi-module support - manage multiple
.version files at once
- Works standalone or in CI -
--strict for strict mode
- Configurable via flags, env vars, or
.sley.yaml
Prerequisites
- Required: Git (for auto-initialization and tag-manager plugin)
- Optional: Go 1.25+ (only required if installing via
go install)
- Recommended: Familiarity with semantic versioning
Why .version?
sley was born from patterns that kept repeating across my projects:
It started with Go: Using //go:embed .version for version info - no build flags, no magic. This became the default approach for every Go project.
Then frontend projects: The same pattern worked for SvelteKit, and other frontend stacks with a Vite plugin to read from .version. One file, same workflow, any stack.
Then multi-stack projects: With a SvelteKit frontend, Go gateway, and Python/Rust services in one repo, the need became clear: version each component individually, but also bump them all at once when needed.
sley solves all of these. The plugin system (audit-log, version-validator, release-gate) came later to support organizational requirements like audit trails and policy enforcement.
What it is
- A single source of truth for your project version
- Language-agnostic - works with Go, Python, Node, Rust, or any stack
- CI/CD friendly - inject into Docker labels, GitHub Actions, release scripts
- Human-readable - just a plain text file containing
1.2.3
- Predictable - no magic, no hidden state, version is what you set
What it is NOT
- Not a replacement for git tags - use the
tag-manager plugin to sync both
- Not a package manager - it doesn't publish or distribute anything
- Not a standalone changelog tool - changelog generation is available via the built-in
changelog-generator plugin
- Not a build system - it just manages the version string
The .version file complements your existing tools. Pair it with git tag for releases, inject it into binaries at build time, or sync it across package.json, Cargo.toml, and other files using the dependency-check plugin.
Installation
Choose Your Installation Method
| If you... |
Use... |
Jump to... |
| Use macOS/Linux with Homebrew |
Homebrew |
Option 1 |
| Want latest version system-wide |
go install (global) |
Option 2 |
| Need local project-specific install |
go install (tool) |
Option 3 |
| Don't have Go installed |
Prebuilt binary |
Option 4 |
| Want to contribute or customize |
Build from source |
Option 5 |
Option 1: Homebrew (macOS/Linux)
brew install indaco/tap/sley
Option 2: Install via go install (global)
go install github.com/indaco/sley/cmd/sley@latest
With Go 1.24 or greater installed, you can install sley locally in your project by running:
go get -tool github.com/indaco/sley/cmd/sley@latest
Once installed, use it with
go tool sley
Option 4: Prebuilt binaries
Download the pre-compiled binaries from the releases page and place the binary in your system's PATH.
Option 5: Clone and build manually
git clone https://github.com/indaco/sley.git
cd sley
just install
CLI Commands & Options
NAME:
sley - Version orchestrator for semantic versioning
USAGE:
sley [global options] [command [command options]]
VERSION:
v0.7.0
COMMANDS:
init Initialize .version file and .sley.yaml configuration
show Display current version
set Set the version manually
bump Bump semantic version (patch, minor, major)
pre Set pre-release label (e.g., alpha, beta.1)
doctor, validate Validate .version file(s) and configuration
changelog Manage changelog files
tag Manage git tags for versions
extension Manage extensions for sley
modules, mods Manage and discover modules in workspace
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--path string, -p string Path to .version file (default: ".version")
--strict, --no-auto-init Fail if .version file is missing (disable auto-initialization)
--no-color Disable colored output
--help, -h show help
--version, -v print the version
Configuration
The CLI determines the .version path in the following order:
--path flag
SLEY_PATH environment variable
.sley.yaml file
- Fallback:
.version in the current directory
Example: Use Environment Variable
export SLEY_PATH=./my-folder/.version
sley patch
Example: Use .sley.yaml
# .sley.yaml
path: ./my-folder/.version
If both are missing, the CLI uses .version in the current directory.
Auto-initialization
If the .version file does not exist when running the CLI:
- It tries to read the latest Git tag via
git describe --tags.
- If the tag is a valid semantic version, it is used.
- Otherwise, the file is initialized to 0.1.0.
This ensures your project always has a starting point.
Using sley init
The recommended way to initialize a new project is with sley init:
# Interactive mode - select plugins and generate .sley.yaml
sley init
# Non-interactive with sensible defaults
sley init --yes
# Use a pre-configured template
sley init --template automation
# Enable specific plugins
sley init --enable commit-parser,tag-manager,changelog-generator
# Initialize as monorepo with workspace configuration
sley init --workspace --yes
# Migrate version from existing package.json, Cargo.toml, etc.
sley init --migrate --yes
# Custom path
sley init --path internal/version/.version
Available flags:
| Flag |
Description |
--yes, -y |
Use defaults without prompts (commit-parser, tag-manager) |
--template |
Use a pre-configured template (see below) |
--enable |
Comma-separated list of plugins to enable |
--workspace |
Initialize as monorepo with workspace configuration |
--migrate |
Detect version from existing files (package.json, etc.) |
--force |
Overwrite existing .sley.yaml |
--path, -p |
Custom path for .version file |
Available templates:
| Template |
Plugins Enabled |
basic |
commit-parser |
git |
commit-parser, tag-manager |
automation |
commit-parser, tag-manager, changelog-generator |
strict |
commit-parser, tag-manager, version-validator, release-gate |
full |
All plugins enabled |
To disable auto-initialization, use the --strict flag.
This is useful in CI/CD environments or stricter workflows where you want the command to fail if the file is missing:
sley bump patch --strict
# => Error: .version file not found
Usage
Display current version
# .version = 1.2.3
sley show
# => 1.2.3
# Fail if .version is missing (strict mode)
sley show --strict
# => Error: version file not found at .version
Set version manually
sley set 2.1.0
# => .version is now 2.1.0
You can also set a pre-release version:
sley set 2.1.0 --pre beta.1
# => .version is now 2.1.0-beta.1
You can also attach build metadata:
sley set 1.0.0 --meta ci.001
# => .version is now 1.0.0+ci.001
Or combine both:
sley set 1.0.0 --pre alpha --meta build.42
# => .version is now 1.0.0-alpha+build.42
Bump version
sley show
# => 1.2.3
sley bump patch
# => 1.2.4
sley bump minor
# => 1.3.0
sley bump major
# => 2.0.0
# .version = 1.3.0-alpha.1+build.123
sley bump release
# => 1.3.0
Increment pre-release (bump pre)
Increment only the pre-release portion without bumping the version number:
# .version = 1.0.0-rc.1
sley bump pre
# => 1.0.0-rc.2
# .version = 1.0.0-rc1
sley bump pre
# => 1.0.0-rc2
# Switch to a different pre-release label
# .version = 1.0.0-alpha.3
sley bump pre --label beta
# => 1.0.0-beta.1
You can also pass --pre and/or --meta flags to any bump:
sley bump patch --pre beta.1
# => 1.2.4-beta.1
sley bump minor --meta ci.123
# => 1.3.0+ci.123
sley bump major --pre rc.1 --meta build.7
# => 2.0.0-rc.1+build.7
# Skip pre-release hooks and extensions during bump
sley bump patch --skip-hooks
# => 1.2.4 (no hooks executed)
[!NOTE]
By default, any existing build metadata (the part after +) is cleared when bumping the version.
To preserve existing metadata, pass the --preserve-meta flag:
# .version = 1.2.3+build.789
sley bump patch --preserve-meta
# => 1.2.4+build.789
# .version = 1.2.3+build.789
sley bump patch --meta new.build
# => 1.2.4+new.build (overrides existing metadata)
Smart bump logic (bump auto)
Automatically determine the next version:
# .version = 1.2.3-alpha.1
sley bump auto
# => 1.2.3
# .version = 1.2.3
sley bump auto
# => 1.2.4
Override bump with --label:
sley bump auto --label minor
# => 1.3.0
sley bump auto --label major --meta ci.9
# => 2.0.0+ci.9
sley bump auto --label patch --preserve-meta
# => bumps patch and keeps build metadata
Valid --label values: patch, minor, major.
Manage pre-release versions
# .version = 0.2.1
sley pre --label alpha
# => 0.2.2-alpha
If a pre-release is already present, it's replaced:
# .version = 0.2.2-beta.3
sley pre --label alpha
# => 0.2.2-alpha
Auto-increment pre-release label
# .version = 1.2.3
sley pre --label alpha --inc
# => 1.2.3-alpha.1
# .version = 1.2.3-alpha.1
sley pre --label alpha --inc
# => 1.2.3-alpha.2
Validate .version file
Check whether the .version file exists and contains a valid semantic version:
# .version = 1.2.3
sley validate
# => Valid version file at ./<path>/.version
If the file is missing or contains an invalid value, an error is returned:
# .version = invalid-content
sley validate
# => Error: invalid version format: ...
Manage changelogs
While sley isn't a standalone changelog tool, the changelog-generator plugin provides full changelog support. This utility command helps you work with versioned changelog files created by the plugin.
[!NOTE]
Requires the changelog-generator plugin. See docs/plugins/CHANGELOG_GENERATOR.md for configuration options including automatic merging with merge-after.
Merge versioned changelog files into a unified CHANGELOG.md:
sley changelog merge
# Specify custom paths
sley changelog merge --changes-dir .changes --output CHANGELOG.md
# Use a custom header template
sley changelog merge --header-template .changes/header.md
Manage git tags
Manual tag management for workflows that need tagging decoupled from version bumping.
[!NOTE]
Requires the tag-manager plugin. See docs/plugins/TAG_MANAGER.md for configuration and workflow examples.
sley tag create # Create tag for current version
sley tag create --push # Create and push
sley tag list # List version tags
sley tag push [tag-name] # Push tag to remote
sley tag delete <tag-name> # Delete a tag
Rolling back a version change
If you need to undo a version bump:
# Manual method - set back to previous version
sley set 1.2.3
# Git method (if changes were committed)
git revert HEAD
# Or reset if not pushed yet
git reset --hard HEAD^
# If using tag-manager plugin, also delete the tag
git tag -d v1.2.4
# If tag was pushed to remote
git push origin :refs/tags/v1.2.4
[!NOTE]
Automated rollback is not built into sley. Always track version changes in git for easy reversion.
Initialize .version file
sley init
# => Interactive mode: select plugins, create .sley.yaml
Use --yes for non-interactive initialization with defaults:
sley init --yes
# => Created .version with version 0.1.0
# => Created .sley.yaml with default plugins (commit-parser, tag-manager)
Enable specific plugins:
sley init --enable commit-parser,changelog-generator,audit-log
# => Created .sley.yaml with 3 plugins enabled
Force overwrite existing configuration:
sley init --yes --force
# => Overwrites existing .sley.yaml
Migrate version from existing project files:
sley init --migrate --yes
# => Detected 2.0.0 from package.json, uses it for .version
Interactive Mode
When running sley init without flags in an interactive terminal, you'll see:
Initializing sley...
Detected:
- Git repository
- package.json (Node.js project)
Select plugins to enable:
[x] Commit Parser - Analyze conventional commits to determine bump type
[x] Tag Manager - Auto-create git tags after version bumps
[ ] Version Validator - Enforce versioning policies
[ ] Dependency Check - Sync version to package.json and other files
[ ] Changelog Parser - Infer bump type from CHANGELOG.md
[ ] Changelog Generator - Generate changelogs from commits
[ ] Release Gate - Pre-bump validation (clean worktree, CI status)
[ ] Audit Log - Record version history with metadata
Created .version with version 0.1.0
Created .sley.yaml with 2 plugins enabled
Next steps:
- Review .sley.yaml and adjust settings
- Run 'sley bump patch' to increment version
- Run 'sley doctor' to verify setup
The init command automatically detects your project type (Git, Node.js, Go, Rust, Python) and suggests relevant plugins.
CI/CD Integration
sley works seamlessly in CI/CD environments with the --strict flag for strict mode and auto-detection of CI environments.
GitHub Actions
name: Version Bump
on:
workflow_dispatch:
inputs:
bump_type:
description: "Bump type"
required: true
type: choice
options:
- patch
- minor
- major
jobs:
bump:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install sley
run: |
curl -L https://github.com/indaco/sley/releases/latest/download/sley-linux-amd64 -o sley
chmod +x sley
sudo mv sley /usr/local/bin/
- name: Bump version
run: sley bump ${{ inputs.bump_type }} --strict
- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .version
git commit -m "chore: bump version to $(sley show)"
git push
- name: Create and push tag
run: sley tag create --push
GitLab CI
version:bump:
stage: deploy
image: golang:1.25
rules:
- if: $CI_COMMIT_BRANCH == "main"
script:
- go install github.com/indaco/sley/cmd/sley@latest
- sley bump patch --strict
- git config user.name "GitLab CI"
- git config user.email "ci@gitlab.com"
- git add .version
- git commit -m "chore: bump version to $(sley show)"
- git push https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git HEAD:${CI_COMMIT_BRANCH}
- sley tag create --push
Docker Example
Inject version into Docker image builds:
FROM alpine:latest
ARG VERSION
LABEL version="${VERSION}"
COPY . /app
# Build with version from sley
docker build --build-arg VERSION=$(sley show) -t myapp:$(sley show) .
Plugin System
sley includes built-in plugins that provide deep integration with version bump logic. Unlike extensions (external scripts), plugins are compiled into the binary for native performance.
[!NOTE]
You don't need a .sley.yaml file to use sley! The tool works out-of-the-box with commit-parser enabled. Create a config only when you need to customize behavior.
Available Plugins
| Plugin |
Description |
Default |
commit-parser |
Analyzes conventional commits to determine bump type |
Enabled |
tag-manager |
Automatically creates git tags synchronized with bumps |
Disabled |
version-validator |
Enforces versioning policies and constraints |
Disabled |
dependency-check |
Validates and syncs versions across multiple files |
Disabled |
changelog-parser |
Infers bump type from CHANGELOG.md entries |
Disabled |
changelog-generator |
Generates changelog from conventional commits |
Disabled |
release-gate |
Pre-bump validation (clean worktree, branch, WIP) |
Disabled |
audit-log |
Records version changes with metadata to a log file |
Disabled |
Quick Example
# .sley.yaml
plugins:
commit-parser: true
tag-manager:
enabled: true
prefix: "v"
annotate: true
push: false
version-validator:
enabled: true
rules:
- type: "major-version-max"
value: 10
- type: "branch-constraint"
branch: "release/*"
allowed: ["patch"]
dependency-check:
enabled: true
auto-sync: true
files:
- path: "package.json"
field: "version"
format: "json"
changelog-generator:
enabled: true
mode: "versioned"
format: "grouped" # or "keepachangelog" for Keep a Changelog spec
repository:
auto-detect: true
For detailed documentation on all plugins and their configuration, see docs/PLUGINS.md.
Extension System
sley supports extensions - external scripts that hook into the version lifecycle for automation tasks like updating changelogs, creating git tags, or enforcing version policies.
# Install an extension
sley extension install --path ./path/to/extension
# List installed extensions
sley extension list
# Remove an extension
sley extension remove my-extension
Ready-to-use extensions are available in contrib/extensions/.
For detailed documentation on hooks, JSON interface, and creating extensions, see docs/EXTENSIONS.md.
Monorepo / Multi-Module Support
sley supports managing multiple .version files across a monorepo. When multiple modules are detected, the CLI automatically enables multi-module mode.
[!TIP]
Working with a monorepo? If your project has multiple services/packages with separate versions, this section shows you how to manage them all at once.
# List discovered modules
sley modules list
# Show all module versions
sley show --all
# Bump all modules
sley bump patch --all
# Bump specific module
sley bump patch --module api
# Bump multiple modules
sley bump patch --modules api,web
# Bump modules matching pattern
sley bump patch --pattern "services/*"
For CI/CD, use --non-interactive or set CI=true to disable prompts.
For detailed documentation on module discovery, configuration, and patterns, see docs/MONOREPO.md.
Troubleshooting
"Error: .version file not found"
The .version file does not exist in the expected location.
Solutions:
- Run
sley init to create the file
- Use the
--path flag to specify a custom location: sley show --path ./custom/.version
- Set the
SLEY_PATH environment variable: export SLEY_PATH=./custom/.version
- Check your
.sley.yaml for the configured path
The .version file contains content that is not a valid SemVer version.
Solutions:
- Run
sley doctor to validate and see detailed error information
- Ensure the file contains only a valid SemVer version (e.g.,
1.2.3, 2.0.0-beta.1)
- Remove any extra whitespace, newlines, or comments
- Use
sley set 1.0.0 to reset to a valid version
Plugin-specific errors
If you encounter errors related to plugins (e.g., tag-manager, changelog-generator):
Solutions:
- Run
sley doctor to validate your configuration
- Check the individual plugin documentation in docs/PLUGINS.md
- Verify your
.sley.yaml configuration syntax
- Try disabling the plugin temporarily to isolate the issue
"Error: git repository not found"
The tag-manager plugin or auto-initialization requires a git repository.
Solutions:
- Initialize a git repository:
git init
- Ensure you're running sley from within a git repository
- Disable the tag-manager plugin if you don't need git integration
Module not detected in monorepo
A .version file exists but is not being discovered in multi-module mode.
Solutions:
- Check
.sleyignore for exclude patterns that might match your module
- Verify the file is named exactly
.version (not version or .version.txt)
- Run
sley modules list to see discovered modules
- Check workspace configuration in
.sley.yaml
Permission denied errors
Unable to read or write the .version file.
Solutions:
- Ensure the file has appropriate permissions:
chmod 644 .version
- Check directory permissions
- Run with appropriate user permissions
Getting more help
- Run
sley doctor for detailed diagnostics
- Check individual plugin documentation for plugin-specific issues
- Review your
.sley.yaml configuration
- See the Contributing Guide for reporting issues
Contributing
Contributions are welcome!
See the Contributing Guide for setting up the development tools.
AI Assistance
Built by humans, with some help from AI. After the v0.5.0 release, Claude Code assisted with test generation, documentation scaffolding, code review, and tedious refactoring (like renaming from "semver" to "sley"). The project logo was also AI-generated under the maintainer's direction. All output was reviewed, reworked and approved by the maintainer.
License
This project is licensed under the MIT License - see the LICENSE file for details.