gitmate

command module
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2026 License: MIT Imports: 1 Imported by: 0

README

gitmate

Your Git workflow, with a brain. And a conscience.

release ci license

Most "AI for Git" tools are commit-message generators in a trenchcoat. gitmate isn't.

Under the hood it's a small swarm of agents running a ReAct loop (think → act → observe → refine), with an evaluator that scores every output, a memory layer that picks up on your style, and a permission gate that refuses to touch your filesystem without a green light. On top sits a Bubble Tea TUI so you can actually watch the thing work instead of staring at a blinking cursor.

You stay in charge. The agent does the typing.

What it actually does

Five things, well:

  1. Ships code. gitmate ship reads your diff, drafts a commit message, scores it, refines if weak, asks before committing, optionally opens a PR.
  2. Resolves merge hell. gitmate resolve <file> walks each conflict block, explains what each side was trying to do, proposes a patch with a confidence score, and waits for you.
  3. Predicts pain. gitmate check scans hotspots and overlap zones with main so you know what's about to explode before it does.
  4. Reverses anything it did. Every mutation (commit, rebase, merge, push, stash, file write, PR) writes a checkpoint. gitmate undo rolls it back. Pushed commits use --force-with-lease against the recorded prior remote SHA.
  5. Syncs on a schedule. gitmate schedule set --enable --time 08:30 wires launchd (macOS) or a systemd user timer (linux) to run gitmate sync --auto --all every morning before you sit down. Auto-installs on enable; auto-uninstalls on disable.

Plus a TUI dashboard if you'd rather click than type.

A taste

gitmate (bare, opens dashboard)
gitmate 0.2.3
branch feature-payments (↑3 ↓1)   base main   risk MEDIUM   overlap 2

  [s]  ship        commit + optional PR
▸ [y]  sync        fetch + integrate origin + base
  [c]  check       predict merge pain
  [t]  status      branch + overlap + risk
  [x]  explain     explain a diff
  [p]  push        push branch to origin
  [m]  metrics     approval rate + latency
  [i]  init        configure provider + key
  [f]  config      show effective config

↑↓/jk navigate · enter select · letter shortcut · q quit
gitmate ship
⠼ drafting commit message
✓ staged diff ready (2433 chars)
✓ draft scored 0.85
· refine kept original (0.72 vs 0.85)

╭ gitmate · action required ─────────────────────╮
│  action  git_commit                            │
│  risk    EXECUTE                               │
│  why     commit staged changes with this msg   │
╰────────────────────────────────────────────────╯
─── input ───
  feat(tui): add Bubble Tea dashboard + live stream

  - dashboard renders repo state + risk + action menu
  - lipgloss-styled approval card replaces ASCII box
  - stream wrapper for ship/sync long-running ops
─────────────

[y] yes  [a] allow session  [p] preview  [e] edit  [n] no  [?] explain  ›
› y
✓ commit landed
[main 97f57d7] feat(tui): add Bubble Tea dashboard + live stream
gitmate resolve src/payment.go
found 1 conflict block in src/payment.go

=== Block 1/1 (line 42, complexity=complex) ===
--- ours ---
return retryWithBackoff(submit(payload))
--- theirs ---
return client.SubmitPayment(ctx, payload)

--- analysis ---
ours intent:    Adds retry logic to payment submission
theirs intent:  Refactors to new payments client
conflict type:  refactor_vs_feature
strategy:       combine_both
confidence:     0.72
rationale:      Keep new client; reapply retry wrapper around it
risk:           Method signatures changed. Run tests before continuing.

╭ gitmate · action required ─────────────────────╮
│  action  resolve_conflict                      │
│  risk    PROPOSE                               │
│  why     apply candidate patch to this block   │
╰────────────────────────────────────────────────╯
gitmate undo
gitmate undo list
ID                              COMMAND   OP           STATUS  INFO
20260502-060529-883008096ea4    ship      commit       done    92202cf4
20260502-060410-1f9c01ab33d2    sync      rebase       done    e64f9773→3a363ad9
20260502-060102-aabbccddeeff    push      push         done    main@e64f9773

gitmate undo
─── undo ship/commit [20260502-060529-883008096ea4] ───
  git reset --soft e64f9773
✓ undone

Undo a pushed commit (rewrites remote with --force-with-lease):

gitmate undo --force
─── undo push/push [20260502-060102-aabbccddeeff] ───
  git push --force-with-lease=main origin e64f9773:refs/heads/main (rewrites origin/main)
✓ undone

Each mutation lives in <repo>/.gitmate/checkpoints.json (capped at 50). Rebase/merge undo uses a refs/gitmate/backup/<id> ref captured before the operation. File writes back up to .gitmate/backups/<id>/.

gitmate schedule
gitmate schedule set --time 08:30 --enable
✓ schedule updated in /Users/you/.gitmate/config.json
→ wiring OS scheduler...
✓ launchd plist installed and loaded: /Users/you/Library/LaunchAgents/com.gitmate.daemon.plist

gitmate schedule add-repo .
✓ added /Users/you/code/api

gitmate schedule status
─── schedule ───
enabled:     true
time:        08:30 (local)
on-conflict: stop
notify:      log
repos:
  - /Users/you/code/api

OS scheduler:
  launchd plist installed at /Users/you/Library/LaunchAgents/com.gitmate.daemon.plist

Every morning at 08:30, OS fires gitmate sync --auto --all. Each operation produces a checkpoint, so anything the morning sync did can be reversed with gitmate undo.

schedule.onConflict controls auto-mode behavior on merge conflicts:

  • stop (default) — leave conflict markers, exit non-zero, you handle it
  • stash-and-skip — abort the rebase/merge, stash the work, skip to next repo

Install

Homebrew (macOS / Linux):

brew install krishyogee/tap/gitmate

Go:

go install github.com/krishyogee/gitmate@latest

Pre-built binary: grab one from Releases and drop it on your PATH.

Configure

The fast path:

gitmate init

Walks you through provider (anthropic / openai / groq), API key, and which shell rc to update. Writes:

  • ~/.gitmate/config.json for provider + model
  • ~/.gitmate/credentials.json for the API key (mode 0600, survives shell restarts)
  • An export <PROVIDER>_API_KEY=... line in your rc as a fallback

No source needed. It just works in new shells.

Prefer to set things by hand? Set the env vars yourself:

export ANTHROPIC_API_KEY=...   # primary (default)
export OPENAI_API_KEY=...      # fallback
export GROQ_API_KEY=...        # fallback
Knobs
Var Purpose
GITMATE_PROVIDER force anthropic / openai / groq
GITMATE_PLANNING_MODEL model for reasoning
GITMATE_DRAFTING_MODEL model for fast text gen
GITMATE_FALLBACK_MODEL fallback model
GITMATE_TEST_COMMAND test command for run_tests
GITMATE_LINT_COMMAND lint command for run_lint
GITMATE_DEFAULT_BASE default base branch
GITMATE_FRIENDLY 1/true — append AI-rephrased plain-English summary after each command
GITMATE_LANGUAGE language for the friendly summary (default english)
Friendly mode

Tired of cryptic CLI output? Turn on friendly mode and gitmate appends an AI-rephrased plain-English summary at the end of status, check, push, sync, and ship.

# one-off
gitmate --friendly status

# persist for this repo
gitmate config set output.friendly true

# persist globally
gitmate config set output.friendly true --global

# different language
gitmate config set output.language spanish

Auto-skips when --no-ai, no provider configured, or stdout is piped. AI failure falls back to the raw summary line. Original verbose output above is untouched.

Config layering

Highest wins:

  1. CLI flags (--auto, --dry-run, --base)
  2. Env vars (GITMATE_*)
  3. Repo-local: <repo>/.gitmate/config.json
  4. Global: ~/.gitmate/config.json
  5. Defaults

gitmate config prints the effective merged config and where each value came from.

Editing config without opening a JSON file like an animal
gitmate config set <key> <value>            # writes repo config (default)
gitmate config set <key> <value> --global   # writes ~/.gitmate/config.json
gitmate config get <key>                    # effective value (after layering)
gitmate config unset <key>                  # remove from file
# change base branch for this repo only
gitmate config set defaultBase develop

# switch to merge globally
gitmate config set syncMode merge --global

# nested keys via dot
gitmate config set models.drafting gpt-4o-mini
gitmate config set guardrails.maxLoopSteps 8

# values auto-parsed: bools, ints, floats, JSON arrays/objects
gitmate config set autoStash false
gitmate config set guardrails.minConfidenceToApply 0.7
gitmate config set guardrails.highRiskPatterns '["auth/","secrets/"]'

gitmate config set creates <repo>/.gitmate/config.json if it's missing.

Commands

Command What it does
gitmate Open the TUI dashboard (bare invocation, TTY only)
gitmate init Interactive provider + API key setup
gitmate ship Diff → commit msg → score → refine → approve → commit → optional PR
gitmate sync [base] Fetch + integrate origin/<branch> + integrate <base>; pause on conflict
gitmate push Push current branch to origin (with approval)
gitmate resolve <file> Explain each conflict block, propose patch, approve, write
gitmate check Predict merge pain — overlap, hotspots, risk score
gitmate status Branch state + overlap zones + risk indicator
gitmate explain [file] Plain-language explanation of a diff
gitmate metrics Approval rate, edit rate, latency, score distribution
gitmate config Show effective config + paths
gitmate config set/get/unset Edit repo or global config (--global) without hand-editing JSON
gitmate undo [list] Reverse the last recorded mutation (commit, rebase, merge, push, stash, file write, PR). --steps N, --id X, --dry-run, --force (pushed commits), --hard (commit reset mode)
gitmate schedule Status of daily auto-sync + OS scheduler
gitmate schedule set --enable --time 08:30 Enable + auto-install launchd / systemd timer for morning sync
gitmate schedule add-repo <path> Add a repo to the scheduled-sync list
gitmate schedule run-now Fire sync --auto --all immediately (test)
gitmate schedule install [--print] / uninstall Manage the OS scheduler entry directly
gitmate version Print version, commit, build date

Global flags: --auto (skip approvals — use sparingly), --dry-run, --base, --no-ai, --friendly, -v.

How it works

USER COMMAND  ──or──  TUI DASHBOARD (Bubble Tea)
         │                       │
         └───────────┬───────────┘
                     ▼
ORCHESTRATOR (ReAct loop, max 6 steps)
    ┌─────┴──────────────────────────────────────────┐
    │                                                │
    ▼                                                ▼
PLANNER (LLM)                              APPROVAL GATE
  - Receives: task + state + memory          - Tiers: READ / ADVISE / PROPOSE / EXECUTE
  - Returns: {thought, action, input}        - Lipgloss approval card
  - Routes: fast (drafting) / strong (planning)
    │
    ▼
EXECUTOR (Tool dispatch)
  git_diff | git_commit | parse_conflicts
  resolve_conflict | create_pr | run_tests
    │
    ▼
EVALUATOR (Score output)
  CommitEvaluator | ConflictEvaluator | ExplainEvaluator | RiskEvaluator
  Score ≥ 0.8 → stop | 0.4–0.8 → refine | < 0.4 → rotate model
    │
    ▼
MEMORY
  Session: attempts, last output, repo context
  Long-term: ~/.gitmate/memory.json (commit style, hot files, approvals)
    │
    ▼
OBSERVABILITY
  ~/.gitmate/ai-log.jsonl
  Every step: action, model, tokens, latency, score, user_action

The orchestrator can self-correct. If the evaluator scores a draft below 0.4 it rotates models; between 0.4 and 0.8 it tries to refine; above 0.8 it ships. You see all of this stream live.

The permission gate

Four tiers, escalating:

Tier Examples Default
READ git_diff, git_status, parse_conflicts auto-allow
ADVISE generate_commit, explain_conflict, explain_diff ask once per session
PROPOSE create_pr, resolve_conflict always ask
EXECUTE git_commit, git_push, run_tests, write_file always ask, every time

Approval card hotkeys: y yes · a allow session · p preview · e edit in $EDITOR · n no · ? explain.

What it will never do

These aren't config flags. They're hard-coded.

  1. Auto-apply an AI-generated patch without approval
  2. Auto-commit after a conflict resolution
  3. Skip the diff preview before any file write
  4. Take away your manual escape hatch (you can always abort)
  5. Hide confidence and risk on conflict resolutions
  6. Under-warn when in doubt — over-warning is the default
  7. Send secrets to the LLM. The redactor in internal/ai/compress.go strips API keys, private keys, and AWS / Slack / GitHub / OpenAI token patterns before any prompt leaves the box
  8. Treat auth/, schema/, migrations/ as routine — those always escalate to Complex routing
  9. Forget to recommend tests after applying a patch
  10. Skip the audit log. Even denials are written

Observability

Every AI call and every approval lands in ~/.gitmate/ai-log.jsonl. Tail it, grep it, ship it to your favorite log eater.

{"timestamp":"2026-05-01T12:00:00Z","provider":"anthropic","model":"claude-haiku-4-5-20251001","task":"commit_draft","input_tokens":312,"output_tokens":48,"latency_ms":612,"success":true}
{"timestamp":"2026-05-01T12:00:01Z","task":"ship","action":"generate_commit","score":0.85,"success":true}
{"timestamp":"2026-05-01T12:00:08Z","action":"git_commit","user_action":"approved","success":true}

gitmate metrics rolls it up:

{
  "total_calls": 142,
  "success_rate": 0.972,
  "approval_rate": 0.91,
  "edit_rate": 0.07,
  "fallback_rate": 0.03,
  "avg_latency_ms": 814.5,
  "avg_score": 0.87,
  "score_by_action": { "generate_commit": 0.83, "refine_commit": 0.91 },
  "approval_by_action": { "git_commit": 0.95, "create_pr": 0.84 }
}

If your approval rate is high and your edit rate is low, gitmate has learned your style. If they're not, the memory layer hasn't caught up yet — give it a week.

Why it's built this way

  • Subprocess git and gh, never reimplemented. Semantics match exactly what you already know. No surprises.
  • JSONL over stdout. Every AI call is auditable. Post-hoc analysis is grep + jq.
  • ReAct, not chains. Agents that can't self-correct are just expensive autocomplete.
  • Evaluator before approval gate. Catch slop without bothering you.
  • Memory as context, not training. Your style biases the model, it doesn't constrain it.
  • Approval as a tiny form, not free-text Y/n. Lipgloss card with structured fields means you read what you're approving.
  • TTY-aware TUI. Auto-disables in CI and pipes. Falls back to plain output.
  • Persistent credentials at 0600. Survive shell restarts. Env vars still win.
  • One static binary. Go. No Python, no Node, no Docker.

Repo layout

gitmate/
├── cmd/                 # cobra CLI entry points
│   ├── root.go          # rootCmd + dashboard launch + shared App
│   ├── ship.go          # ship: diff→draft→score→refine→approve→commit→PR
│   ├── sync.go          # sync: fetch → integrate origin/<branch> → integrate base
│   ├── resolve.go       # resolve: per-block explain + approve + write
│   ├── check.go         # check: overlap + hotspot + risk
│   ├── status.go        # status: branch + overlap + risk
│   ├── explain.go       # explain: AI diff summary
│   ├── push.go          # push: branch → origin (with approval)
│   ├── init.go          # interactive setup (provider + key + rc)
│   ├── metrics.go       # log aggregation
│   ├── config.go        # show effective config
│   ├── undo.go          # reverse recorded mutations
│   ├── schedule.go      # daily auto-sync via launchd / systemd
│   └── util.go          # helpers (scoreLabel, json)
├── internal/
│   ├── agent/           # ReAct loop: orchestrator, planner, executor, evaluator
│   ├── ai/              # multi-provider client + prompts + compression + redaction
│   ├── approval/        # permission tiers + manager + UI
│   ├── checkpoint/      # per-repo Op store + recorder for undo
│   ├── conflict/        # parser + classifier + AI explainer
│   ├── config/          # layered config + credentials.json
│   ├── memory/          # session (in-process) + store (~/.gitmate/memory.json)
│   ├── observability/   # JSONL logger + metrics computer
│   ├── tools/           # git, conflict, pr, shell tool implementations
│   └── tui/             # Bubble Tea dashboard + lipgloss styles + live stream
├── .github/workflows/
│   ├── ci.yml           # build + vet + test on PRs
│   └── release.yml      # GoReleaser on tag push
├── .goreleaser.yaml
├── main.go
└── go.mod

Build from source

git clone https://github.com/krishyogee/gitmate.git
cd gitmate
go build -o gitmate .
go test ./...

Releases: tag a vX.Y.Z, push it. GoReleaser handles the rest. Then bump the Homebrew tap formula.

Trivia: gitmate committed itself

The very first commit on this repo (d6dc1d1feat(cmd): add initial CLI implementation with agent and AI) was written and committed by gitmate, into gitmate's own repo, before it had ever been released anywhere.

The bootstrap:

git init
go build -o gitmate .
git add .
./gitmate ship --no-pr

What ran inside that single command:

[git_diff]        ─→ 42 files, 3750 lines staged → compressed to file-level summary
[generate_commit] ─→ groq llama-3.3-70b drafted message
[evaluator]       ─→ score 1.00 (conventional, ≤72 chars, body, non-generic)
[approval card]   ─→ git_commit · EXECUTE → user approved
[git_commit]      ─→ root-commit d6dc1d1 created

It worked because git diff --staged works without a HEAD, and git commit happily creates a root-commit when no parent exists. gitmate doesn't assume the repo has any history — it just needs something staged.

Every commit and push to this repo since has gone through gitmate ship + gitmate push. Self-hosted from commit zero.

License

MIT. See LICENSE. Use it, fork it, ship it.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal
ai
tui

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL