Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
815a068
chore: update compiled theapp binary
ryanhellyer May 1, 2026
541aeda
feat: add AI client, config loading, and git push
ryanhellyer May 1, 2026
360b6a4
feat: extract git operations into dedicated files
ryanhellyer May 1, 2026
d5f42cb
refactor: improve git run and workflow handling
ryanhellyer May 1, 2026
228e9ed
refactor: simplify git workflow
ryanhellyer May 1, 2026
b323648
fix: minor correction in git run
ryanhellyer May 1, 2026
3284cb5
refactor: enhance AI client and add git diff module
ryanhellyer May 1, 2026
24e4481
feat: implement AI commit message retrieval
ryanhellyer May 1, 2026
891ed9d
chore: add placeholder test file
ryanhellyer May 1, 2026
938ffad
feat: add golang.org/x/term dependency and improve review
ryanhellyer May 1, 2026
8213edc
fix: disable TLS verification for AI API client
ryanhellyer May 1, 2026
277f4c2
feat: add commit message spinner
ryanhellyer May 1, 2026
3fdcb1e
feat: implement push to origin HEAD
ryanhellyer May 1, 2026
62c34c3
feat: add embedded help content
ryanhellyer May 1, 2026
8771c5e
feat: add version flag
ryanhellyer May 1, 2026
6795c0a
refactor: improve commit message review
ryanhellyer May 1, 2026
b6976a6
build: build binary as git-meh
ryanhellyer May 1, 2026
e0f7a96
chore: remove stale theapp binary
ryanhellyer May 2, 2026
c30204d
build: update compile script and add install script
ryanhellyer May 2, 2026
18bd382
feat: bump version to 3.0
ryanhellyer May 2, 2026
f240244
feat: Add OpenRouter API integration and Go rewrite
ryanhellyer May 2, 2026
f64ac7e
chore: cleanup temporary files
ryanhellyer May 2, 2026
be9a688
chore: add git-meh to .gitignore
ryanhellyer May 2, 2026
21e6d33
chore: consolidate .gitignore rules
ryanhellyer May 2, 2026
c8230de
fix: improve API error handling and retry logic
ryanhellyer May 2, 2026
29b218f
feat: update installation instructions
ryanhellyer May 2, 2026
70865b7
build: add cross-compilation targets
ryanhellyer May 2, 2026
bbdce4f
chore: add git-meh exclusions to .gitignore
ryanhellyer May 2, 2026
e6cfb21
chore: stop tracking platform binaries
ryanhellyer May 2, 2026
e4c68d4
chore: remove stale .gitignore entries
ryanhellyer May 2, 2026
b76c970
feat: add install script for global binary
ryanhellyer May 2, 2026
cb78f53
feat: Align README with Go implementation and add basic tests
ryanhellyer May 2, 2026
fe0ef17
Refactor: Clarify architecture and remove unnecessary code sections
ryanhellyer May 2, 2026
179a39d
refactor: consolidate commit and push logic
ryanhellyer May 2, 2026
17356d2
ci: add Go workflow and fatal error helpers
ryanhellyer May 3, 2026
67334b0
refactor: configure HTTP client and add tests
ryanhellyer May 3, 2026
2ff9c4a
feat: add HTTP tests, stdin protocol, and integration tests
ryanhellyer May 3, 2026
20a43ad
test: add review message tests
ryanhellyer May 3, 2026
8b28b1b
fix: handle OpenRouter context length errors
ryanhellyer May 3, 2026
0a2a3ab
Add integration tests and tag for integration build
ryanhellyer May 3, 2026
706efe7
test: expand integration test coverage
ryanhellyer May 3, 2026
18fe664
docs: add testing documentation to README
ryanhellyer May 3, 2026
388d636
Fix: Handle stdin as io.Reader
ryanhellyer May 3, 2026
4086976
feat: default to hosted OpenAI chat API; add server migration doc
ryanhellyer May 3, 2026
1d656c0
chore: add OpenRouter verification script
ryanhellyer May 3, 2026
b4ea49d
refactor: clean up config and build script
ryanhellyer May 3, 2026
795df8a
feat: use hosted OpenAI compatible API by default
ryanhellyer May 3, 2026
d7764e6
Fix: Handle OpenRouter context length errors in git meh
ryanhellyer May 3, 2026
aa940f8
feat: enable OpenRouter API key for default chat backend
ryanhellyer May 3, 2026
16681e9
chore: update default commit prompt to enforce Conventional Commits f…
ryanhellyer May 3, 2026
34e95ae
chore: remove Cursor gitignore entry for plans directory
ryanhellyer May 3, 2026
29d18b2
feat: add fallback models with retry and exponential backoff for AI chat
ryanhellyer May 3, 2026
78457e3
chore: remove shell version, prepare for Go rewrite
ryanhellyer May 4, 2026
f99b9d3
feat: initialize Go project with basic API request
ryanhellyer May 1, 2026
da8abb6
ci: Fixing GitHub actions issues
ryanhellyer May 4, 2026
9d6cc1d
ci: add release automation and update install script to use GitHub re…
ryanhellyer May 4, 2026
6d77262
refactor(config): streamline default commit prompt
ryanhellyer May 4, 2026
8997a87
docs: add IMPROVEMENTS.md tracking document and remove stale PLAN.md
ryanhellyer May 4, 2026
5252f34
ci: add dependabot, linting, security scanning and coverage reporting
ryanhellyer May 4, 2026
0582e89
docs: add environment variable configuration examples to README
ryanhellyer May 4, 2026
9d1f676
feat: add graceful shutdown and context cancellation support
ryanhellyer May 4, 2026
e1837b4
feat: add staged diff size limit with GITMEH_MAX_DIFF_BYTES env var
ryanhellyer May 4, 2026
c813173
feat(git): add per-file staged diff truncation for large changes
ryanhellyer May 4, 2026
1a96546
fix(config): reduce default max diff bytes limit to 10KB
ryanhellyer May 4, 2026
039c65b
chore(scripts): remove verify-openai-chat.sh
ryanhellyer May 4, 2026
280ca91
build: add Makefile with build, test, lint, and cross-compilation tar…
ryanhellyer May 4, 2026
cd1df12
chore: remove IMPROVEMENTS.md tracking file
ryanhellyer May 4, 2026
91b1618
docs(readme): remove development notes and debug logs
ryanhellyer May 4, 2026
da420fd
docs: restructure README with configuration table and developer guide
ryanhellyer May 4, 2026
302a8c2
docs: add default API service disclosure and privacy warning
ryanhellyer May 4, 2026
b052f4f
refactor(config): mark OpenRouter env vars as legacy aliases
ryanhellyer May 4, 2026
27869f3
docs: soften README tone and clarify warnings
ryanhellyer May 4, 2026
abc1c16
feat: add gitmeh symlink for direct command invocation
ryanhellyer May 4, 2026
b235295
refactor(cli): remove long-form --help and --version options
ryanhellyer May 4, 2026
d1832d4
refactor: Remove unused formatBytes function
ryanhellyer May 4, 2026
650c267
chore: remove compile.sh wrapper script
ryanhellyer May 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
69 changes: 69 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Go

on:
workflow_dispatch:
push:
branches: [master, main]
tags: [v*]
pull_request:
branches: [master, main]

permissions:
contents: read

jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Lint
uses: golangci/golangci-lint-action@v7
with:
version: v2.12.1

- name: Vulnerability scan
run: go install golang.org/x/vuln/cmd/govulncheck@latest && govulncheck ./...

- name: Test
run: go test ./... -count=1 -coverprofile=coverage.out

- name: Test (integration)
run: go test -tags=integration ./... -count=1

- name: Upload coverage
if: success()
uses: codecov/codecov-action@v5
with:
file: coverage.out
fail_ci_if_error: false

release:
if: startsWith(github.ref, 'refs/tags/v')
needs: ci
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Build cross-platform binaries
run: make cross

- name: Create release
uses: softprops/action-gh-release@v2
with:
files: |
git-meh-linux-x86_64
git-meh-linux-arm64
git-meh-macos-x86_64
git-meh-macos-arm64
generate_release_notes: true
28 changes: 28 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Built binary (repo root)
git-meh
gitmeh
git-meh.exe
gitmeh.exe
git-meh-*

# Go
*.exe
*.test
*.out
coverage.*
profile.cov

# OS
.DS_Store
Thumbs.db

# Editor / IDE
.idea/
*.swp
*.swo
*~

# Local env / secrets
.env
.env.*
!.env.example
16 changes: 16 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: "2"
linters:
enable:
- staticcheck
- gosec
- govet
- errcheck
- ineffassign
- unused
- misspell
exclusions:
presets: []
run:
timeout: 5m
tests: true

24 changes: 24 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.PHONY: build test lint clean cross all

build:
go build -o git-meh .
ln -sf git-meh gitmeh

test:
go test ./... -count=1

lint:
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.1 && golangci-lint run ./...
govulncheck ./...

clean:
rm -f git-meh gitmeh git-meh.exe gitmeh.exe git-meh-linux-* git-meh-macos-*

cross: clean
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o git-meh-linux-x86_64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o git-meh-linux-arm64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o git-meh-macos-x86_64 .
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o git-meh-macos-arm64 .
go build -o git-meh .

all: lint test cross
117 changes: 81 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,105 @@

**AI-powered git commits for the terminally lazy.**

**gitmeh** is a high-speed shortcut for your personal garbage repositories. It is designed specifically for those projects where quality does not matter and the only thing you care about is closing the laptop as fast as humanly possible.
Stages everything (`git add --all`), AI-guesses a commit message, then shovels it to the cloud. Designed for people who can't be bothered writing their own commit messages.

> **⚠️ WARNING:** Using this on a professional team project is a great way to get a stern talking-to from your engineering manager. This tool is reckless, indifferent, and definitely not "enterprise-ready."

![gitmeh in action](images/screenshot.avif)
> **⚠️** Probably fine for personal projects. Tread carefully on shared repos — know what you're staging before you let AI push it.

### Why use this?

Because writing thoughtful commit messages for your 14th unfinished side project is a waste of your precious nap time.
Because writing commit messages takes effort and you've got better things to do.

* **Automated Staging:** Runs `git add --all` so you don't have to think about what changed.
* **AI Guesswork:** Generates a commit message via an OpenAI-compatible chat API, with retry logic and configurable fallback models.
* **Automatic Pushing:** Commits and pushes in one step.

### Default API service

* **Nuclear Staging:** It runs `git add --all` without asking. It stages your unfinished thoughts, your secrets, and that one large `test.mp4` you forgot was there.
* **AI Guesswork:** By default it flings your staged diff at a free hosted API so you do not have to pretend you will ever sign up for anything. It still explains what you did because you have already forgotten. If you are picky, wave an OpenRouter key around and make it beg a model of your choosing instead.
* **Automatic Pushing:** Shovels your changes directly to the cloud so you can stop looking at the terminal.
* **Built-in Judgement:** Features 40+ randomized status messages that mock your lack of professional standards.
If you don't set `GITMEH_API_KEY`, gitmeh uses a **free hosted API** at `https://ai.hellyer.test/`, run by the author (Ryan Hellyer). The backend automatically selects whichever AI model is working best and cheapest at the time, so models will vary between requests without warning.

### Quick Start
**Your staged diff (code) is sent to this server** and then forwarded to whichever model the backend picks. If you are not comfortable sharing your code with me (Ryan) or with the random third-party model I route it through, **do not use the default service**. Set `GITMEH_API_BASE`, `GITMEH_API_KEY`, `GITMEH_MODEL` etc. to use your own AI provider instead.

1. **Default (no API key):** Do nothing. The script shovels your **staged diff** at `https://ai.hellyer.kiwi/gitmeh` as `text/plain` and whatever text comes back is your commit message. The free tier is **limited to 1000 requests per day per IP address**, so if you and your twelve roommates all commit-spam at once, you will hit the wall together. Pace yourselves.
2. **Optional — OpenRouter:** If you insist on owning the relationship, **get an OpenRouter API key** from [OpenRouter](https://openrouter.ai/keys) and **dump it in your shell config** (`~/.bashrc` or `~/.zshrc`):
```bash
export OPENROUTER_API_KEY='your_key_here'
```
With that set, gitmeh bothers OpenRouter instead of the default URL. Optional: `OPENROUTER_MODEL` (default: `google/gemma-3-4b-it`). See [openrouter.ai/models](https://openrouter.ai/models).
Optional: `GITMEH_PROMPT` to customize the instruction sent to the AI (the diff is always appended; OpenRouter only — the free endpoint does not care about your feelings).
Optional: `GITMEH_DEFAULT_URL` if you want a different keyless endpoint (full URL; default: `https://ai.hellyer.kiwi/gitmeh`).
3. **Install the thing globally** so you can run it from anywhere without that annoying `.sh` extension:
I have zero interest in your code and no intention of looking at it, but it will be processed through my server and the model provider's servers.

## Quick Start

macOS / Linux:
```bash
bash install.sh
# 1. Install
make build && cp git-meh ~/.local/bin/ # from the repo root (requires Go)
# Or: ./install.sh # uses a prebuilt binary

# 2. Set up an API key (OpenCode Zen recommended)
export GITMEH_API_BASE='https://opencode.ai/zen/v1'
export GITMEH_API_KEY='your_zen_key'

# 3. Run
git meh
```

Windows (Git Bash - _totally untested as I don't use Windows_):
Git discovers the binary as a subcommand — works in any repository.

## Configuration

| Env var | Description | Default |
|---|---|---|
| `GITMEH_API_BASE` | API base URL | `https://ai.hellyer.test/v1` (built-in) |
| `GITMEH_API_KEY` | API key | built-in public key |
| `GITMEH_MODEL` | Model name | `gitmeh-hosted` or `google/gemma-3-4b-it` |
| `GITMEH_PROMPT` | System prompt for the model | Conventional Commits prompt |
| `GITMEH_FALLBACK_MODELS` | Comma-separated models to try if the primary fails | — |
| `GITMEH_MAX_DIFF_BYTES` | Per-file diff truncation limit (0 = no limit) | `10000` (10 KB) |

**Auth priority**: `GITMEH_API_KEY` > built-in public key.

**Fallback models**: If the primary model fails (timeout, 5xx, context-length exceeded), gitmeh retries up to 3 times with exponential backoff, then tries each fallback model in order. A 401 or other client error skips retries immediately.

**Diff truncation**: When the staged diff exceeds `GITMEH_MAX_DIFF_BYTES`, gitmeh keeps all file headers and proportionally trims hunk content per file. Truncated sections are marked with `# hunk truncated`.

## Developer Guide

### Prerequisites

- Go (see `go.mod` for version)
- `golangci-lint` and `govulncheck` for linting (install via `go install`)

### Commands

```bash
mkdir -p ~/bin
cp gitmeh.sh ~/bin/gitmeh
# Ensure ~/bin is in your PATH
make build # build native binary
make test # run unit tests
make lint # run golangci-lint + govulncheck
make cross # cross-compile for Linux/macOS, amd64/arm64
make clean # remove built binaries
make all # lint + test + cross-compile

go test -tags=integration ./... -count=1 # integration tests (require git)
```

### Project structure

```
main.go — entry point, CLI orchestration, user review prompt
internal/
aiapi/ — AI API communication (chat, HTTP client, spinner)
config/ — env var parsing
git/ — git command wrappers (add, diff, commit, push)
version/ — version string
```

### Requirements
### Architecture notes

* `git`: duh!
* `curl`: to send the SOS signal (default API or OpenRouter).
* `jq`: to handle the robot's feelings — **only if** you are on the OpenRouter path. The keyless mode does not need it, because apparently plain text is easier than JSON.
- The API call wraps a spinner goroutine for terminal feedback. Ctrl+C cancels the HTTP context, which immediately aborts the request and cleans up the terminal.
- Model retries use exponential backoff (1s, 2s, 4s). Context-length errors and non-retryable status codes skip retries and advance to the next fallback model.
- Diff truncation splits the unified diff at `diff --git` boundaries, preserves all file headers, and allocates the remaining byte budget proportionally by hunk size.

### Changelog
## Changelog

* `2.1.0`: Default to the free hosted plain-text API so you can avoid another signup; OpenRouter when you set `OPENROUTER_API_KEY`; whine about the 1000 requests/day/IP limit on the free tier
* `2.0.2`: Fixing default model documentation
* `2.0.1`: Set default model to Google Gemma 3 4B as it is free
* `2.0`: Conversion to use OpenRouter API and implementing ability to change model used and prompt
* `1.0`: Initial implementation using Google Gemini
- **3.x:** Retry and fallback models, graceful Ctrl+C, diff truncation, CI linting/security scanning, Dependabot, Makefile, support for OpenAI compatible APIs
- **3.0:** Rewrite in Go; run via `git meh`
- **2.x:** OpenRouter and plain-text API versions
- **1.0:** Initial Google Gemini implementation

### Author
## Author

**Ryan Hellyer** [ryan.hellyer.kiwi](https://ryan.hellyer.kiwi) | [GitHub Repo](https://github.com/ryanhellyer/gitmeh)

16 changes: 16 additions & 0 deletions fatal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import (
"fmt"
"os"
)

func fatalErr(err error) {
fmt.Println(err)
os.Exit(1)
}

func fatalMsg(msg string) {
fmt.Println(msg)
os.Exit(1)
}
Loading
Loading