AI-assisted code review tool for GitHub pull requests.
Project description
🛡️ GitOwl
AI-assisted code review for GitHub pull requests.
GitOwl combines a traditional static analyser (Semgrep) with an AI reasoning layer that contextualises findings, filters false positives, flags risky changes, and scores overall PR risk — so human reviewers focus on what matters.
PR opened → GitHub Action → Semgrep scans diff → AI filters + reasons
→ risk score (Low/Medium/High) → structured PR comment
License: MIT · Public, solo-maintained, open to contributions. See CONTRIBUTING.md and PROJECT_BRAIN.md.
Add GitOwl to your repo (3 steps)
Get automated AI review comments on every pull request:
- Copy the workflow — save
examples/gitowl-review.ymlinto your repo at.github/workflows/gitowl-review.yml. It runspip install gitowl. - Add your key — in your repo, go to Settings → Secrets and variables → Actions
and add a secret
AI_API_KEY(your OpenRouter or OpenAI key). - Open a PR — GitOwl reviews the diff and posts a comment automatically.
Defaults to OpenRouter + openai/gpt-4o-mini. Override with repo variables
AI_PROVIDER / AI_MODEL / AI_BASE_URL. Want static analysis too? Use
pip install "gitowl[semgrep]" in the workflow.
Quick start (local)
python -m venv .venv
.venv\Scripts\activate # Windows (macOS/Linux: source .venv/bin/activate)
pip install -r requirements.txt
pip install -r requirements-dev.txt
cp .env.example .env # then edit .env with your provider + key
Review a diff locally
# From a saved diff
python -m gitowl.cli review-diff my.diff
# From git, piped in
git diff main...HEAD | python -m gitowl.cli review-diff -
# Skip static analysis (AI only)
python -m gitowl.cli review-diff my.diff --no-semgrep
Review a GitHub PR
# Print the review
python -m gitowl.cli review-pr MarutiDubey/GitOwl 42
# Post/update the review as a PR comment
python -m gitowl.cli review-pr MarutiDubey/GitOwl 42 --post
# ...and post committable fixes as inline suggestions (GitHub's one-click
# "Commit suggestion" button). Only findings whose fix lands on a changed
# line are posted; the rest stay in the summary comment.
python -m gitowl.cli review-pr MarutiDubey/GitOwl 42 --post --suggest
Describe a PR (auto-generated description)
Generate a clear PR description (title + summary + change list) from a diff:
# From a diff (file or stdin) — prints the description
python -m gitowl.cli describe-diff my.diff
git diff main...HEAD | python -m gitowl.cli describe-diff -
# From a GitHub PR — print it
python -m gitowl.cli describe-pr MarutiDubey/GitOwl 42
# ...or write it into the PR body (between GitOwl markers, preserving your text)
python -m gitowl.cli describe-pr MarutiDubey/GitOwl 42 --post
--post only replaces GitOwl's own marked section, so any description you
wrote by hand stays intact.
List AI providers
python -m gitowl.cli providers
AI providers
GitOwl is provider-agnostic. Set AI_PROVIDER in .env:
AI_PROVIDER |
Notes |
|---|---|
openrouter |
Default. One key, many models. Set AI_API_KEY. |
openai |
Direct OpenAI API. Set AI_API_KEY. |
ollama |
Local, free, offline. Run ollama serve first — no key needed. |
See gitowl/ai_client/README.md to add a provider.
⚠️ Never commit your
.envor API keys..envis git-ignored by default.
Configuration (.gitowl.toml)
Drop a .gitowl.toml file at your repo root to set project-wide review policy.
It's committed to the repo, so the whole team shares the same rules.
[review]
# Hide findings below this level. One of: info | warning | error.
# Default "info" reports everything.
min_severity = "warning"
# Glob patterns for files GitOwl should not report findings on.
# Matches nested paths (e.g. "*.md" also matches "docs/x.md").
ignore_paths = ["tests/**", "**/*.md", "vendor/**"]
[ai]
# Optional: pin the model for this repo (env AI_MODEL still overrides).
model = "openai/gpt-4o-mini"
[pricing]
# Optional: override or add model prices, as [input, output] USD per 1M tokens.
# GitOwl ships prices for common models; add your own for anything else.
"my-org/private-model" = [2.0, 8.0]
Precedence (lowest to highest): built-in defaults → .gitowl.toml →
environment variables. So the repo file sets the baseline, and a CI secret or a
shell export can always override it for a single run. API keys are never read
from this file — they stay in .env / environment only.
Everything is optional; with no file present, GitOwl behaves exactly as before (reports every finding, ignores nothing).
Cost & latency
Each review call's token usage, estimated cost, and latency are logged and shown in a small footer line on the PR comment, e.g.:
Generated by GitOwl · openai/gpt-4o-mini · 1,560 tok · ~$0.0004 · 1,834ms
Cost is estimated from the built-in price table plus any [pricing] overrides;
an unpriced model shows cost unknown rather than a wrong number.
Fix suggestions
When the model is confident of a concrete fix, it includes drop-in replacement
code, rendered under the finding as a suggestion block:
- 🔴 Weak password hash (
auth.py:12, ai) MD5 is broken; use SHA-256.```suggestion return hashlib.sha256(pw.encode()).hexdigest() ```
The code is kept verbatim so you can copy it straight in. (Posting these as inline, one-click-committable review comments is a planned follow-up.)
GitHub Action
.github/workflows/gitowl-review.yml runs GitOwl on every PR
(opened / synchronize / reopened) and posts a review comment.
Configure in your repo settings:
- Secret
AI_API_KEY— your provider key. - Variables (optional)
AI_PROVIDER,AI_MODEL,AI_BASE_URL— default to OpenRouter +gpt-4o-mini.
GITHUB_TOKEN is provided automatically by Actions.
Eval harness
Measure GitOwl's review quality against a corpus of diffs with known seeded bugs — precision / recall / F1. Runs offline and deterministically by default.
python -m gitowl.eval # mock provider (offline, no key)
python -m gitowl.eval --live # score the real provider from .env
python -m gitowl.eval --json # machine-readable output
python -m gitowl.eval --fail-under 0.9 # exit non-zero if aggregate F1 < 0.9
The corpus ships 12 cases (weak hash, eval, hardcoded secret, SQL injection
incl. f-string, unsafe pickle, a multi-bug diff, plus benign/clean cases and a
few categories the offline mock intentionally can't catch). The mock baseline is
precision 1.00 / recall 0.73 / F1 0.84 — deliberately below 1.0 so the
scoring math is genuinely exercised (a real provider via --live should do
better). Cases live in gitowl/eval/cases/ as
<name>.diff + <name>.expected.json pairs — add your own to grow the corpus.
Development
pytest --cov=gitowl # tests
ruff check gitowl tests # lint
black . && isort . # format
mypy gitowl # types
pre-commit run --all-files # all hooks
Roadmap and phases live in CONTRIBUTING.md §1.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gitowl-0.1.0.tar.gz.
File metadata
- Download URL: gitowl-0.1.0.tar.gz
- Upload date:
- Size: 55.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
462458b16e3a3a438c9b32fed5452a3c87f2811700860068ac6060efe409900d
|
|
| MD5 |
e909f5266688a4265526f4186834dfd3
|
|
| BLAKE2b-256 |
54dfad12e4d74fb6d685ff256d623078fddffa1c611ae4142c9d1dae63616acb
|
Provenance
The following attestation bundles were made for gitowl-0.1.0.tar.gz:
Publisher:
publish.yml on MarutiDubey/GitOwl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gitowl-0.1.0.tar.gz -
Subject digest:
462458b16e3a3a438c9b32fed5452a3c87f2811700860068ac6060efe409900d - Sigstore transparency entry: 2076365220
- Sigstore integration time:
-
Permalink:
MarutiDubey/GitOwl@20c27d31ccfc366918e31e517bff2815b2a35623 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/MarutiDubey
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20c27d31ccfc366918e31e517bff2815b2a35623 -
Trigger Event:
release
-
Statement type:
File details
Details for the file gitowl-0.1.0-py3-none-any.whl.
File metadata
- Download URL: gitowl-0.1.0-py3-none-any.whl
- Upload date:
- Size: 51.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b59ef6c77d16f593fb8373424fbe94f5b732ad0b49c08de7bf3c3537311f5537
|
|
| MD5 |
bc84e7e4355f4099f268051929a750a6
|
|
| BLAKE2b-256 |
864ad9ebcc20cd7c15f182c86ca8e491558d984f42352a03c22dca4b6678efa2
|
Provenance
The following attestation bundles were made for gitowl-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on MarutiDubey/GitOwl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gitowl-0.1.0-py3-none-any.whl -
Subject digest:
b59ef6c77d16f593fb8373424fbe94f5b732ad0b49c08de7bf3c3537311f5537 - Sigstore transparency entry: 2076365447
- Sigstore integration time:
-
Permalink:
MarutiDubey/GitOwl@20c27d31ccfc366918e31e517bff2815b2a35623 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/MarutiDubey
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@20c27d31ccfc366918e31e517bff2815b2a35623 -
Trigger Event:
release
-
Statement type: