An open API service indexing awesome lists of open source software.

https://github.com/pbsladek/ai-mr-comment

A command-line tool written in Golang that generates professional Github/Gitlab Merge Request (MR) comments based on git diffs using LLMs.
https://github.com/pbsladek/ai-mr-comment

ai anthropic-claude cli gemini git golang llms ollama openai pull-requests

Last synced: 2 months ago
JSON representation

A command-line tool written in Golang that generates professional Github/Gitlab Merge Request (MR) comments based on git diffs using LLMs.

Awesome Lists containing this project

README

          

# ai-mr-comment

[![Go Test](https://github.com/pbsladek/ai-mr-comment/actions/workflows/test.yml/badge.svg)](https://github.com/pbsladek/ai-mr-comment/actions/workflows/test.yml)
[![Release](https://img.shields.io/github/v/release/pbsladek/ai-mr-comment)](https://github.com/pbsladek/ai-mr-comment/releases)
[![Go Report Card](https://goreportcard.com/badge/github.com/pbsladek/ai-mr-comment)](https://goreportcard.com/report/github.com/pbsladek/ai-mr-comment)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A command-line tool written in Go that generates professional GitLab Merge Request (MR) or GitHub Pull Request (PR) comments based on git diffs using AI (OpenAI, Anthropic, Gemini, Ollama, or local CLI tools — Claude Code, Gemini CLI, or Codex).

## Features

- Reads git diffs from current repo or from a file
- Auto-detects branch diff against `origin/main` or `origin/master` when no flags are given
- Staged-only diff (`--staged`) for reviewing changes before committing
- Exclude files from the diff by glob pattern (`--exclude`)
- Smart chunking (`--smart-chunk`) for large diffs: summarizes each file, then synthesizes a final comment
- Optional MR/PR title generation (`--title`) alongside the comment, printed as a distinct section
- **Generate comments directly from a GitHub PR or GitLab MR URL** (`--pr`) — no local checkout needed
- Supports public **github.com**, **GitHub Enterprise**, public **gitlab.com**, and **self-hosted GitLab** instances
- Supports OpenAI, Anthropic (Claude), Google Gemini, and Ollama APIs
- **Local CLI providers** — delegate auth to locally installed CLI tools, no API key management needed:
- `claude-cli` — uses the local `claude` binary (Claude Code session), no API key required
- `gemini-cli` — uses the local `gemini` binary (Google OAuth), no API key required
- `codex-cli` — uses the local `codex` binary (quiet mode), requires `OPENAI_API_KEY`
- Customizable API endpoints and models via `--model` flag or config
- Multiple prompt styles — `default`, `conventional`, `technical`, `user-focused`, `emoji`, `sassy`, `monday`, `jira`, `commit`, `commit-emoji`, `commit-conventional`, `chaos`, `haiku`, `roast`, `intern`, `shakespeare`, `manager`, `yoda`, `excuse`
- **Jira-aware template** (`--template=jira`) — extracts ticket key from branch name and places it first so Jira auto-links
- **Commit message generation** (`--commit-msg`) — outputs a single conventional-style line ready for `git commit -m`
- **`quick-commit` subcommand** — stages all changes, generates a conventional commit message (type and scope derived from the diff), commits, and pushes in one step
- **Gitmoji support** (`--emoji` on `quick-commit`) — appends a type-matched emoji to the commit subject (✨ feat, 🐛 fix, ♻️ refactor, etc.)
- **Opt-out conventional commits** (`--no-conventional` on `quick-commit`) — skips conventional format enforcement for free-form messages
- **Chaos mode** (`--chaos`) — chaotic/absurd conventional commit on `quick-commit`; unhinged-but-accurate MR/PR description on the root command
- **Haiku mode** (`--haiku`) — commit description as a 5-7-5 haiku on `quick-commit`; full MR/PR description in haiku form on the root command
- **Roast mode** (`--roast`) — passive-aggressive commit on `quick-commit`; sardonically judgmental MR/PR description on the root command
- **Fortune trailer** (`--fortune` on `quick-commit`) — appends a dev-wisdom fortune-cookie quote as a commit body
- **Monday mode** (`--monday` on `quick-commit`) — casual, low-energy "pre-coffee" commit message
- **Jira commit mode** (`--jira` on `quick-commit`) — prefixes the commit message with the Jira ticket key extracted from the branch name
- **Emoji commit mode** (`--emoji-commit` on `quick-commit`) — appends a type-matched gitmoji to the commit description
- **Sassy mode** (`--sassy` on `quick-commit`) — sassy but technically accurate commit message
- **Technical mode** (`--technical` on `quick-commit`) — maximum technical precision; references exact function/struct names from the diff
- **Intern mode** (`--intern`) — written by an overly enthusiastic junior developer who is SO excited about everything; works on both root cmd and `quick-commit`
- **Shakespeare mode** (`--shakespeare`) — Early Modern English, iambic cadence, dramatic flair; works on both root cmd and `quick-commit`
- **Manager mode** (`--manager`) — passive-aggressive corporate non-speak, "per our earlier discussion"; works on both root cmd and `quick-commit`
- **Yoda mode** (`--yoda`) — inverted syntax, object before subject, strong with this one the force is; works on both root cmd and `quick-commit`
- **Excuse mode** (`--excuse`) — technically accurate but every section has a built-in justification for why it had to be this way; works on both root cmd and `quick-commit`
- **CI/CD gate** (`--exit-code`) — exits with code 2 when the AI flags critical issues, enabling pipeline enforcement
- **Auto-post comments** (`--post`) — publishes the generated comment directly to the GitHub PR or GitLab MR via API
- **Update remote PR/MR metadata** (`--update-title`, `--update-description`) — writes the generated title and/or description back to GitHub or GitLab
- **Named config profiles** (`--profile`) — switch between providers/models/templates with a single flag; define profiles in `~/.ai-mr-comment.toml` under `[profile.]`
- Configuration file support (`~/.ai-mr-comment.toml`)
- Environment variable configuration
- Outputs to a file (`--output`) or the system clipboard (`--clipboard=title|description|commit-msg|all`); when `--output` is set, stdout is suppressed — output goes to the file only
- `--output` writes JSON when `--format=json` is set — ideal for saving structured review artifacts
- Structured JSON output for scripting and CI (`--format json`)
- Verbose debug logging to stderr (`--verbose`) — API timing, diff stats, config details
- Live streaming output to the terminal — tokens appear as they are generated
- Bootstrap a config file with `init-config` (never edit TOML by hand again)
- All prompt templates stored as editable files in `templates/` — embedded at build time, overridable at `~/.config/ai-mr-comment/templates/.tmpl`
- Shell completions for bash, zsh, fish, and PowerShell (`completion` subcommand)
- **Shell aliases** (`gen-aliases`) — prints `amc` and `amc-*` convenience aliases ready to source into your shell profile
- **Changelog generation** (`changelog`) — produces a user-facing Keep a Changelog entry from a commit range, grouped by Added / Fixed / Breaking Changes etc.
- **Custom system prompt** (`--system-prompt`) — supply an ad-hoc prompt inline or from a file (`@path`), overriding the active template for a single run
- Precise token counting for Gemini and heuristic estimation for others
- Estimated cost calculation in debug mode
- Native binary with no runtime dependencies

## Installation

### Prerequisites

- Git
- API key for your preferred provider (OpenAI, Anthropic, or Google Gemini) — **not required** when using `claude-cli`, `gemini-cli`, or `ollama`

### macOS (Homebrew)

```bash
brew tap pbsladek/tap
brew install ai-mr-comment
```

Or install directly in one command:

```bash
brew install pbsladek/tap/ai-mr-comment
```

Upgrade later with:

```bash
brew upgrade ai-mr-comment
```

### Other Platforms

Download the latest binary for your OS from the [Releases](https://github.com/pbsladek/ai-mr-comment/releases) page.
Release archives are published for Linux, macOS, and Windows on both x86_64 and arm64.

For Linux/macOS script installation, download and inspect `scripts/install.sh` from the [repository](https://github.com/pbsladek/ai-mr-comment), then run it directly. The script verifies the downloaded archive against the release `checksums.txt` (and optionally the cosign signature) before installing.

### Building from Source

Requires Go 1.26+.

```bash
# Clone the repository
git clone https://github.com/pbsladek/ai-mr-comment.git
cd ai-mr-comment

# Build
make build

# The binary will be available at ./dist/ai-mr-comment
# Build and run on current diff
make test-run

# Fetch latest tags first so --version shows the correct release tag
make fetch-tags build
```

### Docker

No Go toolchain required. The image includes git so all diff and commit commands work.
Published image: `pwbsladek/ai-mr-comment` on Docker Hub.
Release images are published as multi-arch Linux manifests for amd64 and arm64.
FIPS variant tag: `pwbsladek/ai-mr-comment:-fips`.

If you build locally, log in to DHI first (base images are pulled from `dhi.io`):

```bash
docker login dhi.io
```

```bash
# Build the image
make docker-build

# Scan for fixable critical/high CVEs
make docker-scout

# Run against the current repo diff (mounts PWD, passes API keys from env)
make docker-run ARGS="--provider openai"

# Run quick-commit
make docker-quick-commit ARGS="--dry-run"

# Pull a specific version (replace v0.18.1 with the version you want)
docker pull pwbsladek/ai-mr-comment:v0.18.1
docker pull pwbsladek/ai-mr-comment:v0.18.1-fips

# Or use docker directly
docker run --rm -it \
-v "$(pwd):/repo" -w /repo \
-e OPENAI_API_KEY \
pwbsladek/ai-mr-comment:v0.18.1 --provider openai
```

**Mounting your config file:**
```bash
docker run --rm -it \
-v "$(pwd):/repo" -w /repo \
-v "$HOME/.ai-mr-comment.toml:/home/nonroot/.ai-mr-comment.toml:ro" \
-e OPENAI_API_KEY \
pwbsladek/ai-mr-comment:v0.18.1
```

**Fetching a PR/MR by URL (no repo mount needed):**
```bash
docker run --rm \
-e OPENAI_API_KEY \
-e GITHUB_TOKEN \
pwbsladek/ai-mr-comment:v0.18.1 --pr https://github.com/owner/repo/pull/42
```

> **Note:** `--clipboard` is not available inside a container. Use `--output` or `--format json` instead to capture the output.

## Configuration File

The tool looks for `.ai-mr-comment.toml` in your home directory or the current directory.

### Generating the config file

Run `init-config` to write a fully-commented template to `~/.ai-mr-comment.toml`:

```bash
ai-mr-comment init-config

# Write to a custom path instead
ai-mr-comment init-config --output ./ai-mr-comment.toml
```

The command refuses to overwrite an existing file. Remove the old file first if you want to regenerate it.

```toml
# Choose which provider to use: "openai", "anthropic", "gemini", "ollama", "claude-cli", "gemini-cli", or "codex-cli"
provider = "anthropic"

# === Anthropic Settings ===
anthropic_api_key = "xxxx"
anthropic_model = "claude-sonnet-4-6"
anthropic_endpoint = "https://api.anthropic.com/"
# Other Anthropic models: claude-opus-4-6, claude-haiku-4-5-20251001

# === Claude CLI Settings ===
# No API key needed — auth is delegated to the local claude CLI process.
# Requires Claude Code to be installed (https://claude.ai/code).
# claude_cli_path = "" # auto-detected: ~/.claude/local/claude, then PATH
claude_cli_model = "claude-sonnet-4-6"

# === OpenAI Settings ===
openai_api_key = "xxxx"
openai_model = "gpt-4.1-mini"
openai_endpoint = "https://api.openai.com/v1/"
# Other OpenAI models: gpt-4.1, gpt-4.1-nano, gpt-4o, gpt-4o-mini, o3, o3-mini

# === Gemini Settings ===
gemini_api_key = "xxxx"
gemini_model = "gemini-2.5-flash"
# Other Gemini models: gemini-2.5-pro, gemini-2.5-flash-lite, gemini-3

# === Gemini CLI Settings ===
# No API key needed — auth is delegated to the local gemini CLI process (Google OAuth).
# Requires the Gemini CLI to be installed (https://github.com/google-gemini/gemini-cli).
# gemini_cli_path = "" # auto-detected from PATH
gemini_cli_model = "gemini-2.5-flash"

# === Codex CLI Settings ===
# No API key needed — auth is delegated to the local codex CLI process.
# Requires the OpenAI Codex CLI to be installed (https://github.com/openai/codex).
# codex_cli_path = "" # auto-detected from PATH
# codex_cli_model = "" # uses codex default when empty

# === Ollama Settings ===
ollama_model = "llama3.2"
ollama_endpoint = "http://localhost:11434/api/generate"

# === GitHub / GitHub Enterprise ===
github_token = "xxxx" # or set GITHUB_TOKEN env var (required for private repos)
# github_base_url = "" # set for GitHub Enterprise, e.g. https://github.mycompany.com

# === GitLab / Self-Hosted GitLab ===
gitlab_token = "xxxx" # or set GITLAB_TOKEN env var (required for private projects)
# gitlab_base_url = "" # set for self-hosted GitLab, e.g. https://gitlab.mycompany.com

# === Template Settings ===
# Options: default, conventional, technical, user-focused, emoji, sassy, monday,
# jira, commit, commit-emoji, commit-conventional,
# chaos, haiku, roast, intern, shakespeare, manager, yoda, excuse
template = "default"

# === Named Profiles ===
# Switch with: ai-mr-comment --profile
# A profile overrides any top-level setting for that invocation only.

[profile.fast]
provider = "openai"
openai_model = "gpt-4.1-nano"
template = "conventional"

[profile.openai]
provider = "openai"
openai_model = "gpt-4.1-mini"

[profile.anthropic]
provider = "anthropic"
anthropic_model = "claude-opus-4-6"
template = "technical"

[profile.gemini]
provider = "gemini"
gemini_model = "gemini-2.5-flash"

[profile.claude-cli]
provider = "claude-cli"
claude_cli_model = "claude-sonnet-4-6"

[profile.gemini-cli]
provider = "gemini-cli"
gemini_cli_model = "gemini-2.5-flash"

[profile.codex-cli]
provider = "codex-cli"

[profile.local]
provider = "ollama"
ollama_model = "llama3.2"
```

## Example Output

**Text mode (`--title`):**

```
── Title ────────────────────────────────

feat: Add user authentication system

── Description ──────────────────────────

## Summary

Adds a complete user authentication system using JWT tokens and bcrypt password hashing. Required to protect API endpoints and support user identity across sessions.

## Key Changes

- Added user model with bcrypt password hashing
- Implemented JWT authentication middleware
- Created login and registration API endpoints
- Added comprehensive unit tests for auth logic

## Why These Changes

Provides a secure foundation for user identity, allowing protected access to API resources.

```

**JSON mode (`--format json`):**

```json
{
"title": "feat: Add user authentication system",
"description": "## Key Changes\n\n- Added user model...",
"comment": "## Key Changes\n\n- Added user model...",
"provider": "openai",
"model": "gpt-4o-mini"
}
```

`description` and `comment` carry the same value; `comment` is kept for backwards compatibility. When `--exit-code` is set, a `"verdict": "PASS"` or `"verdict": "FAIL"` field is also included.

**Commit message mode (`--commit-msg --format json`):**

```json
{"commit_message":"feat(auth): add JWT refresh token support"}
```

## Usage

```bash
# Generate comment for the full branch diff (auto-detects merge base with origin/main)
ai-mr-comment

# Generate comment for staged changes only
ai-mr-comment --staged

# Exclude generated or vendored files
ai-mr-comment --exclude "vendor/**" --exclude "*.sum"

# Use smart chunking for large diffs (summarizes per-file, then combines)
ai-mr-comment --smart-chunk

# Use a specific provider and template
ai-mr-comment --provider anthropic --template technical

# Override the model for a single run
ai-mr-comment --model gpt-4o

# Generate comment for a specific commit range
ai-mr-comment --commit "HEAD~3..HEAD"

# Pipe a diff through stdin
git diff | ai-mr-comment --file=- --plain
gh pr diff 42 | ai-mr-comment --quiet

# Generate a comment from a GitHub PR URL (no local checkout needed)
ai-mr-comment --pr https://github.com/owner/repo/pull/42

# Generate a comment from a GitLab MR URL
ai-mr-comment --pr https://gitlab.com/group/project/-/merge_requests/5

# Generate a comment from a GitHub Enterprise or self-hosted GitLab URL
# (set github_base_url / gitlab_base_url in config or env)
GITHUB_BASE_URL=https://github.mycompany.com \
ai-mr-comment --pr https://github.mycompany.com/owner/repo/pull/42

GITLAB_BASE_URL=https://gitlab.mycompany.com \
ai-mr-comment --pr https://gitlab.mycompany.com/group/project/-/merge_requests/5

# Generate a title and comment together (shown as separate sections)
ai-mr-comment --title

# Output structured JSON for CI/scripting
# Always includes title, description, provider, and model fields
ai-mr-comment --format json

# Generate only a git commit message (conventional commits style)
ai-mr-comment --commit-msg --staged

# Generate a commit message as JSON
ai-mr-comment --commit-msg --format json | jq -r '.commit_message'

# Copy the description to clipboard
ai-mr-comment --clipboard=description

# Copy the title to clipboard
ai-mr-comment --title --clipboard=title

# Copy a commit message to clipboard
ai-mr-comment --commit-msg --clipboard=commit-msg

# Gate CI on AI review — exits 2 if critical issues are detected
ai-mr-comment --exit-code --pr https://github.com/owner/repo/pull/42

# Generate and immediately post the comment back to the PR/MR
ai-mr-comment --pr https://github.com/owner/repo/pull/42 --post

# Generate and update the actual PR/MR title and description
ai-mr-comment --pr https://github.com/owner/repo/pull/42 --update-title --update-description

# Save JSON review to a file (for artifact upload in CI)
ai-mr-comment --format json --output /tmp/review.json --pr https://github.com/owner/repo/pull/42

# Enable verbose debug logging to stderr (API timing, diff stats, config)
ai-mr-comment --verbose

# Show token and cost estimation without calling the API
ai-mr-comment --debug

# Print the exact prompt/request for agent debugging without calling the API
git diff | ai-mr-comment --file=- --print-prompt
git diff | ai-mr-comment --file=- --print-request | jq .

# Preview planned work without calling the provider or writing files
git diff | ai-mr-comment --dry-run --file=-

# Inspect changed files and diff stats without spending tokens
ai-mr-comment --changed-files
ai-mr-comment --summary-only --format=json | jq .

# Inspect resolved config, tokens, git state, and local CLI binaries
ai-mr-comment doctor
ai-mr-comment config-dump --format=json | jq .

# Apply a built-in preset
ai-mr-comment --preset ci --pr https://github.com/owner/repo/pull/42
ai-mr-comment --preset local-fast --file=-
ai-mr-comment --preset security
ai-mr-comment changelog --preset release-notes --commit="v1.2.0..HEAD"

# Override the system prompt for a single run (inline)
ai-mr-comment --system-prompt="Focus only on security vulnerabilities."

# Override the system prompt from a file
ai-mr-comment --system-prompt=@~/prompts/security-review.txt

# Chaos mode — unhinged but accurate MR/PR description
ai-mr-comment --chaos

# Haiku mode — entire description as 5-7-5 haikus
ai-mr-comment --haiku

# Roast mode — sardonically judgmental but correct
ai-mr-comment --roast

# Generate a user-facing changelog entry from the last 10 commits
ai-mr-comment changelog --commit="HEAD~10..HEAD"

# Generate a changelog entry for a release range, output as JSON
ai-mr-comment changelog --commit="v1.2.0..HEAD" --format=json

# Print shell aliases (amc, amc-qc, amc-cl, …) and add to profile
ai-mr-comment gen-aliases >> ~/.zshrc

# Or evaluate on every shell start to always stay up to date
# eval "$(ai-mr-comment gen-aliases)"

# Generate shell completion script
ai-mr-comment completion bash >> ~/.bash_completion
ai-mr-comment completion zsh > ~/.zsh/completions/_ai-mr-comment

# Bootstrap a config file (writes ~/.ai-mr-comment.toml)
ai-mr-comment init-config

# Use a named profile (provider/model/template preset)
ai-mr-comment --profile fast
ai-mr-comment --profile anthropic --title
```

### Agent and Pipe Integration

`ai-mr-comment` can be used as a Unix-style filter:

```bash
git diff | ai-mr-comment --file=- --plain
git show HEAD | ai-mr-comment commit-message --file=-
gh pr diff 42 | ai-mr-comment review --quiet
git diff | ai-mr-comment verdict --file=-
```

For structured agent input, pass JSON on stdin:

```bash
printf '%s' '{
"title": "Add auth flow",
"description": "Implements token refresh",
"branch": "feat/AUTH-123-refresh",
"diff": "diff --git a/auth.go b/auth.go\n+..."
}' | ai-mr-comment review --input=json --quiet
```

Agent-oriented commands:

```bash
ai-mr-comment review --file=-
ai-mr-comment title --file=-
ai-mr-comment commit-message --file=-
ai-mr-comment verdict --file=-
ai-mr-comment estimate --file=-
```

Machine-friendly output modes:

```bash
ai-mr-comment --quiet
ai-mr-comment --plain
ai-mr-comment --stream=jsonl
ai-mr-comment --print-prompt
ai-mr-comment --print-request
ai-mr-comment --dry-run
ai-mr-comment --summary-only
```

Exit codes are stable for automation: `0` success/pass, `1` tool or runtime error, `2` AI verdict fail, `3` no diff/input, `4` invalid usage.

### Options

- `--pr `: GitHub PR or GitLab MR URL — fetches diff and metadata remotely, no local checkout needed. Works with `github.com`, GitHub Enterprise, `gitlab.com`, and self-hosted GitLab. Mutually exclusive with `--staged`, `--commit`, and `--file`.
- `--commit `: Specific commit or range
- `--staged`: Diff staged changes only (`git diff --cached`); mutually exclusive with `--commit`
- `--exclude `: Exclude files matching glob pattern (e.g. `vendor/**`, `*.sum`). Can be repeated.
- `--smart-chunk`: Split large diffs by file, summarize each, then synthesize a final comment
- `--title`: Generate a concise MR/PR title alongside the comment; printed as a distinct `── Title ──` section in text mode. When `--format=json` is used, title is always generated automatically (no need for `--title`). Mutually exclusive with `--commit-msg`.
- `--commit-msg`: Generate a single-line git commit message instead of a full MR/PR description. Output is clean text or `{"commit_message":"..."}` in JSON mode. Mutually exclusive with `--title`.
- `--multi-line`: Generate a multi-line commit message (subject + blank line + markdown body) when used with `--commit-msg` or `quick-commit`. GitHub and GitLab use this format to pre-fill the PR/MR title and description automatically.
- `--exit-code`: Exit with code 2 when the AI detects critical issues (bugs, security vulnerabilities, data loss risks). Mutually exclusive with `--commit-msg`.
- `--post`: Post the generated comment back to the GitHub PR or GitLab MR via API (requires `--pr`). Uses the same token as diff fetching.
- `--update-title`: Update the GitHub PR title or GitLab MR title with the generated title (requires `--pr`). Mutually exclusive with `--commit-msg`.
- `--update-description`: Update the GitHub PR body or GitLab MR description with the generated description (requires `--pr`). Mutually exclusive with `--commit-msg` and `--title-only`.
- `--dry-run`: Print the resolved plan and diff stats without calling the provider, writing output files, copying to clipboard, posting comments, or updating PR/MR metadata.
- `--changed-files`: Print changed file paths and exit without calling the provider.
- `--summary-only`: Print diff stats and changed files and exit without calling the provider.
- `--file `: Read diff from file instead of git. Use `--file=-` to read from stdin.
- `--output `: Write output to file instead of stdout — **suppresses all terminal output**. Writes JSON when `--format=json` is set; writes the commit message when `--commit-msg` is set.
- `--clipboard `: Copy to system clipboard — `title`, `description` (or `comment`), `commit-msg`, or `all` (title + description separated by a blank line)
- `--format `: Output format — `text` (default) or `json`
- `--input `: Input format — `text` (default) or `json` for structured agent input (`title`, `description`, `branch`, `diff`)
- `--quiet`: Force strict JSON output on stdout for agents and scripts
- `--plain`, `--no-decorate`: Suppress text section headers and decorative separators
- `--stream=jsonl`: Emit JSON Lines events (`start`, `token`, `done`) instead of decorated text
- `--print-prompt`: Print the resolved system prompt and exit without calling the provider
- `--print-request`: Print the resolved provider request as JSON and exit without calling the provider
- `--preset `: Apply built-in defaults: `ci` (JSON + exit-code + technical), `local-fast` (Ollama + plain), `security` (security-focused technical review), or `release-notes` (user-focused title/description).
- `--provider `: Provider (`openai`, `anthropic`, `gemini`, `ollama`, `claude-cli`, `gemini-cli`, `codex-cli`)
- `--model `: Override the model for this run (e.g. `gpt-4o`, `claude-opus-4-6`, `gemini-2.5-flash`)
- `-t, --template `: Template style — `default`, `conventional`, `technical`, `user-focused`, `emoji`, `sassy`, `monday`, `jira`, `commit`, `commit-emoji`, `commit-conventional`, `chaos`, `haiku`, `roast`, `intern`, `shakespeare`, `manager`, `yoda`, `excuse` (`commit`, `commit-emoji`, and `commit-conventional` require `--commit-msg`; style templates cannot be combined with `--commit-msg`)
- `--system-prompt `: Override the system prompt for this run. Pass the prompt inline (`--system-prompt="Focus on security"`) or read it from a file with an `@` prefix (`--system-prompt=@review.txt`). Mutually exclusive with `--template`.
- `--chaos`: Generate a chaotic, dramatically over-the-top MR/PR description (still technically accurate). Mutually exclusive with `--template`, `--system-prompt`, `--commit-msg`.
- `--haiku`: Generate the entire MR/PR description as a sequence of haikus (5-7-5). Mutually exclusive with `--template`, `--system-prompt`, `--commit-msg`.
- `--roast`: Generate a technically accurate but sardonically judgmental MR/PR description. Mutually exclusive with `--template`, `--system-prompt`, `--commit-msg`.
- `--profile `: Activate a named config profile defined under `[profile.]` in `~/.ai-mr-comment.toml`. Overrides top-level provider, model, template, and other settings for this run only.
- `--debug`: Show precise token usage and cost estimation without calling the generation API
- `--verbose`: Print detailed debug lines to stderr — config file path, diff stats, template source, streaming decision, and per-API-call timing and response size
- `--version`: Print version, commit SHA, and repository URL in `key=value` format and exit
- `-h, --help`: Print help

### Diagnostics

`doctor` and its alias `config-dump` inspect local readiness without making a live provider request:

```bash
ai-mr-comment doctor
ai-mr-comment doctor --provider openai
ai-mr-comment doctor --preset local-fast --format=json
```

Use `check` when you want a live provider ping.

### Subcommands

- `quick-commit [flags]`: Stage all changes, generate an AI commit message, commit, and push in one step. See [Quick Commit](#quick-commit) below.
- `publish [flags]`: Generate and sync a PR/MR title, description, managed summary comment, labels, and reviewers in one step. See [Publish](#publish) below.
- `changelog [flags]`: Generate a user-facing changelog entry from a commit range or diff file. See [Changelog](#changelog) below.
- `gen-aliases [--shell bash|zsh] [--output FILE]`: Print `amc` and `amc-*` shell aliases to stdout. See [Shell Aliases](#shell-aliases) below.
- `models [--provider ]`: List known model names for a provider.
- `init-config [--output ]`: Write a default config file to `~/.ai-mr-comment.toml` (or the given path). Refuses to overwrite an existing file.
- `completion [bash|zsh|fish|powershell]`: Print a shell completion script to stdout.

## GitHub & GitLab Integration (`--pr`)

Point `--pr` at any GitHub pull request or GitLab merge request URL to generate a comment without needing the repository checked out locally.

```bash
# GitHub (public)
ai-mr-comment --pr https://github.com/owner/repo/pull/42

# GitLab (public)
ai-mr-comment --pr https://gitlab.com/group/project/-/merge_requests/5
```

### Authentication

Public repos and projects work without a token (subject to API rate limits). For private repos set the token via environment variable or config file:

| Platform | Env var | Config key |
|---|---|---|
| GitHub / GitHub Enterprise | `GITHUB_TOKEN` | `github_token` |
| GitLab / Self-Hosted GitLab | `GITLAB_TOKEN` | `gitlab_token` |

### Self-Hosted Instances

For GitHub Enterprise or self-hosted GitLab, pass the instance base URL. The SDKs automatically append the correct API path (`/api/v3/` for GitHub, `/api/v4/` for GitLab).

**Environment variables:**
```bash
# GitHub Enterprise
GITHUB_BASE_URL=https://github.mycompany.com \
ai-mr-comment --pr https://github.mycompany.com/owner/repo/pull/42

# Self-hosted GitLab
GITLAB_BASE_URL=https://gitlab.mycompany.com \
ai-mr-comment --pr https://gitlab.mycompany.com/group/project/-/merge_requests/5
```

**Config file (`.ai-mr-comment.toml`):**
```toml
github_base_url = "https://github.mycompany.com"
gitlab_base_url = "https://gitlab.mycompany.com"
```

## Templates

Select a template with `-t` / `--template`. All templates receive the branch name as context (useful for ticket key extraction).

All built-in templates live in `templates/` in the repository and are embedded into the binary at build time. You can override any template by placing a file at `~/.config/ai-mr-comment/templates/.tmpl`.

| Name | Description |
|---|---|
| `default` | Professional description with Summary, Key Changes, Why, Checklist, Notes |
| `conventional` | Conventional Commits body with optional BREAKING CHANGE and Refs |
| `technical` | Deep technical focus — Summary, Implementation Details, Rationale, Testing Strategy |
| `user-focused` | Non-technical perspective — What's New, Why Important, Impact |
| `emoji` | Fun emoji-rich format with Summary section |
| `sassy` | Dry-wit tone, technically accurate |
| `monday` | Casual "pre-coffee" tone |
| `jira` | Puts the Jira ticket key from the branch name first so Jira auto-links; includes Summary section |
| `commit` | Single-line conventional commit message (requires `--commit-msg`) |
| `commit-emoji` | Single-line gitmoji-style commit message (requires `--commit-msg`) |
| `commit-conventional` | Full Conventional Commits message with optional body, BREAKING CHANGE footer, and Refs (requires `--commit-msg`) |
| `chaos` | Chaotic, dramatically over-the-top description (still technically accurate) |
| `haiku` | Entire description as a sequence of haikus |
| `roast` | Sardonically judgmental, dry-wit tone |
| `intern` | Overly enthusiastic junior-developer energy |
| `shakespeare` | Shakespearean Early Modern English |
| `manager` | Passive-aggressive corporate non-speak |
| `yoda` | Inverted Yoda syntax |
| `excuse` | Technically accurate with built-in excuses for every decision |

### Jira Integration

The `jira` template extracts the ticket key from your branch name (e.g. `feat/ABC-123-add-login` → `ABC-123`) and places it at the very start of the description so Jira's branch/commit linking picks it up automatically.

```bash
ai-mr-comment --template jira --staged
# Output starts with: "ABC-123 Add login endpoint\n\n## Key Changes..."
```

This works because the current branch name is automatically prepended to the diff context for all local git operations (not `--file` or `--pr`).

## Custom System Prompt

Use `--system-prompt` to replace the active template prompt for a single run — no template file needed.

```bash
# Inline prompt
ai-mr-comment --system-prompt="Focus only on security vulnerabilities."

# Read from a file (@ prefix)
ai-mr-comment --system-prompt=@~/prompts/security-review.txt

# Works on the changelog subcommand too
ai-mr-comment changelog --commit="v1.2.0..HEAD" \
--system-prompt="List only breaking changes in one sentence each."
```

`--system-prompt` and `--template` are mutually exclusive — use one or the other per run.

When `--exit-code` is also set, the verdict preamble is still prepended on top of the custom prompt (same behaviour as with templates).

## Commit Messages

Use `--commit-msg` to generate a single conventional-style commit message instead of a full PR description.

```bash
# Print the message (text mode)
ai-mr-comment --commit-msg --staged

# JSON output — single key, pipeline-friendly
ai-mr-comment --commit-msg --format json
# {"commit_message":"feat(auth): add JWT refresh token support"}

# Pipe directly into git commit
git commit -m "$(ai-mr-comment --commit-msg --staged)"

# Copy to clipboard
ai-mr-comment --commit-msg --staged --clipboard=commit-msg

# Gitmoji style
ai-mr-comment --commit-msg --template commit-emoji --staged

# Multi-line message: subject + blank line + markdown body
# GitHub/GitLab use this to pre-fill the PR/MR title and description
ai-mr-comment --commit-msg --multi-line --staged

# Use it with quick-commit too
ai-mr-comment quick-commit --multi-line
```

`--commit-msg` and `--title` are mutually exclusive. In JSON mode, the response contains only `commit_message` (no `description` or `title` fields).

## Quick Commit

`quick-commit` is a one-command shortcut for the full stage → AI message → commit → push workflow.

```bash
# Full workflow
ai-mr-comment quick-commit

# Preview the generated message without touching git
ai-mr-comment quick-commit --dry-run

# Generate a long multi-section commit body
ai-mr-comment quick-commit --long

# Aim for a specific body length
ai-mr-comment quick-commit --body-lines=32

# Commit but skip the push
ai-mr-comment quick-commit --no-push

# Use a specific provider or model
ai-mr-comment quick-commit --provider anthropic --model claude-opus-4-6

# Use a named profile
ai-mr-comment quick-commit --profile anthropic

# Force a breaking-change commit (feat!) for a major version bump
ai-mr-comment quick-commit --breaking

# Append a type-matched gitmoji to the subject
ai-mr-comment quick-commit --emoji
# e.g. feat(cli): add flag ✨

# Skip conventional commits format — free-form message
ai-mr-comment quick-commit --no-conventional

# Chaos mode — random funny/absurd conventional commit (great for pipeline trigger commits)
ai-mr-comment quick-commit --chaos
# e.g. ci(gremlins): ask nicely that the gremlins stop eating the cache

# Haiku mode — commit description as a 5-7-5 haiku about the diff
ai-mr-comment quick-commit --haiku
# e.g. feat(auth): tokens flow in / sessions find their rightful home / login works at last

# Roast mode — technically accurate but passive-aggressively judgmental
ai-mr-comment quick-commit --roast
# e.g. refactor(naming): rename 'x' to something a human might understand

# Fortune trailer — appends a dev-wisdom quote as a commit body
ai-mr-comment quick-commit --fortune
# commit subject + blank line + "The best code is the code you didn't have to write."

# Combine chaos + fortune for maximum vibes
ai-mr-comment quick-commit --chaos --fortune

# Monday mode — casual, low-energy "pre-coffee" commit
ai-mr-comment quick-commit --monday
# e.g. chore(deps): update the packages, it is what it is

# Jira mode — prefix with ticket key extracted from branch name
ai-mr-comment quick-commit --jira
# e.g. feat(auth): ABC-123 add JWT refresh token support

# Emoji-commit mode — append a type-matched gitmoji to the description
ai-mr-comment quick-commit --emoji-commit
# e.g. feat(auth): add refresh token rotation ✨

# Sassy mode — accurate but with attitude
ai-mr-comment quick-commit --sassy
# e.g. fix(auth): handle the edge case that definitely should have been handled

# Technical mode — maximum precision, references exact names from the diff
ai-mr-comment quick-commit --technical
# e.g. refactor(api): replace O(n²) scan with hash-map lookup in resolveUser

# Intern mode — SO excited about this commit!!
ai-mr-comment quick-commit --intern
# e.g. feat(auth): add the login thing everyone was asking about!!

# Shakespeare mode — hark, the diff doth speak
ai-mr-comment quick-commit --shakespeare
# e.g. fix(parser): hark, the null doth crash our noble code no more

# Manager mode — per our earlier alignment session
ai-mr-comment quick-commit --manager
# e.g. feat(auth): action item from last sprint re: token refresh, going forward

# Yoda mode — inverted syntax, strong with this commit it is
ai-mr-comment quick-commit --yoda
# e.g. fix(cache): stale data, purge we must

# Excuse mode — it happened for reasons, there were constraints
ai-mr-comment quick-commit --excuse
# e.g. fix(parser): handle empty input, which apparently nobody tested before

# JSON output — only commit_message is printed, all status lines suppressed
ai-mr-comment quick-commit --format json
# {"commit_message":"feat(cli): add login endpoint"}
```

Steps performed:
1. `git add .` — stages all changes
2. Reads the staged diff; prepends branch name for ticket key context
3. Calls AI with the conventional commits prompt
4. `git commit -m ""`
5. `git push --set-upstream origin ` — works for new branches too

| Flag | Description |
|---|---|
| `--dry-run` | Generate and print the message, skip all git operations |
| `--no-push` | Commit but skip the push |
| `--breaking` | Force `feat!` conventional commit type to signal a breaking change (major version bump) |
| `--multi-line` | Generate a multi-line message (subject + body) that pre-fills the PR/MR title and description |
| `--long` | Generate a longer multi-section body; implies `--multi-line` |
| `--body-lines` | Target body line count for long multi-line commits; implies `--multi-line` |
| `--emoji` | Append a type-matched gitmoji to the subject (✨ feat, 🐛 fix, ♻️ refactor, 💥 breaking, etc.) |
| `--no-conventional` | Skip conventional commits format — AI generates a free-form message instead |
| `--chaos` | Generate a random funny/absurd conventional commit (ignores the real diff; great for pipeline trigger commits) |
| `--haiku` | Write the commit description as a 5-7-5 haiku about the actual diff |
| `--roast` | Technically accurate but passive-aggressively judgmental commit message |
| `--fortune` | Append a dev-wisdom fortune-cookie quote as a commit body (`git log` shows it as a trailer) |
| `--monday` | Casual, low-energy "pre-coffee" tone; accurate but undemanding |
| `--jira` | Prefix the message with the Jira ticket key extracted from the branch name |
| `--emoji-commit` | Append a type-matched gitmoji to the commit description |
| `--sassy` | Sassy but technically accurate commit message |
| `--technical` | Maximum technical precision; references exact function/struct names from the diff |
| `--intern` | Overly enthusiastic junior-developer commit message |
| `--shakespeare` | Commit description in Shakespearean Early Modern English |
| `--manager` | Passive-aggressive corporate non-speak commit message |
| `--yoda` | Commit description in Yoda's inverted syntax |
| `--excuse` | Technically accurate commit message with a built-in excuse |
| `--format json` | Output `{"commit_message":"..."}` only; suppress status lines |
| `--provider` | Override the AI provider |
| `--model` | Override the model |
| `--profile` | Activate a named config profile |

## Publish

`publish` is the one-shot remote PR/MR workflow. It generates a title and description, updates the remote title/body, preserves manual description text by syncing only a managed section, and creates or updates one managed summary comment.

```bash
# Existing GitHub PR or GitLab MR
ai-mr-comment publish --pr https://github.com/owner/repo/pull/42

# Add labels, request reviewers, and mark the title as Draft: if the generated text looks risky
ai-mr-comment publish \
--pr https://gitlab.com/group/project/-/merge_requests/5 \
--auto-labels \
--label=release-notes \
--reviewer=12345 \
--draft-if-risky

# From a local branch: find an existing PR/MR or create one from origin
ai-mr-comment publish

# Preview without writing remote changes
ai-mr-comment publish --pr https://github.com/owner/repo/pull/42 --dry-run --format=json
```

| Flag | Description |
|---|---|
| `--pr` | Existing GitHub PR or GitLab MR URL. If omitted, `publish` uses the current branch and `origin` remote to find or create a PR/MR |
| `--no-update-title` | Skip updating the remote title |
| `--no-update-description` | Skip updating the remote PR body / MR description |
| `--replace-description` | Replace the full description instead of syncing the managed `ai-mr-comment` section |
| `--post-summary` | Create or update one managed summary comment. Defaults to `true`; use `--post-summary=false` to disable |
| `--auto-labels` | Apply simple labels inferred from changed files and generated text, such as `docs`, `tests`, `ci`, `dependencies`, or `security` |
| `--label` | Add an explicit label. Can be repeated or comma-separated |
| `--reviewer` | Request a reviewer. GitHub uses usernames; GitLab uses numeric user IDs |
| `--draft-if-risky` | Prefix the generated title with `Draft:` when generated text indicates high risk |
| `--dry-run` | Generate and print the planned remote changes without writing them |
| `--format` | `text` (default) or `json` |

## Changelog

`changelog` generates a user-facing changelog entry from any diff source. Output follows the [Keep a Changelog](https://keepachangelog.com/) format, grouped under the headings that apply: Added, Changed, Deprecated, Removed, Fixed, Security, Breaking Changes.

```bash
# Last 10 commits (Makefile default)
make changelog

# Specific release range
ai-mr-comment changelog --commit="v1.2.0..HEAD"

# From a diff file
ai-mr-comment changelog --file=my.diff --provider=anthropic

# JSON output for CI pipelines
ai-mr-comment changelog --commit="v1.2.0..HEAD" --format=json

# Write directly to a file
ai-mr-comment changelog --commit="v1.2.0..HEAD" --output=CHANGELOG.md

# Override the prompt for a custom format
ai-mr-comment changelog --commit="v1.2.0..HEAD" \
--system-prompt="List only breaking changes."
```

| Flag | Description |
|---|---|
| `--commit` | Commit or commit range (e.g. `v1.2.0..HEAD`) |
| `--file` | Read diff from a file instead of git |
| `--format` | `text` (default) or `json` (`{"changelog":"...","provider":"...","model":"..."}`) |
| `--output` | Write output to a file instead of stdout |
| `--provider` | AI provider override |
| `--model` | Model override |
| `--profile` | Activate a named config profile |
| `--system-prompt` | Override the changelog system prompt |

## Shell Aliases

`gen-aliases` prints a block of ready-to-source shell alias definitions.

```bash
# Append once to your profile
ai-mr-comment gen-aliases >> ~/.zshrc

# Or evaluate on every shell start (always up-to-date)
eval "$(ai-mr-comment gen-aliases)"

# Write directly to a file
ai-mr-comment gen-aliases --output=~/.bashrc

# Via make
make gen-aliases
```

Aliases defined:

| Alias | Expands to |
|---|---|
| `amc` | `ai-mr-comment` |
| `amc-staged` | `ai-mr-comment --staged` |
| `amc-commit` | `ai-mr-comment --commit-msg --staged` |
| `amc-commit-multi` | `ai-mr-comment --commit-msg --multi-line --staged` |
| `amc-title` | `ai-mr-comment --title` |
| `amc-json` | `ai-mr-comment --format=json` |
| `amc-debug` | `ai-mr-comment --debug` |
| `amc-chaos` | `ai-mr-comment --chaos` |
| `amc-haiku` | `ai-mr-comment --haiku` |
| `amc-roast` | `ai-mr-comment --roast` |
| `amc-intern` | `ai-mr-comment --intern` |
| `amc-shakespeare` | `ai-mr-comment --shakespeare` |
| `amc-manager` | `ai-mr-comment --manager` |
| `amc-yoda` | `ai-mr-comment --yoda` |
| `amc-excuse` | `ai-mr-comment --excuse` |
| `amc-conventional` | `ai-mr-comment --template=conventional` |
| `amc-emoji` | `ai-mr-comment --template=emoji` |
| `amc-jira` | `ai-mr-comment --template=jira` |
| `amc-monday` | `ai-mr-comment --template=monday` |
| `amc-sassy` | `ai-mr-comment --template=sassy` |
| `amc-technical` | `ai-mr-comment --template=technical` |
| `amc-user` | `ai-mr-comment --template=user-focused` |
| `amc-qc` | `ai-mr-comment quick-commit` |
| `amc-qc-dry` | `ai-mr-comment quick-commit --dry-run` |
| `amc-qc-breaking` | `ai-mr-comment quick-commit --breaking` |
| `amc-qc-chaos` | `ai-mr-comment quick-commit --chaos` |
| `amc-qc-haiku` | `ai-mr-comment quick-commit --haiku` |
| `amc-qc-roast` | `ai-mr-comment quick-commit --roast` |
| `amc-qc-fortune` | `ai-mr-comment quick-commit --fortune` |
| `amc-qc-monday` | `ai-mr-comment quick-commit --monday` |
| `amc-qc-jira` | `ai-mr-comment quick-commit --jira` |
| `amc-qc-emoji` | `ai-mr-comment quick-commit --emoji-commit` |
| `amc-qc-sassy` | `ai-mr-comment quick-commit --sassy` |
| `amc-qc-technical` | `ai-mr-comment quick-commit --technical` |
| `amc-qc-intern` | `ai-mr-comment quick-commit --intern` |
| `amc-qc-shakespeare` | `ai-mr-comment quick-commit --shakespeare` |
| `amc-qc-manager` | `ai-mr-comment quick-commit --manager` |
| `amc-qc-yoda` | `ai-mr-comment quick-commit --yoda` |
| `amc-qc-excuse` | `ai-mr-comment quick-commit --excuse` |
| `amc-cl` | `ai-mr-comment changelog` |
| `amc-models` | `ai-mr-comment models` |
| `amc-init` | `ai-mr-comment init-config` |

| Flag | Description |
|---|---|
| `--shell` | `bash` (default) or `zsh` — both use identical alias syntax |
| `--output` | Also write aliases to this file |

## Local CLI Providers

Three providers delegate AI calls and authentication to a locally installed CLI tool — no API key management in `ai-mr-comment` is required. These are useful on machines where auth is handled by the CLI's own session (SSO, OAuth, Claude Code session).

### `claude-cli` — Claude Code

Delegates to the [`claude`](https://claude.ai/code) binary. Auth is managed by Claude Code — no `ANTHROPIC_API_KEY` needed.

```toml
provider = "claude-cli"
claude_cli_model = "claude-sonnet-4-6"
```

Binary lookup order:
1. `claude_cli_path` in config (if set)
2. `~/.claude/local/claude`
3. `claude` on `$PATH`

| Key | Description | Default |
|---|---|---|
| `claude_cli_path` | Explicit path to the `claude` binary | auto-detected |
| `claude_cli_model` | Model passed via `--model` | `claude-sonnet-4-6` |

### `gemini-cli` — Google Gemini CLI

Delegates to the [`gemini`](https://github.com/google-gemini/gemini-cli) binary. Auth uses Google OAuth — no `GEMINI_API_KEY` needed.

Install: `npm install -g @google/gemini-cli`

```toml
provider = "gemini-cli"
gemini_cli_model = "gemini-2.5-flash"
```

| Key | Description | Default |
|---|---|---|
| `gemini_cli_path` | Explicit path to the `gemini` binary | auto-detected via `$PATH` |
| `gemini_cli_model` | Model passed via `--model` | `gemini-2.5-flash` |

### `codex-cli` — OpenAI Codex CLI

Delegates to the [`codex`](https://github.com/openai/codex) binary in quiet/non-interactive mode. Still requires `OPENAI_API_KEY` but handles its own session and approval flow.

Install: `npm install -g @openai/codex`

```toml
provider = "codex-cli"
# codex_cli_model = "" # leave empty to use codex default
```

| Key | Description | Default |
|---|---|---|
| `codex_cli_path` | Explicit path to the `codex` binary | auto-detected via `$PATH` |
| `codex_cli_model` | Model passed via `--model` | empty (codex default) |

### Profiles

All three have built-in profiles:

```bash
ai-mr-comment --profile claude-cli
ai-mr-comment --profile gemini-cli
ai-mr-comment --profile codex-cli
```

## Named Profiles

Define named profiles in `~/.ai-mr-comment.toml` under `[profile.]` sections. Each profile can override any top-level setting — provider, model, template, endpoint, etc.

```toml
provider = "anthropic"
anthropic_model = "claude-sonnet-4-6"
template = "default"

[profile.fast]
provider = "openai"
openai_model = "gpt-4.1-nano"
template = "conventional"

[profile.openai]
provider = "openai"
openai_model = "gpt-4.1"
template = "technical"

[profile.anthropic]
provider = "anthropic"
anthropic_model = "claude-opus-4-6"
template = "technical"

[profile.gemini]
provider = "gemini"
gemini_model = "gemini-3-pro-preview"
template = "technical"

[profile.local]
provider = "ollama"
ollama_model = "llama3.2"

[profile.claude-cli]
provider = "claude-cli"
claude_cli_model = "claude-sonnet-4-6"

[profile.gemini-cli]
provider = "gemini-cli"
gemini_cli_model = "gemini-2.5-flash"

[profile.codex-cli]
provider = "codex-cli"
```

Activate a profile by passing `--profile ` to any command:

```bash
# Quick review with the fast profile
ai-mr-comment --profile fast

# Deep technical review with Anthropic Opus
ai-mr-comment --profile anthropic --title

# Generate a changelog with Gemini Pro
ai-mr-comment changelog --profile gemini --commit="v1.2.0..HEAD"

# Commit with Ollama (no API key needed)
ai-mr-comment quick-commit --profile local --dry-run

# Use Claude CLI session auth (no API key needed)
ai-mr-comment --profile claude-cli

# Use Gemini CLI with Google OAuth (no API key needed)
ai-mr-comment --profile gemini-cli

# Use Codex CLI
ai-mr-comment --profile codex-cli
```

Run `ai-mr-comment init-config` to generate a config file pre-populated with the standard profiles above.

## CI/CD Usage

Three flags are designed specifically for pipeline integration:

### `--exit-code` — Gate merges on AI review

Instruct the AI to output a `VERDICT: PASS` or `VERDICT: FAIL` line before its review. If FAIL, the process exits with code 2, failing your pipeline step.

```bash
# Fail the pipeline if the AI detects critical issues
ai-mr-comment --exit-code --pr "$PR_URL"
echo $? # 0 = PASS, 2 = FAIL, 1 = tool error

# JSON includes the verdict field for downstream processing
ai-mr-comment --exit-code --format json --pr "$PR_URL" | jq .verdict
```

**GitHub Actions example:**
```yaml
- name: AI Code Review
run: ai-mr-comment --exit-code --pr "${{ github.event.pull_request.html_url }}"
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Step fails (exit 2) if AI detects critical issues
```

### `--post` — Auto-post comments to PRs/MRs

After generating the comment, post it directly to the PR or MR via the GitHub/GitLab API. Uses the same token as diff fetching — no extra setup needed.

```bash
# Generate and post in one step
ai-mr-comment --pr "$PR_URL" --post

# Combine with exit-code: review, post, and gate in one command
ai-mr-comment --exit-code --post --pr "$PR_URL"
```

**GitHub Actions example:**
```yaml
- name: AI Review & Comment
run: ai-mr-comment --post --pr "${{ github.event.pull_request.html_url }}"
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

### `--output` with `--format json` — Save review artifacts

Write the full JSON review to a file. Useful for artifact upload, audit trails, or passing data between pipeline jobs.

```bash
# Write JSON to file
ai-mr-comment --format json --output review.json --pr "$PR_URL"

# Upload as artifact (GitHub Actions)
# - uses: actions/upload-artifact@v4
# with: { name: ai-review, path: review.json }

# Read in a later job
cat review.json | jq -r '.description'
cat review.json | jq -r '.verdict' # when --exit-code was used
```

When `--commit-msg` is set, `--output` writes the commit message (with a trailing newline) rather than JSON.

## Streaming Output

When running interactively (stdout is a TTY), the tool streams tokens directly to the terminal as the model generates them, so you see the comment appear word by word rather than waiting for the full response.

Streaming is automatically disabled and the output is fully buffered when:

- `--format json` is set (the full response is needed before encoding)
- `--smart-chunk` is set (multi-stage summarise + synthesise calls)
- `--output ` is set (writing to a file)
- stdout is not a TTY (piped output, CI, redirected to file)

If a streaming call fails mid-flight, the tool transparently falls back to a standard buffered request and outputs the full comment normally.

## Token & Cost Estimation

When running with the `--debug` flag, the tool provides a detailed breakdown of the expected usage:

- **Gemini**: Uses official SDK token counting (100% accurate).
- **OpenAI/Anthropic/Ollama**: Uses a conservative character-based heuristic (~3.5 chars per token).
- **Cost**: Calculates estimated input cost in USD based on current model pricing (Ollama is free).

## Development

### Project Structure

- `./`: Main Go source files (`main.go`, `api.go`, etc.)
- `templates/`: Markdown prompt templates
- `testdata/`: Sample git diffs for testing
- `dist/`: Compiled binaries (after build)

### Testing

```bash
# Run unit tests
make test

# Run integration tests (requires GEMINI_API_KEY)
make test-integration

# Run Ollama-only integration tests (CPU-friendly small model)
OLLAMA_ENDPOINT=http://127.0.0.1:11434/api/generate \
OLLAMA_MODEL=llama3.2:1b \
make test-integration-ollama

# Run fuzz tests (30s per target)
make test-fuzz

# Run response-quality evals on curated diff fixtures (promptfoo)
make eval-quality

# Run writing-quality evals for commit messages + PR title/description
make eval-quality-writing

# Install/update pinned eval dependencies only
make eval-quality-deps

# Run linter
make lint
```

CI coverage on PRs includes fast unit/lint/fuzz checks.
Long Ollama integration + promptfoo quality eval lanes run from a separate manual workflow: `Ollama Integration (Manual)`.
Open GitHub Actions, select that workflow, then click **Run workflow**.

### Response Quality Evals

`make eval-quality` runs end-to-end evals against fixture diffs in `evals/` and scores the generated review for recall, severity coverage, actionability, and false positives.
In CI, evals write a compact JSON report to `evals/promptfoo-results.json` and print a short summary.
CI uses serialized eval calls and extended per-test timeout for CPU models (`PROMPTFOO_MAX_CONCURRENCY=1`, `PROMPTFOO_EVAL_TIMEOUT_MS=300000`).

Prerequisites:
- Node.js + `npm`
- A reachable model provider (defaults to local Ollama)
- Built binary (handled automatically by `make eval-quality`)
- Pinned eval dependencies (including `promptfoo`) are installed from `evals/package-lock.json`

Useful overrides:
- `AMC_EVAL_PROVIDER` (default `ollama`)
- `AMC_EVAL_MODEL` (default `llama3.2:1b`)
- `AMC_EVAL_TEMPLATE` (default `technical`)
- `AMC_EVAL_FLAGS` (additional CLI flags)

```bash
# Example: run evals against local Ollama 1B model
AMC_EVAL_PROVIDER=ollama AMC_EVAL_MODEL=llama3.2:1b make eval-quality

# Example: run commit + PR writing evals against local Ollama 8B model
AMC_EVAL_PROVIDER=ollama AMC_EVAL_MODEL=llama3.1:8b AMC_EVAL_TEMPLATE=default make eval-quality-writing

# Open the latest promptfoo report UI
make eval-quality-view
```

### Dependency Updates

Dependabot opens grouped PRs weekly (Monday) for Go modules, npm packages in `evals/`, and GitHub Actions.

To approve and auto-merge all open Dependabot PRs at once:

```bash
gh pr list --author "app/dependabot" --json number --jq '.[].number' | \
xargs -I {} gh pr merge {} --auto --squash
```

> `--auto` queues each PR to merge once all required status checks pass. Requires auto-merge to be enabled in the repo settings. Drop `--auto` and use `--squash` alone to merge immediately without waiting for CI.

### Shell Completions

```bash
# Install bash completions
make install-completion-bash

# Install zsh completions
make install-completion-zsh

# Or generate manually for any shell
ai-mr-comment completion [bash|zsh|fish|powershell]
```

## Contributing

### Commit Convention

This project uses [Conventional Commits](https://www.conventionalcommits.org/) to drive automatic semantic versioning. Every commit merged to `main` must follow the format:

```
[optional scope]:
```

| Type | Description | Version bump |
|---|---|---|
| `fix:` | Bug fix | patch (`0.0.x`) |
| `feat:` | New feature | minor (`0.x.0`) |
| `feat!:` or `BREAKING CHANGE:` footer | Breaking change | major (`x.0.0`) |
| `chore:`, `docs:`, `ci:`, `test:`, `refactor:`, `style:`, `perf:` | Non-functional | none |

**Examples:**
```
fix: handle empty diff gracefully
feat: add clipboard output support
feat!: redesign config file format
chore: update dependencies
```

### Release Process

Merging to `main` automatically creates a version tag (e.g. `v0.2.0`) based on the commits since the last release — no GitHub Release is created yet.

When ready to publish a release:
1. Go to **GitHub → Releases → Draft a new release**
2. Pick the pre-created tag from the dropdown
3. Click **Publish release** — GoReleaser builds and attaches signed binaries automatically
4. Release workflow dispatches the source tarball metadata to `pbsladek/homebrew-tap` for Homebrew validation/update automation

## License

MIT

## Acknowledgements

This project is a Go rewrite of [mr-comment](https://github.com/RobertKozak/mr-comment) originally created by [Robert Kozak](https://github.com/RobertKozak).