Documentation
The full usage reference for gitl v0.6.2.
Requires git in PATH; Go 1.22+ only for
go install / building from source.
Install
# Homebrew (macOS/Linux)
brew install akomyagin/tap/gitl
# npm — downloads the prebuilt binary for your platform from GitHub Releases
# and verifies its SHA256 checksum (no Go toolchain needed)
npx gitl-cli review HEAD~5..HEAD # or: npm install -g gitl-cli
# Go toolchain
go install github.com/akomyagin/gitl/cmd/gitl@latest
Or download a signed release binary from GitHub Releases — every release is cosign-signed (keyless) and covered by SLSA L3 build provenance; the verification steps are in VERIFY.md.
Commands & flags
# AI review of a commit range — streams tokens to the terminal in real time
GITL_API_KEY=sk-... gitl review HEAD~5..HEAD
# no key = deterministic offline review (heuristic risk, no network call)
gitl review HEAD~5..HEAD
# review staged (not yet committed) changes before `git commit`
gitl review --staged
# review a GitHub PR by number — requires the `gh` CLI (installed + authenticated);
# resolves base/head via gh, fetches `pull/N/head` locally when needed, and reviews
# the merge-base diff (base...head), same as GitHub shows
gitl review pr/42
# machine-readable output for CI + risk gating
gitl review HEAD~5..HEAD --format=json
gitl review HEAD~5..HEAD --fail-on=high # exit code 2 on high risk
# estimate cost without making an API call
gitl review HEAD~5..HEAD --dry-run
# cap the estimated cost of a run
gitl review HEAD~5..HEAD --max-cost-usd=0.50
# skip the on-disk LLM cache (always call the model)
gitl review HEAD~5..HEAD --no-cache
# disable streaming (non-interactive, buffered output)
gitl review HEAD~5..HEAD --no-stream
# suppress the informational offline-mode notice on stderr
# (also via GITL_QUIET=1 or output.quiet: true)
gitl review HEAD~5..HEAD --quiet
# changelog from last tag (or full history if no tags) — no LLM by default
gitl changelog
gitl changelog v1.2.0..HEAD --format=json
# AI changelog: rewrites the grouped result as release-note prose; falls back
# to the deterministic changelog without a key — never fails
GITL_API_KEY=sk-... gitl changelog --ai
# activity summary for the last N days — no LLM
gitl digest --days=14
# multi-repo digest: runs in parallel; one unreachable repo doesn't fail the rest
gitl digest --repos=../service-a,../service-b --format=json
# interactive TUI viewer for digest (requires a TTY)
gitl digest --days=14 --tui
# write a commented starter .gitl.yaml to the repo root
gitl init
gitl version
gitl --help
All three commands support --format=md|text|json. A custom
review system prompt is set via config only
(prompt.system_template_file); there is no
--system-template flag — see
Custom templates.
Exit codes
| Code | Meaning |
|---|---|
0 | ok — risk below the --fail-on threshold (or no gate set) |
1 | tool/runtime error — git, LLM, or config failure |
2 | the --fail-on risk gate triggered — CI can branch on exactly this |
Shell completions
gitl ships cobra-generated completions for bash, zsh, fish, and PowerShell.
Homebrew installs bash/zsh/fish completions automatically (release archives also
carry them under completions/). Otherwise:
# bash (current shell)
source <(gitl completion bash)
# bash (persistent) — Linux
gitl completion bash > /etc/bash_completion.d/gitl
# zsh (persistent)
gitl completion zsh > "${fpath[1]}/_gitl"
# fish
gitl completion fish > ~/.config/fish/completions/gitl.fish
# PowerShell
gitl completion powershell | Out-String | Invoke-Expression
Flags with fixed value sets — --format (md|text|json),
--fail-on (never|low|medium|high), and --provider —
complete their allowed values.
Configuration
The fast path: gitl init writes a commented starter
.gitl.yaml to the repo root (refusing to overwrite an existing one
without --force; --output writes elsewhere).
Two levels, merged by priority:
flag > env > .gitl.yaml (repo) >
~/.config/gitl/config.yaml (personal).
The repo-level .gitl.yaml is committed as a shared team policy
(risk threshold, excluded paths, changelog categories). Without a key, gitl runs
in deterministic offline mode.
In offline mode — or when a real model omits a valid risk block and gitl
falls back to the heuristic — the risk header is annotated with
*(heuristic)* (and "heuristic": true in
--format=json), so a deterministic score is never mistaken for a
model's own judgement.
Providers (llm.provider)
# OpenAI-compatible API (default)
llm:
provider: "openai"
api_key: "" # or env GITL_API_KEY
base_url: "https://api.openai.com/v1"
model: "gpt-4o-mini"
# Ollama — local/self-hosted, no key, free
llm:
provider: "ollama"
base_url: "http://localhost:11434/v1"
model: "llama3.1"
# Azure OpenAI — custom auth/endpoint format
llm:
provider: "azure_openai"
api_key: "" # or env GITL_API_KEY
model: "gpt-4o-mini" # used only for cost estimation
azure_openai:
endpoint: "https://<resource>.openai.azure.com"
deployment: "<deployment-name>"
api_version: "2024-08-01-preview"
# Anthropic (native Claude Messages API)
llm:
provider: "anthropic"
api_key: "" # or env GITL_API_KEY
model: "claude-sonnet-4-6"
# base_url optional; defaults to https://api.anthropic.com
# Google Gemini (Google AI Studio)
llm:
provider: "gemini"
api_key: "" # or env GITL_API_KEY
model: "gemini-2.5-flash"
# base_url optional; defaults to https://generativelanguage.googleapis.com/v1beta
Streaming (output.stream)
When reviewing interactively (md or text format on
a TTY), gitl streams tokens to the terminal as they arrive. Streaming is on by
default and switches off automatically in CI (non-TTY stdout), with
--format=json, and when a custom output.template_file
is configured (the template needs the complete response, so the review is
buffered and rendered through it instead).
Streaming is currently implemented only for the OpenAI-compatible
provider (openai / ollama /
azure_openai). With the native anthropic or
gemini provider, gitl transparently produces the same review as a
single buffered response regardless of output.stream /
--no-stream.
output:
stream: true # default; set false to always buffer
Color (output.color)
On an interactive terminal, gitl review colorizes the risk level
in the header (HIGH red, MEDIUM yellow, LOW green). Color switches off
automatically when stdout is not a TTY and never appears in
--format=json output. Precedence, highest first:
NO_COLORenv var set (any value, even empty) — color off (no-color.org);output.color: falsein config (orGITL_OUTPUT_COLOR=false) — color off;- stdout is not a TTY — color off;
- otherwise — color on.
Quiet mode (output.quiet)
Without an API key, review prints an informational "using
deterministic offline review" notice to stderr on every run (and
changelog --ai prints an analogous fallback notice). Suppress it
with any of:
- the
--quietflag onreview/changelog; - the
GITL_QUIETenv var set (any value, even empty); output.quiet: truein config (orGITL_OUTPUT_QUIET=true).
--quiet only silences the informational banner: errors, the
rendered review/changelog on stdout, and the --fail-on gate are
never affected.
LLM response cache (cache)
gitl review caches model responses on disk (SHA-256 of provider
+ model + prompt) under ~/.cache/gitl/review/ (XDG-compliant).
Identical diffs reuse the cached result instantly, with no API call or cost.
cache:
enabled: true # default
ttl_hours: 24 # entries older than this are ignored
Disable per-call with --no-cache. In --format=json
every review artifact carries additive run metadata:
duration_ms (wall-clock of the run) and
cache: { "hit": bool, "tier": "none|local|tiered" } —
tier reports the configured cache topology, not which backend
served a particular hit.
Shared remote cache (cache.remote) — opt-in
Off by default, BYO-backend: gitl never hosts a service and makes no network request to any cache until you configure one. Useful for CI cold starts — a shared HTTP KV endpoint lets one runner reuse another's review of the same diff.
cache:
enabled: true
ttl_hours: 24
remote: # opt-in shared cache for CI cold starts
url: https://cache.example.com/gitl # your endpoint; gitl hosts nothing
token_env: GITL_REMOTE_CACHE_TOKEN # env var holding an optional bearer token
timeout_ms: 3000
The local disk cache stays the first tier: reads check disk, then the remote
(a remote hit is backfilled to disk); writes go to both. The protocol is a dumb
key-value store over HTTP: GET {url}/{key} → 200 with the JSON
entry or 404 = miss; PUT {url}/{key} with the JSON entry → any 2xx
= stored. If token_env names an env var with a non-empty value,
both requests carry Authorization: Bearer. Keys are 64-char hex
SHA-256 strings.
Safety contract: any remote failure silently degrades to the local cache — it never fails the review. Entries contain only the model's response, keyed by an opaque hash: no diff and no prompt text ever reach the remote cache.
Risk trend (policy.risk_log_enabled)
Every gitl review run appends its risk outcome to a local JSONL
log (~/.local/share/gitl/risk-history.jsonl;
%AppData%\gitl\ on Windows). gitl digest reads it back
and shows a per-repo "Risk trend (last N days)" section — review count by level,
high-risk direction, and the last few reviews. In --format=json
it's the optional risk_trend field. Reviews are correlated with a
repository by its origin remote URL (falling back to the worktree
path).
policy:
risk_log_enabled: false # opt out (config only, no CLI flag)
Custom templates
Three independent, config-only overrides (no CLI flags):
prompt.system_template_file— your own review system prompt (security checklist, architecture constraints, team rules). Used only bygitl review; the template has access to{{ .Commits }},{{ .Diff }},{{ .Range }},{{ .Staged }}.prompt.changelog_system_template_file— your own changelog system prompt, used only bygitl changelog --ai; it gets{{ .Commits }},{{ .Range }},{{ .Grouped }}— not{{ .Diff }}(changelog --aiworks from commit metadata; there is no diff).output.template_file— your ownmd-format render template for the finished review artifact.
prompt:
system_template_file: "./review-policy.md" # path relative to CWD
changelog_system_template_file: "./changelog-policy.md"
output:
template_file: "./review-output.tmpl"
.gitl.yaml, so running gitl review
against a cloned repository you don't control can point it at a template inside
that same repository. text/template here can't read arbitrary files
or execute code, but treat an untrusted repo's .gitl.yaml with the
same caution you'd give its .git/hooks or build scripts.CI integrations
GitHub Action
The Action AI-reviews a pull request's commits and posts a sticky comment
with the risk score, optionally blocking merge above a threshold. It builds gitl
from source (go install at a pinned version).
name: gitl review
on:
pull_request:
permissions:
contents: read # for checkout
pull-requests: write # to post the review comment
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0 # required: without full history base..head won't resolve
- uses: akomyagin/gitl@v0.6.2
with:
gitl-api-key: ${{ secrets.GITL_API_KEY }} # BYOK
fail-on: high # optional: block merge on high risk
Key points:
- Key via
secrets.*only. If the secret is not set, the Action runs in deterministic offline mode (no network, no cost). - Minimal permissions. Only
pull-requests: writeandcontents: readare needed. fetch-depth: 0is required — a shallow clone won't resolvebase.sha..head.sha.fail-ondefaults tonever— the Action only comments unless you opt into the gate. When the gate triggers, the job fails with exit code2; a genuine tool error fails with1.- Provider selection. Pass
provider:(openai|ollama|azure_openai|anthropic|gemini), and optionallymodel:andbase-url:, alongsidegitl-api-key:. When omitted, they fall through to your.gitl.yaml/personal config and gitl's defaults. - Diff privacy. In CI the diff is sent to whichever LLM provider is configured. For private code, use a self-hosted/enterprise provider (Ollama, Azure OpenAI).
- PR description summary (opt-in). With
update-pr-description: true(defaultfalse) the Action also maintains a compact risk-summary block at the end of the PR description, delimited by a marker pair — only the text between the markers is ever replaced. GitHub-only.
Gitea Actions (experimental)
The same action.yml runs on Gitea Actions — the platform is
detected at run time via the GITEA_ACTIONS=true variable, and the
sticky comment is posted through Gitea's REST API with curl instead
of the gh CLI.
name: gitl review
on:
pull_request:
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: https://github.com/actions/checkout@v7
with:
fetch-depth: 0
- uses: https://github.com/akomyagin/gitl@v0.6.2
with:
gitl-api-key: ${{ secrets.GITL_API_KEY }} # BYOK; omit for offline mode
Requirements: Actions enabled, a recent act_runner (node24-capable), and a
runner image providing bash, git, curl,
jq and node. The REST-call path was verified end-to-end against a
real Gitea instance; the surrounding act_runner CI context is still
experimental — bug reports from real instances are very welcome.
GitLab CI (experimental)
gitl ships a GitLab CI/CD component mirroring the GitHub Action: it reviews the merge request's range and creates/updates a sticky MR note. On gitlab.com, include it as a catalog component:
# .gitlab-ci.yml (gitlab.com)
include:
- component: gitlab.com/alkom68/gitl/gitl-review@v0.6.2
inputs:
fail_on: "never" # default; set "high" to block risky MRs
On a self-hosted GitLab instance, consume the template via
include:remote straight from GitHub:
# .gitlab-ci.yml (self-hosted GitLab)
include:
- remote: "https://raw.githubusercontent.com/akomyagin/gitl/v0.6.2/templates/gitl-review.yml"
inputs:
fail_on: "never"
Setup — two CI/CD variables (both masked, never in YAML):
GITL_API_KEY (optional; without it gitl runs the offline review)
and GITL_GITLAB_TOKEN (project access token or PAT,
api scope, Reporter+, for posting the MR note — the
CI_JOB_TOKEN fallback is usually not authorized for the Notes
API). The REST calls and component YAML were verified against a real GitLab CE
instance; a live pipeline run is still experimental.
Bitbucket Pipelines (experimental)
The Bitbucket integration is a Pipe — a self-contained Docker image
(alkom68/gitl-review-pipe on Docker Hub, published since
v0.5.2) that resolves the PR range, runs
gitl review --format=json, and creates/updates a sticky PR
comment.
# bitbucket-pipelines.yml
pipelines:
pull-requests:
'**':
- step:
name: gitl review
clone:
depth: full # the default depth-50 clone may not contain the PR base
script:
- pipe: docker://alkom68/gitl-review-pipe:0.6.2
variables:
GITL_API_KEY: $GITL_API_KEY # BYOK; omit for offline review
GITL_BITBUCKET_TOKEN: $GITL_BITBUCKET_TOKEN # posts the PR comment
# FAIL_ON: "high" # default "never" — comment only, no gate
Setup — two secured repository/workspace variables:
GITL_API_KEY (optional) and GITL_BITBUCKET_TOKEN (an
access token with the pullrequest:write scope; alternatively
GITL_BITBUCKET_USER + GITL_BITBUCKET_APP_PASSWORD for
Basic auth). The pipe executes nothing fetched at run time — the binary and
scripts are baked into the versioned image. The in-container flow was verified
against a mock of the Bitbucket API; the live-pipeline path is still
experimental.
Pre-commit hook
gitl ships a pre-commit framework hook
so that gitl review --staged --quiet runs before every commit —
locally, offline, and at zero cost by default.
repos:
- repo: https://github.com/akomyagin/gitl
rev: v0.6.2 # pin to a released tag
hooks:
- id: gitl-review
then run pre-commit install. The framework builds the gitl
binary itself (language: golang) and caches the environment, so the
build cost is paid once. To opt into a blocking hook with a cost cap:
hooks:
- id: gitl-review
args: [--fail-on=high, --max-cost-usd=0.05]
A plain git hook works too:
# .git/hooks/pre-commit (chmod +x)
#!/usr/bin/env bash
set -euo pipefail
gitl review --staged --quiet || true
# to block the commit on high risk instead:
# gitl review --staged --quiet --fail-on=high
MCP server
gitl mcp runs gitl as a
Model Context Protocol stdio
server, for using gitl interactively inside an agent session (Claude Desktop,
Cursor, Windsurf, etc.) instead of shelling out. It exposes two tools:
gitl_review— same engine asgitl review:range/pr/staged(exactly one), optional per-callmodeloverride. Provider and endpoint are fixed at server startup by design: the tool caller is an AI agent, which can be steered by prompt injection inside the reviewed content — a per-callbase_urlwould let a malicious commit redirect the request and leak the real API key. Always returns the structured JSON artifact;risk.levelis returned as data (there is no--fail-onin MCP mode — no process exit code to gate).gitl_digest— same asgitl digest:days(default 7), optionalrepos. Without an explicitreposargument the tool only digests the server's working directory (plusdigest.reposfrom.gitl.yaml) — it never walks arbitrary paths on its own initiative.
{
"mcpServers": {
"gitl": {
"command": "gitl",
"args": ["mcp"]
}
}
}
Config is loaded once at startup the same way as the plain commands. Without
a key, tool calls run in the same deterministic offline mode as the CLI.
stdout is reserved for the MCP protocol; warnings go to stderr.