Offline CLI tool for static analysis of Python repositories
Project description
Repo Inspector
Offline static analysis CLI for Python repositories. No API keys. No internet. Just your code.
Repo Inspector parses your Python project with the standard library ast module and surfaces code quality metrics, architectural problems, and technical debt — all without touching the network.
Features
- Cyclomatic complexity — per-function CC scores via Radon, God Object file detection
- Dead code — heuristic detection of unreferenced functions, classes, and files
- Module coupling — fan-in / fan-out scores, highlights highly coupled modules
- Import graph — visualizes the internal dependency tree, detects circular imports
- Duplicate code — AST-normalized structural similarity hashing
- Change impact — BFS on the reversed import graph to find blast radius of a change
- Health score — weighted 0–10 score across six signals (tests, docs, CI, complexity, coupling, dead code)
- TODO scanner — inventories TODO / FIXME / HACK / NOQA / XXX comments
- Respects
.gitignore— never traverses.git/,__pycache__/,.venv/,dist/
Installation
pip install repo-inspector
Requirements: Python 3.11 or later, no other system dependencies.
Quick Start
# Full analysis summary
repo-inspector scan /path/to/your/project
# With all individual findings printed
repo-inspector scan /path/to/your/project --full
# Export results to JSON
repo-inspector scan /path/to/your/project --output report.json
Example output:
────────────────────── Project Analysis Summary ──────────────────────
Root path /path/to/your/project
Total files 84
Python files 71
Lines of code 24,392
Functions 512
Classes 73
Analyzer Critical Warning Info
Complexity Analysis 0 4 0
Import Analysis 1 0 0
Coupling Analysis 0 2 0
Dead Code Analysis 0 0 18
Duplication Analysis 0 3 0
TODO/FIXME Analysis 0 0 11
Health Analysis 0 1 0
Health Score ███████░░░ 7.2/10
Commands
| Command | Description |
|---|---|
scan <path> |
Full analysis summary across all analyzers |
complexity <path> |
Cyclomatic complexity per function, large file detection |
deadcode <path> |
Unused functions, classes, and unreferenced files |
coupling <path> |
Fan-in / fan-out module coupling scores |
imports <path> |
Import graph tree and circular dependency detection |
impact <path> <file> |
Modules affected if a given file changes |
health <path> |
Aggregated repository health score (0–10) |
duplication <path> |
Structurally identical function bodies |
todos <path> |
TODO / FIXME / HACK / NOQA / XXX inventory |
Flags
repo-inspector scan . --full # Print all individual findings after the summary
repo-inspector scan . --output out.json # Write full results to a JSON file
Thresholds
| Metric | Warning | Critical |
|---|---|---|
| Cyclomatic complexity | >= 10 | >= 20 |
| Function size (lines) | >= 50 | >= 80 |
| File size (lines) | >= 500 | >= 1000 |
| Coupling score (fan-in + fan-out) | >= 10 | >= 20 |
Health Score Signals
The health command produces a weighted score out of 10. Weights are defined in analysis/health.py.
| Signal | Weight | Description |
|---|---|---|
| Test coverage estimate | 0.25 | Ratio of test files to source files |
| Avg complexity score | 0.20 | Inverted average cyclomatic complexity |
| Documentation ratio | 0.20 | Functions with docstrings / total functions |
| CI pipeline present | 0.15 | .github/workflows/, .gitlab-ci.yml, or Jenkinsfile |
| Dead code ratio | 0.10 | Inverted ratio of unreferenced functions |
| Linting config present | 0.10 | .flake8, .pylintrc, pyproject.toml [tool.ruff], etc. |
How It Works
scan_project()traverses the directory respecting.gitignore, builds aProjectIndex- Each analyzer receives the immutable
ProjectIndexand returns anAnalysisResult cli.pycalls all analyzers and passes results toreport/formatter.pyfor Rich rendering- No analyzer touches the filesystem directly after the index is built
Development Setup
git clone https://github.com/your-org/repo-inspector
cd repo-inspector
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest
Running the test suite
pytest # all 211 tests
pytest --tb=short -v # verbose with short tracebacks
pytest tests/test_complexity.py # single module
Building the package
python -m build
# Produces:
# dist/repo_inspector-0.1.0-py3-none-any.whl
# dist/repo_inspector-0.1.0.tar.gz
Project Structure
repo_inspector/
├── cli.py # Typer CLI — all command definitions
├── scanner/
│ └── project_scanner.py # File traversal, .gitignore handling, ProjectIndex builder
├── analysis/
│ ├── complexity.py # Cyclomatic complexity, function/file size
│ ├── coupling.py # Fan-in / fan-out per module
│ ├── deadcode.py # Unused functions, classes, unreferenced files
│ ├── health.py # Health score aggregator (weights live here)
│ ├── impact.py # Change impact via reverse dependency BFS
│ ├── imports.py # Import graph, circular dependency detection
│ ├── similarity.py # Duplicate function detection via AST hashing
│ └── todos.py # TODO/FIXME/HACK/NOQA/XXX comment scanner
├── utils/
│ ├── ast_parser.py # Dataclasses: ProjectIndex, ModuleInfo, Finding, …
│ └── graph_builder.py # NetworkX DiGraph construction from ProjectIndex
└── report/
└── formatter.py # Rich-based console output (tables, trees, rules)
tests/
├── conftest.py # Shared fixtures: make_project_index, make_module_info
├── test_integration.py # End-to-end subprocess tests for every CLI command
├── test_ast_parser.py
├── test_scanner.py
├── test_complexity.py
├── test_coupling.py
├── test_deadcode.py
├── test_health.py
├── test_impact.py
├── test_imports.py
├── test_similarity.py
└── test_todos.py
Architecture Rules
These constraints are enforced by convention and tested indirectly:
- Single public function per analyzer: each module in
analysis/exposes exactlyanalyze(index: ProjectIndex) -> AnalysisResult. - Immutable
ProjectIndex: frozen dataclass — no analyzer may mutate it. - No
print()in analyzers: all output goes throughreport/formatter.py. cli.pyis the only cross-layer importer — it imports from bothanalysis/andreport/; analyzers never import fromreport/.- NetworkX for all graph work —
graph_builder.pyreturnsnx.DiGraph; all cycles/BFS use NetworkX algorithms.
Contributing
Contributions are welcome. Please open an issue before submitting a large change so we can discuss the approach.
# Fork, clone, create a branch
git checkout -b feature/my-analyzer
# Make changes, add tests, confirm the suite passes
pytest --tb=short
# Open a pull request against main
Adding a new analyzer:
- Create
repo_inspector/analysis/my_analyzer.pywithanalyze(index) -> AnalysisResult - Add the corresponding
tests/test_my_analyzer.py - Import and wire up in
cli.py - Add it to the
scancommand'sresultsdict incli.py
License
MIT — © 2026 Repo Inspector Contributors
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 repo_inspector-0.1.0.tar.gz.
File metadata
- Download URL: repo_inspector-0.1.0.tar.gz
- Upload date:
- Size: 29.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
42dbd0a540c09901da0fbd9f4448c747634583bde56981a2984bd5d0ff112300
|
|
| MD5 |
3a4e8ae5f8bda09f5a73589b8bed0e94
|
|
| BLAKE2b-256 |
eb9ceb385285d2c713deed1ea7334200b43dff6f795172aaf283827a3caa908d
|
File details
Details for the file repo_inspector-0.1.0-py3-none-any.whl.
File metadata
- Download URL: repo_inspector-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
699b6dc955d70e40d9a87e7a03bb7242bd5f7c6f2f91d631d35233d5e1cd2d03
|
|
| MD5 |
2caa8b84486985a2a45c52157737773e
|
|
| BLAKE2b-256 |
c682e0082be00afa1ae018123207f690901c1841e2d5fbf1263191eef1b712ca
|