Skip to content

themohitkhare/pycodegate

Repository files navigation

PyCodeGate

Trust, but verify. The quality gate for AI-generated Python code.

CI PyPI Python License: MIT


Why PyCodeGate?

AI coding agents ship code fast — but fast doesn't mean safe. Every eval() an LLM drops in, every mutable default it forgets, every circular import it creates is a landmine waiting to go off. PyCodeGate is the trust layer between AI-generated code and your production codebase: one command, one score, zero ambiguity.

How it works

PyCodeGate detects your project's context: framework (Django, FastAPI, Flask), Python version, package manager (uv, poetry, pip), and test framework. That context drives which rules are active — Django projects get SQL injection checks, FastAPI projects get async-correctness checks, and so on.

It then runs two analysis passes in parallel: a lint pass that evaluates 40+ rules across 8 categories (Security, Correctness, Complexity, Architecture, Performance, Structure, Imports, Dead Code), and a dead-code pass powered by Vulture that finds unused functions, classes, imports, and variables.

Findings are filtered through your configuration and scored using a weighted category-budget system. Each category has a maximum deduction budget proportional to its weight. Within a category the top 3 findings apply at full cost; additional findings apply diminishing returns (10% each), so fixing the worst issues always moves the needle. The final result is a 0–100 health score with a label: Excellent (90+), Great (75–89), Needs work (50–74), or Critical (<50).

Install

Run instantly with uvx — no install needed:

uvx pycodegate .

Install globally with pipx or uv:

pipx install pycodegate
# or
uv tool install pycodegate
# or
pip install pycodegate

Quick Start

# Basic scan — score + summary
pycodegate .

# Show file paths and line numbers for every finding
pycodegate . --verbose

# Machine-readable output for AI agents and CI
pycodegate . --json

# Auto-fix ruff-fixable issues, then scan
pycodegate . --fix

# Output only the numeric score (useful in scripts)
pycodegate . --score

# Scan only files changed vs a base branch
pycodegate . --diff main

JSON Output

Pass --json to get structured output that AI agents and CI pipelines can parse:

pycodegate . --json
{
  "version": "0.1.0",
  "path": ".",
  "score": 87,
  "label": "Great",
  "errors": 1,
  "warnings": 4,
  "elapsed_ms": 212,
  "project": {
    "framework": "fastapi",
    "python_version": "3.12",
    "package_manager": "uv",
    "test_framework": "pytest"
  },
  "diagnostics": [
    {
      "rule": "no-mutable-default",
      "severity": "error",
      "category": "Correctness",
      "message": "Mutable default argument `[]` is shared across all calls",
      "file_path": "src/api/routes.py",
      "line": 34
    },
    {
      "rule": "high-complexity",
      "severity": "warning",
      "category": "Complexity",
      "message": "Function 'process_order' has cyclomatic complexity 17 (max 15)",
      "file_path": "src/api/orders.py",
      "line": 88
    }
  ]
}

Agent Integration

PyCodeGate is designed to be used by AI coding agents. Add it to your agent's context so it runs after every Python change.

Claude Code

Add the skill to your project:

mkdir -p .claude/skills
curl -fsSL https://raw.githubusercontent.com/themohitkhare/pycodegate/main/skills/pycodegate/SKILL.md \
  -o .claude/skills/pycodegate.md

Or copy AGENTS.md to your project root — Claude Code picks it up automatically.

Cursor

Add to .cursor/rules/pycodegate.mdc:

After modifying Python files, run `uvx pycodegate . --json` and fix all findings
with severity "error" before marking the task complete. Target score: 80+.

Windsurf

Add to .windsurfrules:

After modifying Python files, run: uvx pycodegate . --json
Fix all "error" severity findings. Re-run to verify the score improved.

Codex

Add to your system prompt:

After modifying Python files, run `uvx pycodegate . --json` to check code quality.
Fix errors first. Target score: 80+.

Aider

aider --read AGENTS.md

GitHub Actions

name: Quality Gate

on: [push, pull_request]

jobs:
  pycodegate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # required for --diff

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Run PyCodeGate
        run: |
          pip install pycodegate
          pycodegate . --verbose --diff main --fail-on error

CLI Reference

Flag Default Description
[DIRECTORY] . Path to the Python project to scan
--lint / --no-lint on Enable or disable lint checks
--dead-code / --no-dead-code on Enable or disable dead code detection
--verbose off Show file path and line number per finding
--score off Print only the numeric score and exit
--json off Emit structured JSON (for agents and CI)
--fix off Run ruff --fix before scanning
--diff TEXT Scan only files changed vs this base branch
--fail-on [error|warning|none] none Exit code 1 when findings at this level exist
-v, --version Show version and exit
-h, --help Show help and exit

What It Checks

Category Weight Max Deduction What it catches
Security 5 ~24 pts eval, exec, pickle.load, unsafe YAML, hardcoded secrets, weak hashes
Correctness 4 ~19 pts Mutable defaults, bare/broad except, assert in production, bad __init__ return
Complexity 3 ~14 pts Cyclomatic complexity > 15 (warning) or > 25 (error)
Architecture 3 ~14 pts Giant modules (>500 lines), deep nesting (>5), god functions (>50 lines), too many args (>7)
Performance 2 ~10 pts String concat in loops, imports inside functions, star imports
Structure 2 ~10 pts Missing __init__.py, missing tests directory, no type hints
Imports 1 ~5 pts Circular imports, wildcard imports, import order issues
Dead Code 1 ~5 pts Unused functions, classes, variables, and imports via Vulture

Framework-specific rules (Django, FastAPI, Flask) are mapped into the Security or Correctness budget.

Scoring

PyCodeGate uses a weighted category-budget system:

  1. Each category has a weight (see table above). Weights are normalized to sum to 100 points of total deduction budget.
  2. Within a category, findings are sorted by cost (errors cost more than warnings). The top 3 findings apply at full cost; every additional finding applies diminishing returns (10% of its cost).
  3. A category's deduction is capped at its budget, so a single broken category can never zero out an otherwise healthy project.
  4. Final score = 100 - sum(capped category deductions), floored at 0.
Score Label Meaning
90–100 Excellent Production-ready
75–89 Great Minor issues to address
50–74 Needs work Significant issues present
0–49 Critical Blocking issues, do not ship

Configuration

Create pycodegate.toml in your project root:

[options]
lint = true
dead_code = true
verbose = false
fail_on = "none"

[ignore]
rules = ["dead-code", "no-import-in-function"]
files = ["tests/fixtures/**", "migrations/**", "scripts/**"]

[per-file-ignores]
"src/legacy/*.py" = ["high-complexity", "no-god-function"]

[scoring]
max-deduction.Security = 20
max-deduction.Dead Code = 0  # disable dead-code penalty entirely

Or use pyproject.toml:

[tool.pycodegate]
lint = true
dead_code = true
fail_on = "error"

[tool.pycodegate.ignore]
rules = ["no-import-in-function"]
files = ["tests/fixtures/**"]

[tool.pycodegate.per-file-ignores]
"src/legacy/*.py" = ["high-complexity"]

[tool.pycodegate.scoring]
"max-deduction" = { Security = 20, "Dead Code" = 0 }

If both files exist, pycodegate.toml takes precedence. CLI flags always override config values.

Profiles

PyCodeGate auto-detects a project profile and adjusts rule weights accordingly. You can also set a profile explicitly in config (profile = "library").

Profile Auto-detected when Adjustments
cli [project.scripts] in pyproject.toml Architecture rules weighted up; dead-code weighted down
web Django / FastAPI / Flask detected Security and correctness weighted up; framework rules active
library No scripts, no framework, has py.typed Public API checks active; dead-code weighted up
script Single-file project or scripts/ directory Architecture rules relaxed; complexity thresholds raised

Pre-commit Hook

Use --pre-commit to run PyCodeGate as a pre-commit hook. It automatically scans only the staged files:

pycodegate . --pre-commit --fail-on error

Add to .pre-commit-config.yaml:

repos:
  - repo: local
    hooks:
      - id: pycodegate
        name: PyCodeGate quality check
        entry: pycodegate . --pre-commit --fail-on error
        language: system
        types: [python]
        pass_filenames: false

Contributing

git clone https://github.com/themohitkhare/pycodegate
cd pycodegate
uv sync --all-extras
uv run pytest -q
uv run pycodegate . --verbose  # dogfood it

To add a new rule:

  1. Create a file in src/pycodegate/rules/ extending BaseRules
  2. Implement check(self, source: str, filename: str) -> list[Diagnostic]
  3. Register it in src/pycodegate/rules/__init__.py
  4. Add tests in tests/rules/

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages