Argus
Model-agnostic AI code review for Bitbucket pull requests, wired into the Jenkins pipeline you already run.
Give it a PR URL and a Bitbucket token; it fetches the diff, reviews it file by file with an LLM — any provider LiteLLM supports, or your own self-hosted LiteLLM proxy/gateway — and prints, or posts, the findings as PR comments. Runs standalone from the CLI, or as a stage in an existing Jenkinsfile via a Jenkins shared library.
- Bitbucket Cloud and Server/Data Center, auto-detected from the PR URL
- Any LLM provider — OpenAI, Anthropic, Bedrock, self-hosted OSS models via Ollama/vLLM, or a self-hosted LiteLLM proxy — via one abstraction, LiteLLM
- Jenkins-native — a shared-library step (
argusReview()) wraps the CLI in a Docker image, so application repos need nothing but Docker and three credentials - Global standards + per-repo, per-module rules — an org-wide baseline
merges with each repo's own
.argus/rules.yaml, with path-scoped overrides for monorepos where one module's standards differ from another's - No vendor lock-in, no SaaS dependency — self-hostable end to end, including the model if you use a local one
Status
Early and evolving. Today: one default review prompt (no per-language packs yet), plus a working global/repo rule hierarchy — see What's next. Contributions welcome; the codebase is small on purpose so it's easy to read before you extend it.
Quickstart
Built and run with uv.
git clone https://github.com/<your-username>/argus.git
cd argus
uv sync --extra dev # creates .venv, installs the package + test deps
Copy .env.example to .env and fill in:
ARGUS_BITBUCKET_TOKEN— for Bitbucket Cloud, an API token or repository/workspace access token; for Bitbucket Server/Data Center, a personal access token. Either way:Repository: read+Pull requests: read+writescope (write only needed for--post-comments).ARGUS_BITBUCKET_EMAIL— Bitbucket Cloud only, and only if the token above is an Atlassian API token rather than a repository/workspace access token. The two token types authenticate differently (Basic vs. Bearer); set this to your Atlassian account email to switch Argus onto Basic auth. Leave unset for access tokens, OAuth tokens, or Bitbucket Server.- Model access — pick one:
- Direct to a provider: set
ANTHROPIC_API_KEY/OPENAI_API_KEY/ etc. (whichever matches--model), leaveLITELLM_API_BASEunset. - Through a self-hosted LiteLLM proxy: set
LITELLM_API_BASE(the proxy's URL) andLITELLM_API_KEY(the proxy's virtual key) instead — no provider keys needed locally, the proxy holds those.--modelthen becomes exactly whatever model name is registered on the proxy, byte for byte — check withcurl -s "$LITELLM_API_BASE/v1/models" -H "Authorization: Bearer $LITELLM_API_KEY"if you're not sure. Some proxies register aliases that themselves look likeopenai/gpt-4.1-mini(i.e. the alias contains a slash); Argus posts straight to the proxy's/v1/chat/completionsin this mode rather than going through LiteLLM's own SDK, specifically so a prefix likeopenai/in your alias doesn't get parsed as a LiteLLM provider marker and stripped off before the proxy ever sees it.
- Direct to a provider: set
Then source .env (or use direnv/your shell's env loading of choice) before running.
Usage
# Bitbucket Cloud
uv run argus review --pr https://bitbucket.org/<workspace>/<repo>/pull-requests/10
# Bitbucket Server / Data Center
uv run argus review --pr https://bitbucket.example.com/projects/PROJ/repos/my-repo/pull-requests/123
Options:
--model— any LiteLLM model string, e.g.anthropic/claude-sonnet-5,openai/gpt-5-mini,bedrock/anthropic.claude-sonnet-5,ollama/qwen2.5-coder:32b. Defaults toanthropic/claude-sonnet-5.--api-base/--api-key(orLITELLM_API_BASE/LITELLM_API_KEY) — point at a self-hosted LiteLLM proxy instead of calling the provider in--modeldirectly.--standards(orARGUS_STANDARDS) — a local path or URL for your org's global standards, replacing the bundled default. See Rule hierarchy.--post-comments— also post inline + summary comments back to the PR (default: off, console-only report).--fail-on high|medium|low|never— force a severity gate for this run, overriding whatever the merged rules would otherwise use. Omit to let the rules decide (nothing fails by default, including on the bundled standards alone).--insecure— skip TLS verification for self-signed Bitbucket Server certs.
Run uv run pytest -q to run the test suite.
Trying out a .argus/rules.yaml idea without a real PR or LLM calls
tests/test_cli_integration.py mocks the two network boundaries (load_pr,
review_file) and drives the real argus review command through Click's
CliRunner — no Bitbucket access, no model calls, but it exercises the actual
rule-merging, secret-scanning, and policy logic. Useful as a template for
trying out a new rules idea locally: build a LoadedPR with synthetic
FileDiffs and a repo_rules_yaml string, invoke the command, and check the
output/exit code.
What it does today
- Auto-detects Bitbucket Cloud vs. Server/Data Center from the PR URL.
- Fetches the PR's diff (Cloud: unified text; Server: structured JSON — both normalized to the same shape internally).
- Fetches
.argus/rules.yamlfrom the PR's target branch, if present, and merges it with the global standards baseline — see below. - Reviews each changed file independently, filtered to the categories the merged
rules say apply to that file, against a single default prompt
(
src/argus/prompts/default.jinja2). - Prints a findings table; optionally posts comments back to the PR; exits non-zero if any finding crosses its file's effective severity gate.
Rule hierarchy: global standards + repo & module rules
What Argus checks for is a merge, not a fallback. An org-wide global standards
baseline (bundled default, or your own via --standards) can mark categories
locked: true — those apply everywhere and can't be turned off by a repo. Each
repo's own .argus/rules.yaml, read from the PR's target branch, can add
categories, adjust the severity gate, and layer path-scoped overrides for
modules that need different treatment — a monorepo's frontend/ and legacy/
directories don't have to be reviewed the same way.
# .argus/rules.yaml, at the root of the repo being reviewed
severity_gate: high # fail the build at/above this, repo-wide
categories:
add: [accessibility] # on top of whatever the global baseline includes
remove: [style] # ignored for any category the global baseline locked
overrides:
- paths: ["frontend/*"] # glob; `*` matches across `/` too
categories:
add: [accessibility]
severity_gate: medium # stricter than the repo default, for this module only
- paths: ["legacy/*"]
severity_gate: low # more lenient here — findings still shown, just don't fail the build
No .argus/rules.yaml? The repo just inherits the global baseline untouched. The
bundled default ships with security and correctness locked and no severity
gate set (report-only) — see src/argus/standards/global.yaml.
Two checks that aren't LLM judgment calls
Some things shouldn't depend on a model noticing them. Two mechanisms sit alongside the per-file LLM review for exactly that:
-
Secret/
.envdetection is always on. Becausesecurityis locked in the bundled global standards, every repo already gets a deterministic, regex-based scan (AWS keys, GitHub/Slack tokens, private key blocks, hardcodedapi_key = "..."-shaped assignments, and any.env-shaped file being added) on top of whatever the LLM finds — no config needed. A module can't opt out of this without a global standards change, since it rides on a locked category. -
policies:are structural, PR-level checks — not per-file. "If this module changed, some other file must have changed too" — evaluated once against every file touched in the PR, independent of the LLM.
Putting both together for a monorepo where module-a just needs the default secret scanning (already covered, no config) and module-b should never ship a feature without a README update:
# .argus/rules.yaml
overrides:
# module-a needs nothing here — .env/API-key detection already applies
# everywhere via the locked `security` category above.
- paths: ["module-b/**"]
policies:
- require: "module-b/README.md"
message: "module-b changed but its README wasn't updated."
severity: medium
If a PR touches anything under module-b/ but doesn't also touch
module-b/README.md, Argus adds a PR-level finding for it — posted as a
summary comment (there's no single line it's "about"), and it can fail the
build the same way a file-level finding does, using this override's
severity_gate (falling back to the repo's, then the global one, if unset).
Jenkins integration
The CLI is wrapped in a Jenkins shared-library step (argusReview()), packaged
as a Docker image so application repos don't need Python/uv on the agent —
just Docker and three credentials. See jenkins/README.md
for one-time platform setup and jenkins/examples/Jenkinsfile
for how an application repo adds one stage to its existing pipeline:
@Library('argus-shared-library') _
pipeline {
agent any
stages {
stage('Checkout') { steps { checkout scm } }
stage('AI Code Review') {
when { changeRequest() } // only run against pull requests
steps {
argusReview(
image: 'registry.internal/argus-review:0.1.0',
model: 'openai/gpt-4.1-mini',
)
}
}
// ... your existing build/test stages, unchanged ...
}
}
Contributing
Issues and PRs welcome. The codebase is deliberately small and split by
concern — an SCM connector (bitbucket_cloud.py / bitbucket_server.py) is
about 80 lines and a reasonable template for adding GitHub/GitLab; a review
pack is just a prompt template and a rules file. Run uv run pytest -q
before opening a PR.
What's next
- Per-language review packs (Python, Java, Angular, React) instead of one generic prompt.
- Roll out to a handful of pilot repos to gauge signal vs. noise on the global
baseline before asking teams to write their own
.argus/rules.yaml. - A versioned, centrally-owned global standards repo instead of a single
bundled/
--standards-pointed file, so a platform team can update the baseline without touching Argus itself.
License
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 argus_reviewer-0.1.0.tar.gz.
File metadata
- Download URL: argus_reviewer-0.1.0.tar.gz
- Upload date:
- Size: 200.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.7.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3cdc310c201dea7d0aa05d111976bfd8c5d9634adee79c723c1b2e84c71bc579
|
|
| MD5 |
acc0d77d147fa6ccb541bab6d043701e
|
|
| BLAKE2b-256 |
8ab87dae708eba1e0d803a082d6a683833fdac90395fc9dfb6a2fd6829cabff1
|
File details
Details for the file argus_reviewer-0.1.0-py3-none-any.whl.
File metadata
- Download URL: argus_reviewer-0.1.0-py3-none-any.whl
- Upload date:
- Size: 24.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.7.20
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72f010d7b618654c269b58ba0f4242ce51581a75ea037d0357fd155e9e4da744
|
|
| MD5 |
b8b52f40aa5ed4f8c63b09b16ee4e93c
|
|
| BLAKE2b-256 |
ce5415e2d3b06c1c4fd6114fd6a59642c978f1d6c7663b083a8ea375c90db7cc
|