gem-commit
v0.3.0
Published
Conventional Commits message recommender CLI powered by the Gemini CLI
Maintainers
Readme
gcommit
A Conventional Commits message recommender CLI powered by the Gemini CLI. It feeds your staged diff to Gemini (gemini-2.5-flash), shows a few candidate messages, and commits with the one you pick.
Install:
npm i -g gem-commit(the npm package isgem-commit; the installed command isgcommit).
The message language (English/Korean) and the number of candidates can be changed via
gcommit config. Multi-provider support, model switching, and a "regenerate" flow are intentionally out of scope.
Requirements
- Node.js >= 18
- Gemini CLI must be installed on your
PATHand authenticated.- Verify with:
gemini --version - Install/auth guide: Gemini CLI official docs
- Verify with:
- macOS / Linux / Windows (Windows 10+, Windows Terminal or recent conhost recommended)
gcommit only spawns the Gemini CLI as a child process — it never handles API keys directly.
Usage
git add <files>
gcommit- The staged diff is read and sent to Gemini (with a progress indicator that shows elapsed time).
- Conventional Commits candidates (3 English messages by default; configurable via
gcommit config) are shown as an interactive list. - Selection controls:
↑/↓(ork/j) — moveEnter— confirm the highlighted candidate1–9— pick that candidate by number and confirm immediately (up to the candidate count)q/ESC/Ctrl+C— cancel
git commitruns immediately with the chosen message.
In non-TTY environments (pipes, CI, etc.) interactive selection is unavailable; candidates are printed and the run is cancelled.
If Gemini exits with an error or returns output that can't be parsed, its
stderris printed to help diagnose the failure. Cancelling withCtrl+Calso stops the underlyinggeminiprocess so it doesn't keep running in the background.
Reword an existing commit
Regenerate the message for a commit that already exists, then replace it:
gcommit reword <commit> # e.g. HEAD, HEAD~2, a1b2c3dThe commit's diff is sent to Gemini the same way, you pick from the candidates, and the message is rewritten:
HEAD→ rewritten withgit commit --amend.- An older commit → rewritten via a non-interactive rebase that touches only that commit's message.
Only commits on a linear, merge-free path from the target up to HEAD are supported. Before rewriting, gcommit checks that:
- the target is an ancestor of
HEAD(not on a different branch), - neither the target nor anything between it and
HEADis a merge commit, - the working tree is clean.
If any check fails it stops with a clear message. Rewriting a commit that was already pushed is allowed but warned about — you'll need a force-push afterward.
Exit codes
| Code | Meaning |
| --- | --- |
| 0 | Success / user cancelled |
| 1 | Generic error (not a git repo, parse failure, git commit failure, …) |
| 2 | Gemini CLI missing or auth error |
| 3 | No staged changes |
Flags
gcommit --help,-h— show helpgcommit --version,-v— show versiongcommit reword --help— help for the reword subcommand
CLI flags don't change behavior. Persistent settings (language, candidate count) live under gcommit config.
Configuration (gcommit config)
gcommit config # interactive wizard (language → candidate count)
gcommit config get # print all current values
gcommit config get <key> # print a single key's value
gcommit config set <key> <val> # update and persist
gcommit config path # print the current config file pathSupported keys:
| Key | Allowed values | Default |
| --- | --- | --- |
| lang | en / ko | en |
| candidates | integer 1–9 | 3 |
Storage location
The file location is picked automatically based on how gcommit itself is installed.
| Install form | Storage location |
| --- | --- |
| Global install (npm i -g gem-commit) or npm link — macOS/Linux | $XDG_CONFIG_HOME/gcommit/config.json (defaults to ~/.config/gcommit/config.json) |
| Global install — Windows | %APPDATA%\gcommit\config.json (defaults to C:\Users\<user>\AppData\Roaming\gcommit\config.json) |
| Project devDependency (npm i -D gem-commit) | <project-root>/.gcommitrc.json |
Whether to commit a project-level
.gcommitrc.jsonis up to each project's policy (committing it is usually fine).
Fixed parameters
| Item | Value |
| --- | --- |
| Model | gemini-2.5-flash |
| Max diff size | 50,000 bytes (anything beyond is truncated with a warning) |
| Commit format | Conventional Commits |
| type keywords | feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert |
The model, diff limit, and allowed type list are hard-coded in src/constants.js.
Flow
git add → gcommit
│
├─ verify we're in a git repo
├─ verify `gemini --version` works
├─ git diff --cached (truncate if > 50KB)
├─ load config (lang / candidates saved via `gcommit config`)
├─ gemini -p <prompt> --output-format json -m gemini-2.5-flash (stdin: diff)
├─ parse → N candidates (retry once on failure)
├─ interactive selection (↑/↓ · Enter · 1-N · q/ESC)
└─ git commit -F <tempfile> → delete tempfile → print resultDevelopment
npm install
node bin/cli.js # run locallyProject layout
gcommit/
├─ package.json
├─ README.md
├─ bin/
│ └─ cli.js # #!/usr/bin/env node — dispatches run() / config / reword
└─ src/
├─ index.js # staged-commit flow (config → diff → choose → commit)
├─ reword.js # `gcommit reword <sha>` flow (validate → choose → amend/rebase)
├─ candidates.js # shared generate + interactive choose (used by both flows)
├─ constants.js # fixed values + default config / validation ranges
├─ config.js # install-scope detection, path resolution, load/save/validate
├─ configCli.js # `gcommit config` subcommand (wizard + get/set/path)
├─ git.js # isRepo, diff/commit, plus reword helpers (amend, rebase, checks)
├─ gemini.js # ensureInstalled, runGemini, parseResponse, generateCandidates
├─ prompt.js # buildPrompt(config) — English/Korean + N candidates
└─ ui.js # output + readline-based pickers (chooseCandidate, chooseOne)Out of scope
Multi-provider support (Claude/Codex), model switching, git-hook integration, and a "regenerate" flow are all out of scope. (Message language and candidate count are still configurable via gcommit config.)
License
MIT
