🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

culprit

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

culprit

Root-cause analysis for a PR or branch: classify feature vs bugfix, find the introducing commit (suspect set) or the blast radius.

pipPyPI
Version
0.4.0
Weekly downloads
143
286.49%
Maintainers
1
Weekly downloads
 

culprit

CI PyPI Python versions License: MIT

Root-cause analysis for a pull request or branch. Read-only — never modifies your repo or PR. Runs as a CLI, a CI gate, or an MCP server your coding agent (or subagent) calls to verify a fix is complete before it commits.

Given a PR or branch, culprit classifies it as a bugfix or feature:

  • Bugfix — blames the lines the fix removed (at base revision) to rank the commits that introduced it, then adds context:
    • Suspect set — the ranked introducing commits
    • Intent — the author's original goal (introducing PR + linked issue)
    • Lifecycle — which releases shipped the bug, plus hotspot files
    • Completeness — untouched call sites, a missing test, or a revert
  • Feature — maps the blast radius: reverse-import dependents, covering tests, high-risk shared modules.

Example

rca --html report.html on a bugfix — a one-line formula silently broken by a perf commit, shipped across three releases. QA risk score, introducing commit intent, line-evolution timeline (created → broke (red) → fix (green)), test impact, co-change gaps, reviewer suggestions.

culprit RCA report

Single self-contained HTML file. No server, no CDN. Opens offline, attaches to CI.

Architecture

The deterministic git work (diff parsing, git blame/git log -L, suspect set, reverse-import map) emits structured JSON. The LLM narrative is isolated behind a ReasoningAdapterHarnessAdapter for Claude Code (no key needed), ClaudeAPIAdapter for standalone use (claude-opus-4-8 default, --fast for claude-sonnet-4-6).

  PR / branch / stack trace
    -> pr_context (diff, changed files, commits, host links)
    -> classify (bugfix vs feature, with evidence)
         bugfix  -> suspect -> evolution -> intent / lifecycle / completeness / test_gap
         feature -> blast_radius (importers, covering tests, high-risk modules)
    -> report.build -> QA risk score   (+ test_impact, coupling, owners, coverage)
    -> optional LLM "why" narrative
    -> output: JSON | HTML | markdown | --select-tests | --fail-on (CI exit code)

Full module map and data shapes: docs/ARCHITECTURE.md.

Accuracy

Suspect-finding is benchmarked against 50 real regressions — 25 from git, 25 from systemd — where the introducing commit is known from each fix's Fixes: trailer (author-verified ground truth). culprit is run exactly as you would: given only the fix commit, it blames the removed lines to rank the commits that introduced the bug.

MetricResult
Introducing commit ranked #1 (top-1)50% (25/50)
Introducing commit in the top-5 suspect set66% (33/50)

Fully deterministic and offline, on large C codebases the engine has never seen. Reproduce: python benchmarks/run.py (clones the repos, scores every case).

Install

uvx culprit                          # runs from PyPI on demand, no install step
uvx --from "culprit[api]" rca        # include the Claude API reasoning layer

pip install culprit                  # permanent install
pip install "culprit[api]"           # + anthropic SDK
pipx install culprit                 # isolated CLI
pip install -e ".[dev]" && pytest    # from source

PR metadata uses the GitHub CLI when available (brew install gh && gh auth login). For public repos, rca --pr N falls back to the unauthenticated REST API (GitHub and GitLab) — set GITHUB_TOKEN / GITLAB_TOKEN to raise rate limits. Without either, culprit uses local git only (no PR title/labels).

Hosts: deep links work for GitHub, GitLab, Bitbucket, and Gitea. For self-hosted forges set host = "gitlab" in .culprit.toml or CULPRIT_HOST.

Languages: suspect/timeline are language-agnostic (git blame/log -L). Blast radius detects imports across JS/TS, Python, Go, Java/Kotlin, Ruby, C/C++, C#, PHP, Rust, Scala, Swift.

Usage

rca                          # current branch vs configured base (or HEAD~1)
rca --last                   # latest commit only
rca --pr 16786               # specific PR (uses the PR's own base)
rca --repo /path --base main
rca --mode api --fast        # Claude API reasoning, sonnet model
rca --json                   # structured JSON output only
rca --html report.html --open
rca --trace crash.txt        # RCA from a stack trace, no fix/PR needed
rca --verify-fix patch.diff  # check a diff for completeness before committing
rca --select-tests           # print tests to run for this change (CI-pipeable)
rca --pr 16889 --bisect "pytest tests/test_x.py::test_y"
rca --pr 16889 --fail-on high   # exit non-zero when QA risk >= high
rca serve --repo /path          # local web UI with base picker (http://127.0.0.1:8722)

CI

culprit signals risk via exit code only — no PR comments, no writes. Copy examples/github-actions/culprit-pr.yml into .github/workflows/:

- uses: actions/checkout@v4
  with: { fetch-depth: 0 }
- uses: actions/setup-python@v5
  with: { python-version: "3.12" }
- run: pip install "culprit>=0.3.0"
- env: { GH_TOKEN: "${{ github.token }}" }
  run: rca --pr ${{ github.event.pull_request.number }} --html culprit-report.html --no-save --fail-on high
- if: always()
  uses: actions/upload-artifact@v4
  with: { name: culprit-report, path: culprit-report.html }

MCP server

culprit ships an MCP server that works with any MCP-compatible client over stdio: Claude Code, Cursor, Windsurf, VS Code, Codex CLI, Zed, Continue.dev, Cline, Amazon Q, Goose, or any agent built on the MCP SDK. Requires Python 3.10+ and uv (brew install uv).

Claude Code (plugin — recommended): installs the MCP server and a skill that tells the agent when to reach for it (RCA on a regression, verify_fix before a commit):

/plugin marketplace add noordeen123/culprit
/plugin install culprit@culprit

Claude Code (MCP server only):

claude mcp add culprit -- uvx --from "culprit[mcp]" culprit-mcp

Other clients — add to your client's MCP config (mcpServers key; file location varies by client):

{
  "mcpServers": {
    "culprit": {
      "command": "uvx",
      "args": ["--from", "culprit[mcp]", "culprit-mcp"]
    }
  }
}

Tools (11):

ToolDescription
analyzeFull RCA in one call — classify + suspects/blast-radius + risk + test impact
find_suspectsRank commits by likelihood of introducing the bug
get_evolutionPer-commit line history via git log -L for the buggy range
get_intentIntroducing commit: message body, linked PR, referenced issues
check_completenessCall sites the fix didn't touch
verify_fixCheck a proposed diff before committing — complete/partial/risky
get_risk_scoreQA gate score (0–100, low/medium/high) with contributing factors
get_blast_radiusFeature change impact: dependents, covering tests, high-risk files
get_test_impactMinimal test set to run for this change
classify_changeBugfix vs feature with evidence
from_traceRCA from a stack trace — no diff or PR required

The Claude Code plugin above bundles this skill for you. For a non-plugin setup (or another agent), copy examples/claude-code-skill/SKILL.md into .claude/skills/rca/ and fill in <REPO_PATH> / <BASE_BRANCH>.

vs git bisect

git bisectculprit
InputA reliable failing testThe fix diff (or a stack trace)
MethodChecks out commits and runs the testBlames the fix's lines + git log -L
SpeedMinutes (~log₂N test runs)Instant
OutputFirst bad commitSuspect set, line evolution, intent, lifecycle, completeness, risk score
ConfidenceProofStrong heuristic

--bisect "<cmd>" runs a real bisect as an optional confirmation layer — in a throwaway git worktree so your checkout is never touched. When the first failing commit matches the blamed suspect, the HTML report stamps it confirmed by git bisect. --good / --bad override the search bounds.

HTML report

--html PATH produces a single self-contained file (no CDN, opens offline). For a bugfix it renders a line-evolution timeline: for each line the fix touched, every commit that ever changed it from creation through the breaking commit (red) to the fix (green), each node expandable to its diff. Also includes:

  • TL;DR banner and lifecycle strip (releases that shipped the bug)
  • Introducing-PR intent card and fix-completeness callout
  • Deep links on every commit / PR / file, plus weight bars on suspects
  • Per-file filter and a copy-as-markdown button
rca --pr 16889 --html rca.html --open
rca --pr 16889 --html rca.html --narrative-file why.md   # embed a pre-written narrative

Configuration

Base branch resolution order: --base flag → CULPRIT_BASE env → .culprit.tomlHEAD~1.

# .culprit.toml
base = "origin/main"

--last forces the latest-commit view regardless of config.

Tests

pip install -e ".[dev]" && pytest

Keywords

git

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts