https://github.com/everduin94/better-commits
A CLI for creating better commits following the conventional commits specification
https://github.com/everduin94/better-commits
clack cli commit-message git typescript zod
Last synced: 14 days ago
JSON representation
A CLI for creating better commits following the conventional commits specification
- Host: GitHub
- URL: https://github.com/everduin94/better-commits
- Owner: Everduin94
- License: mit
- Created: 2023-03-09T13:17:22.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2026-05-31T00:02:14.000Z (14 days ago)
- Last Synced: 2026-05-31T00:15:50.456Z (14 days ago)
- Topics: clack, cli, commit-message, git, typescript, zod
- Language: TypeScript
- Homepage:
- Size: 490 KB
- Stars: 2,283
- Watchers: 13
- Forks: 80
- Open Issues: 8
-
Metadata Files:
- Readme: readme.md
- License: LICENSE
Awesome Lists containing this project
README

[](https://github.com/Everduin94/better-commits)
[](https://npmx.dev/package/better-commits)
[](https://discord.gg/grHVnZwYup)
A CLI for writing better commits, following the conventional commits specification.
## โจ Features
- Generate conventional commits through a series of prompts
- Highly configurable with sane defaults
- Infers ticket, commit scope, and commit-type from branch for consistent & fast commits
- Consistent branch creation with flexible workflow hooks via `better-branch`
- Interactive git status/add on commit
- Preview commit messages in color
- Support for git emojis per commit-type
- Configure globally or per repository
- Config validation and error messaging
- [Lightweight](https://bundlejs.com/?q=better-commits&treeshake=%5B*%5D) (17kb)
As a side-effect of formatting messages
- Auto populate PR title / body
- Automate semantic releases
- Automate changelogs
- Automatically link & close related tickets / issues
## ๐ฆ Installation
> Requires Node.js 20 or newer.
```sh
npm install -g better-commits
```
## ๐ Usage
To run the CLI in your terminal:
```sh
better-commits # Create a new commit
better-branch # Create a new branch
```
`better-commits` will prompt a series of questions. These prompts will build a commit message, which you can preview, before confirming the commit. - To better understand these prompts and their intention, read [Conventional Commits Summary](https://www.conventionalcommits.org/en/v1.0.0-beta.4/#summary)
Some of the values in these prompts will be inferred by your branch name and auto populated. You can adjust this in your `.better-commits.jsonc` (or `.better-commits.json`) configuration file.
For documentation on passing commit values to `better-commits` via the CLI, see [CLI Flags](#cli-flags).
> [!TIP]
> The `--no-interactive` flag, allows automated workflows or AI agents like OpenCode and Claude Code, to use better-commits to generate consistent commit messages using less tokens.
>
> Run `better-commits --help` / `better-branch --help` for more information.
## โ๏ธ Configuration
### Global
Your first time running `better-commits`, a default config will be generated in your `$HOME` directory, named `.better-commits.jsonc` (formerly `.better-commits.json`)
- This config will be used if a repository-specific config cannot be found.
### Repository
To create a **repository-specific config**, navigate to the root of your project.
- Run `better-commits-init`
- This will create a default config named `.better-commits.jsonc`
- Properties such as `confirm_with_editor` and `overrides` will prefer the global config
### ๐ซ Properties
> [!NOTE]
> All properties are optional and can be removed from the config. They will be replaced by the default at run-time.
>
> - See `.better-commits.json` in this repository as an example
```jsonc
{
// Run interactive `git status` before composing a commit
"check_status": true,
"check_status_autocomplete": true,
/* COMMIT FIELDS */
"commit_type": {
"enable": true,
// Default selected type from options
"initial_value": "feat",
"max_items": 20,
// Infer type from the current branch name: user/TYPE/my-branch
"infer_type_from_branch": true,
// Include emoji in prompt label
"append_emoji_to_label": false,
// Include emoji from prompt label in commit message
"append_emoji_to_commit": false,
// "Start" | "After-Colon"
"emoji_commit_position": "Start",
"autocomplete": true,
"options": [
{
"value": "feat",
"label": "feat",
"hint": "A new feature",
"emoji": "๐",
"trailer": "Changelog: feature",
},
{
"value": "fix",
"label": "fix",
"hint": "A bug fix",
"emoji": "๐",
"trailer": "Changelog: fix",
},
{
"value": "docs",
"label": "docs",
"hint": "Documentation only changes",
"emoji": "๐",
"trailer": "Changelog: documentation",
},
{
"value": "refactor",
"label": "refactor",
"hint": "A code change that neither fixes a bug nor adds a feature",
"emoji": "๐จ",
"trailer": "Changelog: refactor",
},
{
"value": "perf",
"label": "perf",
"hint": "A code change that improves performance",
"emoji": "๐",
"trailer": "Changelog: performance",
},
{
"value": "test",
"label": "test",
"hint": "Adding missing tests or correcting existing tests",
"emoji": "๐จ",
"trailer": "Changelog: test",
},
{
"value": "build",
"label": "build",
"hint": "Changes that affect the build system or external dependencies",
"emoji": "๐ง",
"trailer": "Changelog: build",
},
{
"value": "ci",
"label": "ci",
"hint": "Changes to our CI configuration files and scripts",
"emoji": "๐ค",
"trailer": "Changelog: ci",
},
{
"value": "chore",
"label": "chore",
"hint": "Other changes that do not modify src or test files",
"emoji": "๐งน",
"trailer": "Changelog: chore",
},
{
"value": "",
"label": "none",
},
],
},
"commit_scope": {
"enable": true,
// If true, users can type a scope not listed in options
"custom_scope": false,
// Default selected scope from options
"initial_value": "app",
// Infer scope from the current branch name: user/type/ticket-SCOPE-my-branch
"infer_scope_from_branch": true,
"max_items": 20,
"autocomplete": true,
"options": [
{ "value": "app", "label": "app" },
{ "value": "shared", "label": "shared" },
{ "value": "server", "label": "server" },
{ "value": "tools", "label": "tools" },
{ "value": "", "label": "none" },
],
},
"check_ticket": {
// Infer ticket / issue from the branch name - user/type/TICKET-my-branch
"infer_ticket": true,
// Prompt for confirmation / edit before using an inferred ticket
"confirm_ticket": true,
// Add the ticket to the commit title - feat(app): TICKET my commit title
"add_to_title": true,
// Deprecated, prefer `prepend_hashtag`
"append_hashtag": false,
// "Never" | "Prompt" | "Always" - 12345 --> #12345
"prepend_hashtag": "Never",
// Wrap the ticket in the commit title: "" | "[]" | "()" | "{}"
"surround": "",
// "start" | "end" | "before-colon" | "beginning"
"title_position": "start",
},
"commit_title": {
// Includes total size of title + type + scope + ticket
"max_size": 70,
},
"commit_body": {
"enable": true,
"required": false,
// Split sentences into multiple lines automatically
"split_by_period": false,
},
"commit_footer": {
"enable": true,
"initial_value": [],
// "closes", "trailer", "breaking-change", "deprecated", "custom"
"options": ["closes", "trailer", "breaking-change", "deprecated", "custom"],
},
"breaking_change": {
// Adds `!` to the commit title when a breaking change is selected
"add_exclamation_to_title": true,
},
// Confirm / edit with $GIT_EDITOR or $EDITOR
"confirm_with_editor": false,
// Show a final confirmation prompt before running git commit
"confirm_commit": true,
// Reuse the last known value from a previous canceled or failed commit
"cache_last_value": true,
// Pretty-print the final commit preview before execution
"print_commit_output": true,
/* BRANCH FIELDS */
// Optional shell commands to run before / after creating branches or worktrees
"branch_pre_commands": [],
"branch_post_commands": [],
"worktree_pre_commands": [],
"worktree_post_commands": [],
"branch_user": {
"enable": true,
"required": false,
// "/" | "-" | "_" - user/feat/my-branch
"separator": "/",
},
"branch_type": {
"enable": true,
"separator": "/",
"autocomplete": true,
},
"branch_scope": {
"enable": true,
"separator": "-",
"autocomplete": true,
},
"branch_ticket": {
"enable": true,
"required": false,
"separator": "-",
},
"branch_version": {
"enable": false,
"required": false,
"separator": "/",
},
"branch_description": {
// Maximum length for the description segment of the branch name
"max_length": 70,
// Allowed values: "" | "/" | "-" | "_"
"separator": "",
},
// "branch" | "worktree"
"branch_action_default": "branch",
// Order of values in the final branch name
"branch_order": ["user", "version", "type", "ticket", "scope", "description"],
// Deprecated, prefer `worktrees.enable`
"enable_worktrees": true,
"worktrees": {
// If false, always create a branch instead of prompting for a worktree
"enable": true,
// Directory where worktrees are created
"base_path": "..",
// Available template variables include:
// {{repo_name}}, {{branch_description}}, {{user}}, {{type}}, {{scope}}, {{ticket}}, {{version}}
"folder_template": "{{repo_name}}-{{ticket}}-{{branch_description}}",
},
/* OTHER FIELDS */
"overrides": {
// Useful on Windows or for shells with different multiline behavior
"shell": "/bin/sh",
},
}
```
### ๐ Inference
`better-commits` will attempt to infer the ticket/issue, commit-type, and commit scope from your branch name. It will auto populate the corresponding field if found.
**Ticket / Issue-Number**
- If a `STRING-NUMBER` or `NUMBER` are at the start of the branch name or after a `/`
**Commit Type**
- If a type is at the start of the branch or is followed by a `/`
**Commit Scope**
- If a configured scope appears as its own branch word, separated by `/`, `-`, or `_`
- If scope appears after a ticket, the expected shape is `TICKET-SCOPE-description`
- Empty scope values and the `custom` scope option are ignored during inference
## ๐ณ Better Branch
Better branch is a secondary feature that works with better commits
- Supports consistent branch naming conventions
- Uses same type-list/prompt from your config
- Enables better-commits to infer type, scope, and ticket
- Caches your username for speedy branching
- Convenient worktree creation
To run the CLI in your terminal:
```sh
better-branch
```
### Worktree Support
`better-branch` will prompt for **Branch** or **Worktree**. The Worktree flow creates a folder/worktree from your **branch description** and a git branch inside with your **full branch name**.
> [!NOTE]
> Creating a worktree named `everduin94/feat/TAC-123-add-worktrees` with the native git command would create a nested folder for each `/`. `better-branch` removes the hassle by creating 1 folder while still using the full name for the branch.
> [!TIP]
> By default, `better-branch` will create **worktrees** as a sibling folder. To change this, see `worktrees.base_path`.
### Pre/Post Branch Checkout Hooks
Optionally configure pre and post checkout commands, for example:
- checkout and rebase main before branching
- run `npm install` before branching
- run `npm run dev` after branching
See _branch_pre_commands_ and _branch_post_commands_ in default config. (or _worktree_pre_commands_ and _worktree_post_commands_ for creating worktrees)
## ๐ก Tips & Tricks
### Building / Versioning
`better-commits` works with [Semantic Release](https://github.com/semantic-release/semantic-release)
- See _package.json_ and _.github/workflows/publish.yml_ for example
### Github
If you use `better-commits` to create your _first_ commit on a new branch
- When you open a PR for that branch, it will properly **auto-populate the title and body**.
- When you squash/merge, all later commits like "addressing comments" or "fixing mistake". Will be prefixed with an asterisk for easy deletion. This way, you **maintain your pretty commit even when squashing**.
If you're using Github issues to track your work, and select the `closes` footer option when writing your commit. Github will **automatically link and close** that issue when your **pr is merged**
### Changelogs
`better-commits` can append a commit trailer per commit type. This allows you to [automate change logs](https://docs.gitlab.com/ee/user/project/changelogs.html) with tools like Gitlab.
### Git
`better-commits` uses native `git` commands under the hood. So any hooks, tools, or staging should work as if it was a normal commit.
Setting `confirm_with_editor=true` will allow you to edit/confirm a commit with your editor.
- For example, to edit with Neovim: `git config --global core.editor "nvim"`
- For VS Code, `git config --global core.editor "code -n --wait"`
You can pass arguments to `git` through `better-commits` like so:
```sh
better-commits --git-dir="$HOME/.config" --work-tree="$HOME"
```
A practical example of this would be managing dotfiles, as described in this [Atlassian Article](https://www.atlassian.com/git/tutorials/dotfiles)
### CLI Flags
Use CLI flags to pass commit values directly instead of answering prompts.
- Use `--no-interactive` to skip prompts, confirmation, and editor flows. This is the recommended mode for OpenCode, Claude Code, and other coding agents.
- Use `--dry-run` to validate the generated `git commit` command without creating a commit.
- Supported commit field flags: `--type`, `--scope`, `--title`, `--body`, `--ticket`, `--closes`, `--deprecates`, `--breaking-title`, `--breaking-body`, `--deprecates-title`, `--deprecates-body`, `--custom-footer`, `--trailer`.
- Supported branch field flags: `--user`, `--type`, `--scope`, `--description`, `--ticket`, `--branch-version`, `--checkout`.
**Examples**
```sh
better-commits --no-interactive --dry-run --type feat --scope cli --title "add parser"
better-branch --no-interactive --type feat --scope cli --ticket TAC-123 --description "add parser" --checkout worktree
```
---
### ๐ช Troubleshooting Windows
#### Git Bash
`TTY initialization failed: uv_tty_init returned EBADF (bad file descriptor)`. This may happen because you're running something like git-bash on Windows. Try another terminal/command-prompt or `winpty` to see if its still an issue.
#### Multi-line
If you are having issues with multilines for commits on windows, you can override the shell via your `.better-commits.jsonc` config.
Example
```json
"overrides": {
"shell": "c:\\Program Files\\Git\\bin\\bash.exe"
}
```
๐ Sponsors
[](https://flotes.app)
[Markdown Notetaking - Built for Learning](https://flotes.app)