gitl
Reads and analyzes git history for AI review, changelog generation, and activity summaries.
Allows reviewing GitHub pull requests by number, and can post AI review comments and gate on risk as a GitHub Action.
Provides AI-powered git history review using Google Gemini models via the Gemini API.
Provides AI-powered git history review using local/self-hosted Ollama models, enabling offline or private AI.
Provides AI-powered git history review using OpenAI-compatible APIs (e.g., GPT models), with support for streaming.
Integrates as a pre-commit hook to review staged changes before committing.
gitl
AI-powered git history reviewer for CLI and CI. gitl (git-log-lens) reads a
repository's git history and turns it into a structured engineering artifact via LLM:
gitl review <range>— AI review of a commit range / PR with machine-readable risk scoring (low|medium|high) for CI gating (--fail-on=high→ non-zero exit code); streams tokens to the terminal in real time; on-disk LLM response cache with an optional shared remote cache for CI; custom system-prompt templates;--stagedreviews staged (uncommitted) changes beforegit commit(also available as a pre-commit hook).gitl changelog [<range>]— Keep a Changelog-style changelog, grouped by conventional commits (defaults to last tag →HEAD); deterministic by default,--aioptionally rewrites it with the model as readable release-note prose;gitl digest [--days=N] [--repos=a,b,c]— activity summary by author/topic/file, including multiple repositories in parallel; interactive TUI viewer (--tui).
A clean CLI binary plus a GitHub Action wrapper — no server, no database, no hosted key storage. BYOK (bring your own key) with multi-provider support: OpenAI-compatible API, Ollama (local/self-hosted), Azure OpenAI, native Anthropic (Claude), Google Gemini. No telemetry.
Status:
v0.6.2released — all three commands work on real repositories with all three output formats (md|text|json). The Action posts AI reviews as sticky PR comments and gates on risk score. Release binaries are cross-compiled, cosign-signed, and covered by SLSA L3 build provenance (see VERIFY.md).
Quick start
Requires Go 1.22+ and git in PATH.
# build
go build ./...
# AI review of a commit range — streams tokens to the terminal in real time
GITL_API_KEY=sk-... go run ./cmd/gitl review HEAD~5..HEAD
# no key = deterministic offline review (heuristic risk, no network call)
go run ./cmd/gitl review HEAD~5..HEAD
# review staged (not yet committed) changes before `git commit`
go run ./cmd/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
go run ./cmd/gitl review pr/42
# machine-readable output for CI + risk gating
go run ./cmd/gitl review HEAD~5..HEAD --format=json
go run ./cmd/gitl review HEAD~5..HEAD --fail-on=high # non-zero exit on high risk
# estimate cost without making an API call
go run ./cmd/gitl review HEAD~5..HEAD --dry-run
# custom system-prompt template (e.g. your team's review policy) — set via
# config only (prompt.system_template_file); there is no --system-template flag
# see Configuration → Custom templates below
# skip the on-disk LLM cache (always call the model)
go run ./cmd/gitl review HEAD~5..HEAD --no-cache
# disable streaming (non-interactive, buffered output)
go run ./cmd/gitl review HEAD~5..HEAD --no-stream
# changelog from last tag (or full history if no tags) — no LLM by default
go run ./cmd/gitl changelog
go run ./cmd/gitl changelog v1.2.0..HEAD --format=json
# AI changelog: the model rewrites the grouped result as release-note prose and
# reclassifies significant non-conventional commits out of "Other". Without an API
# key (or on a malformed model response) it falls back to the deterministic
# changelog with a warning — never fails. --dry-run/--max-cost-usd/--no-cache work
# the same as for review.
GITL_API_KEY=sk-... go run ./cmd/gitl changelog --ai
# activity summary for the last N days — no LLM
go run ./cmd/gitl digest --days=14
# multi-repo digest: runs in parallel; one unreachable repo does not fail the rest
go run ./cmd/gitl digest --repos=../service-a,../service-b --format=json
# interactive TUI viewer for digest (requires a TTY)
go run ./cmd/gitl digest --days=14 --tui
go run ./cmd/gitl version
go run ./cmd/gitl --help
# tests
go test ./...Install:
# Go toolchain
go install github.com/akomyagin/gitl/cmd/gitl@latest
# 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
# Or download a signed release binary from GitHub Releases (see VERIFY.md)Local multi-provider test (Ollama)
docker-compose.yml starts only the dev dependency — a local Ollama instance for
testing the multi-provider LLM client (gitl itself is not containerized):
docker compose up ollamaRelated MCP server: code-intel-mcp
Configuration
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/v1betaStreaming (output.stream)
When reviewing interactively (md or text format on a TTY), gitl streams tokens
to the terminal as they arrive — no waiting for the full response. 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 (no token-by-token output) regardless of output.stream / --no-stream.
output:
stream: true # default; set false to always bufferDisable per-call: gitl review HEAD~5..HEAD --no-stream
LLM response cache (cache)
gitl review caches model responses on disk (SHA-256 of provider + model + prompt).
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 ignoredCache lives in ~/.cache/gitl/review/ (XDG-compliant). Disable per-call:
gitl review HEAD~5..HEAD --no-cache
Shared remote cache (cache.remote) — opt-in
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 — every runner starts with an empty disk, but 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 (off by default)
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: 3000When configured, 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 — any static object store or tiny handler works:
GET {url}/{key}→200with the JSON entry body, or404= miss. Any other status, network error, or timeout is treated as a miss.PUT {url}/{key}with the JSON entry as the request body (Content-Type: application/json) → any2xx= stored.If
token_envnames an env var with a non-empty value, both requests carryAuthorization: Bearer <token>. The token itself is never read from a config file (same discipline asGITL_API_KEY).Keys are 64-character hex SHA-256 strings; values are opaque to the server.
Safety contract: any remote failure (timeout, 5xx, unreachable endpoint)
silently degrades to the local cache / no cache — it never fails the review.
The stored entries contain only the model's response, keyed by an opaque hash:
no diff and no prompt text ever reach the remote cache. Entries older than
ttl_hours are ignored client-side regardless of what the server returns.
Risk trend (policy.risk_log_enabled)
Every gitl review run appends its risk outcome (level, range, provider, timestamp)
to a local JSONL log: $XDG_DATA_HOME/gitl/risk-history.jsonl (default
~/.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 (recent half vs earlier half
of the window), and the last few reviews. In --format=json it appears as an
optional risk_trend field (schema_version stays 1; consumers that predate
it see the same document as before). Repos with no history simply omit the section.
Reviews are correlated with a repository by its origin remote URL (falling back
to the worktree path when there is no origin).
Limitation: the history is local to your machine — it does not persist across CI runners (each starts with a cold disk), so the trend is a feature for local developer use, not for CI.
Opt out in config (no CLI flag):
policy:
risk_log_enabled: falseCustom templates (prompt.*_template_file / output.template_file)
Independent, config-only overrides (there is no CLI flag for any of them):
prompt.system_template_file— your own review system prompt, to steer the model's focus (security checklist, architecture constraints, team rules). Used only bygitl review:prompt: system_template_file: "./review-policy.md" # path relative to CWDThe review system-prompt template has access to
{{ .Commits }},{{ .Diff }},{{ .Range }},{{ .Staged }}(seeinternal/prompt/templates.go).prompt.changelog_system_template_file— your own changelog system prompt, used only bygitl changelog --ai:prompt: changelog_system_template_file: "./changelog-policy.md" # path relative to CWDThe changelog system-prompt template has access to
{{ .Commits }},{{ .Range }},{{ .Grouped }}— not{{ .Diff }}:changelog --aiworks from commit metadata, there is no diff, and a review-shaped template using.Diffwould fail here. That's exactly why the two keys are separate: each command only reads its own key, and either may be set without the other.output.template_file— your ownmd-format render template for the finished review artifact:output: template_file: "./review-output.tmpl" # path relative to CWDThe output template has the render template functions in
internal/render/render.go(render.TemplateFuncs()).
Trust note: the
prompt.*_template_file/output.template_filekeys can be set by a repo-level.gitl.yaml, not just your personal config — so runninggitl reviewagainst a cloned repository you don't control can point it at a template inside that same repository. This is the intended mechanism for a team's shared review policy, not a bug:text/templatehere can't read arbitrary files or execute code, but treat an untrusted repo's.gitl.yamlwith the same caution you'd give its.git/hooksor build scripts.
GitHub Action
gitl can be wired up as a GitHub Action: it AI-reviews a pull request's commits and
posts a comment with the risk score, optionally blocking merge above a threshold. The
Action builds gitl from source (go install at a pinned version). Also listed on the
GitHub Marketplace if you'd rather add it from there.
Add .github/workflows/gitl-review.yml to your repository:
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, see below
fail-on: high # optional: block merge on high riskSecurity best practices:
Key via
secrets.*only.gitl-api-keycomes fromsecrets.GITL_API_KEY(set under Settings → Secrets and variables → Actions), never hardcoded in YAML or committed. If the secret is not set, the Action runs in deterministic offline mode (no network, no cost).Minimal
permissions:. Onlypull-requests: write(posting the comment) andcontents: read(checkout) are needed — do not grant broader rights.fetch-depth: 0is required. GitHub providesbase/headSHAs in thepull_requestevent, but a shallow clone won't resolvebase.sha..head.sha.fail-ondefaults tonever. The Action only comments; it does not block merges unless you opt in explicitly (fail-on: high, etc.) — same "WARN by default, hard gate is explicit opt-in" principle as the CLI (--fail-on).Diff privacy. In CI, the diff is sent to whichever LLM provider is configured (default: OpenAI-compatible API). For private code, use a self-hosted/enterprise provider (Ollama, Azure OpenAI) — see Providers above.
Provider selection. By default the Action uses your config's provider (OpenAI-compatible if unset). To target a native provider, pass
provider:(openai|ollama|azure_openai|anthropic|gemini), and optionallymodel:andbase-url:, alongsidegitl-api-key:. All three are optional and, when omitted, fall through to your.gitl.yaml/personal config and gitl's built-in defaults — see Providers above. Example:provider: anthropicwith a Claude key insecrets.GITL_API_KEY.Secret masking. GitHub automatically masks
secrets.*values in runner logs as***, but that's not a reason to print the key in your own workflow steps.
PR description risk summary (opt-in)
With update-pr-description: true (default false), the Action additionally maintains
a compact risk-summary block at the end of the PR description — the risk line plus a
link back to the full review comment, updated on every run:
- uses: akomyagin/gitl@v0.6.2
with:
gitl-api-key: ${{ secrets.GITL_API_KEY }}
update-pr-description: trueIt's opt-in because editing the PR description is more intrusive than a sticky comment;
no extra permissions are needed — pull-requests: write, already required for the
comment, also covers the PR body. The block is delimited by the
<!-- gitl-review-summary --> marker pair, and only the text between the markers is
ever replaced — everything you write outside them is never touched. GitHub-only for
now (ignored on Gitea Actions).
Gitea Actions (experimental)
The same action.yml also runs on Gitea Actions —
Gitea's runner executes GitHub-style composite actions, and gitl's action detects the
platform at run time via the GITEA_ACTIONS=true variable that Gitea's act_runner
injects into every job. The only platform-specific part — posting the sticky PR
comment — then goes through Gitea's REST API
(POST/PATCH /api/v1/repos/{owner}/{repo}/issues/...) with curl instead of the
gh CLI, which only speaks GitHub's API. GitHub users are unaffected: without
GITEA_ACTIONS the action behaves exactly as before.
Add .gitea/workflows/gitl-review.yml to your repository (full commented example:
.gitea/workflows/gitl-review.yml in this repo):
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 modeRequirements: Actions enabled, a recent act_runner (node24-capable), and a runner
image providing bash, git, curl, jq and node. GITL_API_KEY goes into
Gitea's Actions secrets, never into the YAML — same BYOK rules as on GitHub.
Verification status — read before relying on this. The
curl-based REST calls (list comments, create, patch, sticky-detection) were run against a real Gitea instance (gitea/giteain Docker) end-to-end — list-empty → POST-create → re-list-finds-it → PATCH-update → still exactly one comment. That part works as written. What's not yet verified is the surroundingact_runnerCI context: whetherGITEA_ACTIONS/GITHUB_API_URL/the PR event payload look exactly as assumed inside a live workflow run (this was cross-checked against Gitea/ act_runner/act-fork source, not run inside an actual job). Treat the CI-triggering path as experimental until someone confirms a green run end-to-end inside real Gitea Actions; bug reports from real instances are very welcome.
GitLab CI (experimental)
gitl also ships a GitLab CI/CD component —
templates/gitl-review.yml — mirroring the GitHub Action:
it installs gitl with go install at a pinned version, reviews the merge request's
range ($CI_MERGE_REQUEST_DIFF_BASE_SHA..$CI_COMMIT_SHA), renders the comment through
the shared platform-neutral ci/comment.sh, and creates/updates a
sticky MR note through GitLab's REST API (same <!-- gitl-review --> marker as on
GitHub/Gitea). The job only runs in merge request pipelines.
The component is published in the GitLab CI/CD Catalog
through a release-time mirror of this repository at
gitlab.com/alkom68/gitl (one-way GitHub → GitLab,
pushed on every release tag). 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
# max_cost_usd: "0.50"
# gitl_version: "v0.6.2"On a self-hosted GitLab instance, include:component only resolves components
from the same instance — consume the template via include:remote straight from
GitHub instead (inputs work with remote includes):
# .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 (Settings → CI/CD → Variables, both masked, never in YAML):
GITL_API_KEY— the BYOK LLM key. Optional: without it gitl runs the deterministic offline review (no network, no cost). Defining the project variable is enough — it takes precedence over the component's emptygitl_api_keyinput default. If you use the input instead, pass a variable reference (gitl_api_key: $MY_LLM_KEY), never a literal key: input values are interpolated into the pipeline config.GITL_GITLAB_TOKEN— token for posting the MR note (project access token or PAT,apiscope, Reporter role or higher; sent asPRIVATE-TOKEN). If unset, the job falls back toCI_JOB_TOKEN(JOB-TOKENheader) — but on most GitLab configurationsCI_JOB_TOKENis not authorized for the Notes API, so the fallback is expected to fail (with an explicit error message, not a silent skip). An explicitGITL_GITLAB_TOKENis the reliable path.
A full commented self-test pipeline — also the closest thing to a complete usage
example — is .gitlab-ci-selftest.yml (runnable as
.gitlab-ci.yml in a GitLab mirror of this repo).
Verification status — read before relying on this. The GitLab REST calls (list MR notes + sticky-marker detection,
POSTcreate,PUTupdate) and the component YAML itself (spec:/inputs:interpolation,include:localwith inputs, via the CI Lint API) were verified end-to-end against a real local GitLab CE instance (gitlab/gitlab-ce19.2.0 in Docker) on a real merge request — list-empty → POST-create → re-list-finds-it → PUT-update → still exactly one note — using the exactcurl/jqcommands from the template. What's not yet verified is a live pipeline run: the values ofCI_MERGE_REQUEST_DIFF_BASE_SHA/CI_COMMIT_SHA/CI_JOB_URLinside a real merge-request pipeline are written from GitLab docs, not observed, and theCI_JOB_TOKEN-fallback rejection is documented per GitLab's job-token allowlist docs, not reproduced. Treat the pipeline path as experimental until someone confirms a green end-to-end run; bug reports welcome.
Trust note. The component downloads
ci/comment.shfrom the GitLab mirror (gitlab.com/alkom68/gitl) atgitl_versionand executes it — with no checksum/signature check, same trust boundary as thego install ...@${gitl_version}line right above it (same repo, same ref). This fetch happens regardless of how the component is included — Catalog orinclude:remote— because a component include ships only the YAML template, not the component repository's files, so the fetch can't be avoided mechanically. Downloading from the same GitLab instance that publishes the component (rather than from GitHub) keeps it same-namespace/same-ref, a more honest trust model than a cross-host fetch. If that matters for your threat model, pingitl_versionto a commit SHA rather than a tag (tags are movable).
Bitbucket Pipelines (experimental)
The Bitbucket integration ships as a Pipe —
and pipes are Docker images by definition, so unlike the GitHub/Gitea action and the
GitLab component (plain YAML wrappers) this one is a self-contained image:
bitbucket-pipe/Dockerfile builds a static gitl
binary and bakes in the shared ci/comment.sh renderer plus the
entrypoint bitbucket-pipe/pipe.sh. The pipe resolves the
PR range ($BITBUCKET_PR_DESTINATION_COMMIT..$BITBUCKET_COMMIT), runs
gitl review --format=json, and creates/updates a sticky PR comment through the
Bitbucket Cloud REST API (same <!-- gitl-review --> marker as on the other
platforms). Variables reference: bitbucket-pipe/pipe.yml.
Image status. Published on Docker Hub as
alkom68/gitl-review-pipesincev0.5.2— the release workflow'sdocker-publishjob pushes:<version>and:lateston every release tag. Only0.5.2and later exist in the registry: earlier releases predate the publication (the0.5.0/0.5.1tags were never pushed), so don't pin those.
# bitbucket-pipelines.yml
pipelines:
pull-requests:
'**':
- step:
name: gitl review
clone:
depth: full # the default depth-50 clone may not contain the PR base commit
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
# MAX_COST_USD: "0.50"Setup — two secured repository/workspace variables (Repository settings →
Pipelines → Repository variables; always referenced as $VAR, never literal values
in the YAML):
GITL_API_KEY— the BYOK LLM key. Optional: without it gitl runs the deterministic offline review (no network, no cost).GITL_BITBUCKET_TOKEN— credential for posting the PR comment: a repository/project/workspace access token with thepullrequest:writescope, sent asAuthorization: Bearer. Alternative: setGITL_BITBUCKET_USER+GITL_BITBUCKET_APP_PASSWORD(app password withpullrequest:write) for Basic auth instead. If neither is configured the pipe fails fast with an explicit message — before spending any LLM budget.
Supply-chain note (why this differs from the GitLab component). The pipe executes nothing fetched at run time: the
gitlbinary,ci/comment.shand the entrypoint are all built into the versioned image from one source tree. The GitLab component has to downloadci/comment.shover the network with no integrity check (see its trust note above); the pipe closes that gap by construction.
Verification status — read before relying on this. The image build and the full in-container flow were verified locally:
docker buildfrom this repo, thendocker runagainst a real test git repository with emulatedBITBUCKET_*variables — offline review → correct stickycomment.md→ comment create (POST), sticky update (PUT, still exactly one comment) and--fail-onexit-code propagation, exercised end-to-end against a local mock of the Bitbucket comments API; the fail-fast paths (missing credential/PR variables) and the fallback notice on a bad range were also exercised in the container. What's not yet verified: anything touching real Bitbucket infrastructure — the REST calls against api.bitbucket.org (shapes taken from Atlassian's API docs), the exact predefined variables inside a live PR pipeline (BITBUCKET_PR_DESTINATION_COMMITetc. are documented assumptions, not observed values), and how Pipelines mounts the clone into pipe containers. Treat the live pipeline path as experimental until someone confirms a green run on a real Bitbucket workspace; bug reports welcome.
Pre-commit hook (local)
gitl ships a pre-commit framework hook so that
gitl review --staged runs automatically before every commit — locally, offline,
and at zero cost by default.
Add to your repository's .pre-commit-config.yaml:
repos:
- repo: https://github.com/akomyagin/gitl
rev: v0.6.2 # pin to a released tag
hooks:
- id: gitl-reviewthen run pre-commit install. The framework builds the gitl binary itself
(language: golang) and caches the environment under ~/.cache/pre-commit/, so
the build cost is paid once, not on every commit.
To opt into a blocking hook with a cost cap:
hooks:
- id: gitl-review
args: [--fail-on=high, --max-cost-usd=0.05] # opt-in: block on high risk, cap costExport GITL_API_KEY in your environment for a real AI review; without it the
hook performs the deterministic offline review (no network, no cost).
Things to know:
Offline by default. No API key, no network, no per-commit cost. Set
GITL_API_KEYto opt into a real AI review.Non-blocking by default. The hook prints the review but does not fail the commit — same "WARN by default, hard gate is explicit opt-in" principle as the CLI/Action. Add
args: [--fail-on=high]to block.Latency. A real-API review takes a few seconds; keep it off the hot path by leaving it offline, or cap it with
--max-cost-usd.Diff privacy. With a real key the staged diff goes to your configured LLM provider — use a self-hosted/enterprise provider (Ollama, Azure OpenAI) for private code, see Providers above.
Without the pre-commit framework
A plain git hook works too:
# .git/hooks/pre-commit (chmod +x)
#!/usr/bin/env bash
set -euo pipefail
# Offline, non-blocking review of staged changes (WARN by default).
gitl review --staged || true
# To block the commit on high risk instead, replace the line above with:
# gitl review --staged --fail-on=highMCP server
gitl mcp runs gitl as a Model Context Protocol stdio
server — a separate, additional channel from the CLI/CI usage above, for using gitl
interactively inside an agent session (Claude Desktop, Cursor, Windsurf, etc.) instead of
shelling out. It exposes two tools:
gitl_review— same review 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 (no md/text rendering, no streaming — a tool result is atomic).risk.levelis returned as data; there is no--fail-onin MCP mode since there is 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, if configured) — it never walks arbitrary paths on its own initiative. An explicitreposargument is honored as-is (the calling agent already has filesystem access through its own tools; this isn't an access-control boundary, just a "don't surprise the user" default).
Add to your MCP client config (Claude Desktop, Cursor, etc.):
{
"mcpServers": {
"gitl": {
"command": "gitl",
"args": ["mcp"]
}
}
}Config is loaded once at startup the same way as the plain commands (.gitl.yaml +
personal config + GITL_* env, from the directory gitl mcp is launched in). Without a
key, tool calls run in the same deterministic offline mode as the CLI. stdout is
reserved for the MCP protocol — nothing human-readable is ever written there; warnings go
to stderr.
License
MIT.
This server cannot be installed
Maintenance
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/akomyagin/gitl'
If you have feedback or need assistance with the MCP directory API, please join our Discord server