Offline bug-risk prediction for code repositories. Static analysis + git intelligence + local ML. No cloud, no API keys required.
Project description
๐ BugSeer
Offline bug-risk prediction for code repositories. Static analysis + git intelligence + a local ML model โ and an explanation for every number it shows you.
No cloud. No API keys. No telemetry. Your source code never leaves your machine.
๐ฅ 97 โโโโโโโโโโโโโโโโโโโโ src/flask/app.py Learned model predicts elevated defect risk ยท Mutable global state
๐ด 74 โโโโโโโโโโโโโโโโโโโโ src/payment.py High bug-fix density ยท No error handling ยท Deeply nested control flow
๐ก 41 โโโโโโโโโโโโโโโโโโโโ src/config.py Frequently modified file
๐ข 12 โโโโโโโโโโโโโโโโโโโโ src/utils.py โ
Why this exists
Most "AI code review" tools give you a number and expect you to trust it. Developers don't, and they're right not to.
BugSeer's rule is simple: every point of risk traces back to named, inspectable evidence.
๐ฅ src/payment.py 96/100 (critical)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
static 102 ยท git 70 ยท raw 172 pts
โ +40 High bug-fix density [bugfix-density]
9 of 10 commits (90%) look like bug fixes. Code that has needed
repeated repair tends to need more.
โ +27 No error handling around risky operations [no-error-handling]
4 I/O, network, parsing or subprocess call(s) and zero try/catch
blocks. Failures here surface as unhandled exceptions in production.
โ +25 Deeply nested control flow [deep-nesting]
Maximum loop nesting is 3 and maximum block depth is 7 (threshold 3).
โณ L12 (depth 7), L28 (depth 7), L11 (depth 6)
โ +14 Previously reverted [revert-history]
1 revert/rollback commit(s) touched this file. A revert is direct
evidence that a change here broke something in production.
You can argue with that. That's the point.
Install
pip install -e ".[all]" # everything
pip install -e . # core only โ still fully functional
| Extra | Adds | Without it |
|---|---|---|
parsers |
tree-sitter for 19+ languages | Python via stdlib ast, others via heuristics |
server |
FastAPI dashboard | Use the self-contained HTML report |
ml |
XGBoost / LightGBM | scikit-learn gradient boosting (built in) |
env |
.env file loading |
Plain environment variables |
Every extra is optional. Core BugSeer needs no network access at any point.
Quick start
bugseer scan . # rank files by risk
bugseer explain src/payment.py # full reasoning for one file
bugseer heatmap . # colour-coded project tree
bugseer impact src/database.py # "what if I change this?"
bugseer train . # learn from your own bug history
bugseer serve . # interactive dashboard
bugseer report . -o risk.html # shareable offline HTML
The five phases
Phase 1 โ Rule-based static analysis (no AI, no API)
Parsed with the best available backend: stdlib ast for Python (exact), tree-sitter for 19+ other languages, and a heuristic analyzer that never fails so an unknown language still gets scored.
| Rule | Points | Fires when |
|---|---|---|
no-error-handling |
20 | Risky I/O with no try/catch |
low-coverage |
20 | Measured coverage below threshold |
deep-nesting |
15 | Nested loops deeper than 3 |
global-state |
15 | Mutable module-level state |
high-complexity |
12 | Cyclomatic complexity > 20 |
long-function |
10 | Function longer than 100 lines |
high-branching |
10 | More than 15 conditional branches |
duplicate-code |
10 | Cloned blocks within or across files |
swallowed-exception |
8 | except: / catch(e){} that hides failures |
god-file |
8 | Over 600 lines |
| โฆplus magic numbers, TODO debt, mutable defaults, long parameter lists |
Phase 2 โ Git intelligence
Parses git log locally. No API, no network.
- Change frequency โ the strongest empirical defect predictor
- Bug-fix density โ how many commits here were repairs
- Revert history โ direct evidence a change broke production
- Fix-follow rate โ how often an edit here needed a follow-up fix within 7 days
- Authorship spread and single-owner bus-factor risk
- Co-change coupling โ files habitually committed together
Works on partial/blobless clones (
--filter=blob:none), wheregit log --numstatwould otherwise stall fetching blobs. BugSeer detects this, falls back to--name-only, and tells you churn is unavailable rather than silently reporting "no git history."
Phase 3 โ Learn from past bugs
bugseer train . --label-window 180
Trains locally on your repository. Labels come from bug-fix commits in a recent window; features come from history before that window โ a temporal split, so the model is genuinely predictive rather than circular.
โ Model trained
Estimator sklearn.GradientBoostingClassifier
Samples 80 files (17 bug-fixed)
ROC AUC (5-fold) 0.7652
Label window 1095 days
What the model learned to look at:
largest parameter list โโโโโโโโโ 24.5%
comment ratio โโโโโ 14.8%
ownership concentration โโโโ 10.0%
Honesty guarantees, because a prediction you can't trust is worse than none:
- Reported metrics are out-of-fold (5-fold CV), never training-set scores.
- Files in the training set get their cross-validated probability, not the memorised ~100% a fitted ensemble would return.
- Probabilities are clamped to 2โ95%: a few dozen samples cannot justify certainty.
- Too little signal? It says so and keeps using rules, rather than fitting noise.
Phase 4 โ Project heat map
bugseer heatmap in the terminal, or bugseer serve for the React dashboard. Click any file for its full evidence chain.
Phase 5 โ "What if?" simulator
$ bugseer impact src/flask/app.py
If you modify src/flask/app.py, 25 file(s) are most likely to be affected.
Impact File Own risk Why
65 โโโโโโโโ src/flask/sessions.py 66 directly imports `app.py`;
changed together in 22% of commits
52 โโโโโโโโ tests/test_basic.py 93 depends on `globals.py` transitively
(2 hops); is itself high-risk
Combines the import graph with historical co-change, weighted by each candidate's own fragility โ and explains every prediction.
Configuration
bugseer init # writes a starter .bugseer.toml
[bugseer]
history_days = 730
exclude = ["docs/*", "examples/*"]
# coverage_file = "coverage.xml"
[bugseer.weights]
no_error_handling = 20
change_frequency = 15
[bugseer.thresholds]
long_function_lines = 100
band_critical = 85
Precedence: CLI flags โ .bugseer.toml โ environment/.env โ defaults.
Test coverage
BugSeer auto-detects coverage.xml, lcov.info, coverage.json, and Clover. Without one it falls back to a filename heuristic โ and labels it as such, so a guess is never mistaken for a measurement.
CI usage
- run: pip install -e ".[parsers]"
- run: bugseer scan . --fail-over 85 --ignore-tests
Exits non-zero when any file exceeds the threshold. Add --json report.json to archive results.
The optional AI narrator
BugSeer needs no API key. All five phases above are deterministic and offline.
The only optional AI feature rewrites already-computed evidence as prose:
cp .env.example .env # then set one key
bugseer explain src/payment.py --narrate
Supports OpenAI, Anthropic (Claude), Gemini, and Ollama (local).
Safety properties, enforced by tests:
- It cannot change a score โ it receives finished evidence, and the score is computed before it runs.
- It sends metrics and rule names only; source code requires explicit
BUGSEER_AI_SEND_SOURCE=1. BUGSEER_OFFLINE=1blocks all outbound requests even if a key is set.BUGSEER_AI_REDACT_PATHS=1hashes file paths before transmission.- Any failure degrades gracefully to the deterministic explanation.
See .env.example โ every variable in it is optional.
Dashboard
bugseer serve . # http://127.0.0.1:8420
React + TypeScript, bound to localhost. The build is committed, so it runs without a node toolchain. If the bundle is missing, the server falls back to the static HTML report.
To develop the frontend:
cd frontend && npm install && npm run dev # proxies /api to :8420
Architecture
bugseer/
โโโ analysis/
โ โโโ static.py Phase 1 โ ast | tree-sitter | heuristic backends
โ โโโ langspec.py Declarative grammar vocabularies (19+ languages)
โ โโโ duplication.py Rolling-hash clone detection
โโโ git_intel.py Phase 2 โ git log parsing, coverage reports
โโโ rules.py Scoring engine โ every hit carries its evidence
โโโ ml.py Phase 3 โ local model, out-of-fold honesty
โโโ graph.py Phase 5 โ dependency graph + impact simulation
โโโ scanner.py Orchestration (parallel, with serial fallback)
โโโ server.py Phase 4 โ FastAPI dashboard
โโโ report.py Self-contained HTML output
โโโ narrate.py Optional AI narrator (stdlib urllib only)
โโโ cli.py Typer CLI
Adding a language is a few lines of data in langspec.py.
Adding a rule is one function in rules.py returning a RuleHit with its evidence.
Testing
pytest -q # 104 tests
Covers the analyzers, rule scoring, git parsing, coverage formats, the graph, impact simulation, ML degradation paths, the CLI, and the narrator's privacy guarantees. Verified end-to-end against a real repository (Flask: 84 files, 5,539 commits) and a battery of pathological inputs โ binary files, invalid UTF-8, syntax errors, 800 KB files, broken symlinks, empty files.
Performance
Flask (84 files, 13k LOC, 3,812 commits analysed): ~1.0s full scan on 8 workers.
Design decisions worth knowing
- Additive scores, saturating curve. Raw points are summed then squashed to 0โ100, so eight moderate problems and one catastrophic one both land sensibly.
- Rules over ML by default. The model is opt-in and additive; it only moves a score when it genuinely disagrees with the repo baseline.
- Naming heuristics are labelled. "No matching test file" never masquerades as measured coverage.
- Trivial files score zero. Empty files and constants-only modules generate no findings โ noise is how a tool gets ignored.
- Graceful degradation everywhere. No git, no tree-sitter, no model, no network: BugSeer still produces a useful report and tells you what it couldn't do.
License
MIT
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 bugseer-0.1.0.tar.gz.
File metadata
- Download URL: bugseer-0.1.0.tar.gz
- Upload date:
- Size: 142.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f02bbb36f9cc889ee3d57fb8619c52f8acf94f123884be397640c50a5baafd3
|
|
| MD5 |
7602daf54bd03c21940058e9aeff47a6
|
|
| BLAKE2b-256 |
3bd0c6b3ad663be4b2c8f8be6b2be64fde0bd35b55c42de469d49c7387eaa5f2
|
Provenance
The following attestation bundles were made for bugseer-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on rishibanota/Bugseer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bugseer-0.1.0.tar.gz -
Subject digest:
9f02bbb36f9cc889ee3d57fb8619c52f8acf94f123884be397640c50a5baafd3 - Sigstore transparency entry: 2334092776
- Sigstore integration time:
-
Permalink:
rishibanota/Bugseer@853e4e34ee0e0d4f2135a2334c533bdf255cbf1b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/rishibanota
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@853e4e34ee0e0d4f2135a2334c533bdf255cbf1b -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file bugseer-0.1.0-py3-none-any.whl.
File metadata
- Download URL: bugseer-0.1.0-py3-none-any.whl
- Upload date:
- Size: 135.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
901794122fe67d0af5136a5f009c037ce3bce5b1a243f2ae00108ddface698ac
|
|
| MD5 |
276f2421d43b357d9c79ec801421eca5
|
|
| BLAKE2b-256 |
9387d48a5768d62696de424ffa8429485bcfbe01bb0c8851deb3aa35a202159c
|
Provenance
The following attestation bundles were made for bugseer-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on rishibanota/Bugseer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bugseer-0.1.0-py3-none-any.whl -
Subject digest:
901794122fe67d0af5136a5f009c037ce3bce5b1a243f2ae00108ddface698ac - Sigstore transparency entry: 2334092785
- Sigstore integration time:
-
Permalink:
rishibanota/Bugseer@853e4e34ee0e0d4f2135a2334c533bdf255cbf1b -
Branch / Tag:
refs/heads/main - Owner: https://github.com/rishibanota
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@853e4e34ee0e0d4f2135a2334c533bdf255cbf1b -
Trigger Event:
workflow_dispatch
-
Statement type: