Metadata-Version: 2.5
Name: argus-reviewer
Version: 0.1.0
Summary: Model-agnostic AI code review agent for Bitbucket pull requests, wired into Jenkins
Project-URL: Homepage, https://github.com/akashtomy/argus
Project-URL: Repository, https://github.com/akashtomy/argus
Project-URL: Issues, https://github.com/akashtomy/argus/issues
Author-email: Akash Thomas <akash.thomas@arcone.com>
License: MIT
License-File: LICENSE
Keywords: bitbucket,ci-cd,code-review,jenkins,litellm,llm
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: jinja2>=3.1
Requires-Dist: litellm>=1.44
Requires-Dist: pydantic>=2.6
Requires-Dist: pyyaml>=6.0
Requires-Dist: requests>=2.31
Requires-Dist: rich>=13.7
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# Argus

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)

**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](https://docs.litellm.ai/docs/providers)
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](#whats-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](https://docs.astral.sh/uv/).

```bash
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](https://support.atlassian.com/bitbucket-cloud/docs/api-tokens/) or
  repository/workspace access token; for **Bitbucket Server/Data Center**, a
  personal access token. Either way: `Repository: read` + `Pull requests: read+write`
  scope (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`), leave `LITELLM_API_BASE` unset.
  - **Through a self-hosted LiteLLM proxy**: set `LITELLM_API_BASE` (the proxy's URL) and
    `LITELLM_API_KEY` (the proxy's virtual key) instead — no provider keys needed locally,
    the proxy holds those. `--model` then becomes **exactly** whatever model name is
    registered on the proxy, byte for byte — check with
    `curl -s "$LITELLM_API_BASE/v1/models" -H "Authorization: Bearer $LITELLM_API_KEY"`
    if you're not sure. Some proxies register aliases that themselves look like
    `openai/gpt-4.1-mini` (i.e. the alias contains a slash); Argus posts straight to the
    proxy's `/v1/chat/completions` in this mode rather than going through LiteLLM's own
    SDK, specifically so a prefix like `openai/` in your alias doesn't get parsed as a
    LiteLLM provider marker and stripped off before the proxy ever sees it.

Then `source .env` (or use `direnv`/your shell's env loading of choice) before running.

## Usage

```bash
# 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 to `anthropic/claude-sonnet-5`.
- `--api-base` / `--api-key` (or `LITELLM_API_BASE` / `LITELLM_API_KEY`) — point at a
  self-hosted LiteLLM proxy instead of calling the provider in `--model` directly.
- `--standards` (or `ARGUS_STANDARDS`) — a local path or URL for your org's global
  standards, replacing the bundled default. See [Rule hierarchy](#rule-hierarchy-global-standards--repo--module-rules).
- `--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
`FileDiff`s and a `repo_rules_yaml` string, invoke the command, and check the
output/exit code.

### What it does today

1. Auto-detects Bitbucket Cloud vs. Server/Data Center from the PR URL.
2. Fetches the PR's diff (Cloud: unified text; Server: structured JSON — both
   normalized to the same shape internally).
3. Fetches `.argus/rules.yaml` from the PR's target branch, if present, and merges
   it with the global standards baseline — see below.
4. 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`).
5. 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.

```yaml
# .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`](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/`.env` detection is always on.** Because `security` is locked in the
  bundled global standards, every repo already gets a deterministic, regex-based
  scan (AWS keys, GitHub/Slack tokens, private key blocks, hardcoded
  `api_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:

```yaml
# .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`](jenkins/README.md)
for one-time platform setup and [`jenkins/examples/Jenkinsfile`](jenkins/examples/Jenkinsfile)
for how an application repo adds one stage to its existing pipeline:

```groovy
@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

[MIT](LICENSE)
