{"id":50733591,"url":"https://github.com/chpock/gen-commit-msg","last_synced_at":"2026-06-10T11:30:22.055Z","repository":{"id":358244366,"uuid":"1235214536","full_name":"chpock/gen-commit-msg","owner":"chpock","description":"AI-powered Git commit message generator for staged changes, built for editor-first workflows with OpenCode.","archived":false,"fork":false,"pushed_at":"2026-05-16T13:28:25.000Z","size":363,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-16T13:37:11.687Z","etag":null,"topics":["ai","ai-tools","commit-message","commit-message-generator","git-commit","opencode","terminal-ui","tui-app"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chpock.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-11T05:48:20.000Z","updated_at":"2026-05-16T13:28:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/chpock/gen-commit-msg","commit_stats":null,"previous_names":["chpock/gen-commit-msg"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/chpock/gen-commit-msg","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chpock%2Fgen-commit-msg","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chpock%2Fgen-commit-msg/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chpock%2Fgen-commit-msg/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chpock%2Fgen-commit-msg/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chpock","download_url":"https://codeload.github.com/chpock/gen-commit-msg/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chpock%2Fgen-commit-msg/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34151271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-tools","commit-message","commit-message-generator","git-commit","opencode","terminal-ui","tui-app"],"created_at":"2026-06-10T11:30:21.329Z","updated_at":"2026-06-10T11:30:22.049Z","avatar_url":"https://github.com/chpock.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gen-commit-msg\n\n`gen-commit-msg` is a Go CLI that generates Git commit message candidates from\nstaged changes using OpenCode, then lets you pick the best one in a terminal\nUI (or outputs one message in non-interactive mode).\n\nThe tool is built for editor-first commit workflows: generate inside the commit\nmessage editor, review, then edit before finalizing the commit.\n\n## Why this tool exists\n\nMany AI commit-message tools are either:\n\n- tied to `prepare-commit-msg` hooks (runs every commit, even when you do not\n  want generation), or\n- wrappers that run `git commit` for you.\n\nThis project intentionally does neither.\n\n- No Git hook integration.\n- No commit execution.\n- You keep your existing flow (`git commit`, `lazygit`, editor buffer).\n\n`gen-commit-msg` only generates message text and writes it to stdout (or a file\nvia `--output`).\n\n## Features\n\n- Collects structured staged-change context via Git before prompting AI.\n- Starts and stops `opencode serve` automatically (with health checks).\n- Creates and reuses a dedicated OpenCode agent prompt file.\n- Interactive progress view for pipeline steps in TTY mode.\n- Subject selection list when multiple candidates are generated.\n- Non-interactive mode support (`--subject-max 1`) for scripts/editor callbacks.\n- Config via flags and `GCM_*` environment variables.\n- Optional output file mode (`--output` / `GCM_OUTPUT`).\n\n## Agent installation and customization\n\nOn startup, the tool ensures an OpenCode agent file exists.\n\n- Default agent name: `gen-commit-msg`\n- Default install mode: `if-not-exists`\n- Installed path:\n  `${XDG_CONFIG_HOME:-$HOME/.config}/opencode/agents/gen-commit-msg.md`\n\nThis file contains the agent configuration and generation instructions used for\ncommit message creation.\n\nYou can customize it when needed:\n\n- adjust model settings (for example model name, temperature, steps),\n- tune or replace the prompt/instructions for how commit messages are generated.\n\nImportant behavior:\n\n- `--install-agent if-not-exists` (default) keeps your custom file intact.\n- `--install-agent always` overwrites the file with the built-in default prompt.\n- `--install-agent no` skips installation entirely.\n\n## Requirements\n\n- Go 1.26+\n- `git` available in `PATH`\n- `opencode` available in `PATH`\n- Run inside a Git repository\n- Have staged changes (`git add ...`) before running\n\n## Install\n\nBuild from source:\n\n```bash\nmake build\n```\n\nBinary will be created as `./gen-commit-msg`.\n\n## Quick start\n\nGenerate up to 5 candidates and choose one:\n\n```bash\ngen-commit-msg\n```\n\nGenerate exactly one candidate (no selection list):\n\n```bash\ngen-commit-msg --subject-max 1\n```\n\nWrite result to a file instead of stdout:\n\n```bash\ngen-commit-msg --output /tmp/commit-msg.txt\n```\n\nThen use your normal commit command:\n\n```bash\ngit commit\n```\n\n## Editor workflow (Vim example)\n\nThis utility is designed to be called from a commit message buffer.\n\nThe snippet below runs `gen-commit-msg`, captures output to a temp file, and\ninserts the generated message at the top of the current `gitcommit` buffer.\n\n```vim\nlet s:gen_commit_msg_height = 5\nlet s:gen_commit_msg_subject_max = 5\n\nfunction! \u003cSID\u003eGenCommitMsgCallback(orig_bufnr, term_bufnr, tmpfile, job, status)\n    if a:status == 0\n        if filereadable(a:tmpfile) \u0026\u0026 getfsize(a:tmpfile) \u003e 0\n            let l:output = readfile(a:tmpfile)\n            call appendbufline(a:orig_bufnr, 0, l:output)\n        endif\n    endif\n    call delete(a:tmpfile)\n    if a:status == 0 || a:status == 130\n        execute 'silent! bwipeout! ' . a:term_bufnr\n    else\n        echohl WarningMsg | echo 'Command failed with status: ' . a:status | echohl None\n    endif\nendfunction\n\nfunction! \u003cSID\u003eGenCommitMsg()\n    let l:tmpfile = tempname()\n    let l:cmd = 'gen-commit-msg --subject-max ' . shellescape(s:gen_commit_msg_subject_max) . ' --output ' . shellescape(l:tmpfile)\n    let l:orig_bufnr = bufnr('%')\n    execute 'topleft ' . s:gen_commit_msg_height . 'split | enew'\n    let l:Callback = function('\u003cSID\u003eGenCommitMsgCallback', [l:orig_bufnr, bufnr('%'), l:tmpfile])\n    call term_start([\u0026shell, \u0026shellcmdflag, l:cmd], {'curwin': 1, 'exit_cb': l:Callback})\nendfunction\n\naugroup GitCommitMapping\n    autocmd!\n    autocmd FileType gitcommit nnoremap \u003cbuffer\u003e \u003cLeader\u003eO :call \u003cSID\u003eGenCommitMsg()\u003cCR\u003e\naugroup END\n```\n\nPress `\u003cleader\u003eO` in a commit buffer to generate candidates and insert the\nselected message.\n\n## CLI options\n\nFlag precedence is:\n\n`CLI flag \u003e environment variable \u003e default`\n\n| Flag | Env var | Default | Description |\n|---|---|---|---|\n| `--subject-min`, `-m` | `GCM_SUBJECT_MIN` | `1` | Minimum subject candidate count |\n| `--subject-max`, `-x` | `GCM_SUBJECT_MAX` | `5` | Maximum subject candidate count (max 20) |\n| `--body` | `GCM_BODY` | `true` | Generate commit body |\n| `--quiet`, `-q` | `GCM_QUIET` | `false` | Hide progress view |\n| `--agent`, `-a` | `GCM_AGENT` | `gen-commit-msg` | OpenCode agent name |\n| `--install-agent` | `GCM_INSTALL_AGENT` | `if-not-exists` | `always`, `if-not-exists`, `no` |\n| `--pause` | `GCM_PAUSE` | `on-error` | Pause before exit: `on`, `off`, `on-error` |\n| `--output`, `-o` | `GCM_OUTPUT` | `\"\"` | Write selected message to file |\n| `--log-level`, `-l` | `GCM_LOG_LEVEL` | `none` | `trace`, `debug`, `info`, `warn`, `error`, `none` |\n| `--log-file` | `GCM_LOG_FILE` | `stderr` | Log destination (`-` for stdout) |\n| `--version`, `-V` | — | — | Print version and exit |\n| `--help`, `-h` | — | — | Print help and exit |\n\nAdditional runtime env vars:\n\n- `NO_COLOR`: disables selection color styling.\n- `GCM_TUI_SELECTION_COLORS`: set to `0` to disable selection color styling.\n\n## How it works\n\nPipeline (interactive mode):\n\n1. Collect staged-change context with multiple Git commands.\n2. Ensure OpenCode agent prompt exists.\n3. Start OpenCode server and verify readiness.\n4. Create OpenCode session.\n5. Request structured commit candidates (JSON schema-constrained).\n6. Show candidate subjects; user selects one.\n7. Cleanup session/server and print (or write) selected message.\n\nThe staged context includes summaries (`--name-status`, `--stat`, `--numstat`,\n`--summary`, `--dirstat`) and full staged patch (`git diff --cached ...`).\n\n## Non-interactive behavior\n\n- If stdout is not a TTY and `--subject-max \u003e 1`: exits with an error and\n  suggests `--subject-max 1`.\n- If stdout is not a TTY and `--subject-max == 1`: generates silently and prints\n  one message (or writes to `--output`).\n\n## Exit codes\n\n- `0`: success (including \"no staged files\" early exit)\n- `1`: runtime error\n- `2`: flag/config parsing error\n\n## Development\n\nCommon commands:\n\n```bash\nmake fmt\nmake vet\nmake lint\nmake test\nmake build\nmake all\n```\n\nSingle package tests:\n\n```bash\ngo test -count=1 -race ./internal/config/\n```\n\n## Project structure\n\n```text\ncmd/gen-commit-msg/main.go    # CLI orchestration\ninternal/config/              # flags + env parsing\ninternal/git/                 # repo checks + staged context collection\ninternal/agent/               # OpenCode agent prompt management\ninternal/server/              # opencode serve lifecycle\ninternal/opencode/            # OpenCode session/prompt client\ninternal/tui/                 # progress + selection Bubble Tea UI\ninternal/logging/             # slog setup and custom trace level\n```\n\n## License\n\nMIT (see `LICENSE`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchpock%2Fgen-commit-msg","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchpock%2Fgen-commit-msg","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchpock%2Fgen-commit-msg/lists"}