Skip to content

samcab28/Repo-Inspector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Repo Inspector

Offline static analysis CLI for Python repositories. No API keys. No internet. Just your code.

License: MIT Python 3.11+ PyPI version

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

  1. scan_project() traverses the directory respecting .gitignore, builds a ProjectIndex
  2. Each analyzer receives the immutable ProjectIndex and returns an AnalysisResult
  3. cli.py calls all analyzers and passes results to report/formatter.py for Rich rendering
  4. No analyzer touches the filesystem directly after the index is built

Development Setup

git clone https://github.com/samcab28/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:

  1. Single public function per analyzer: each module in analysis/ exposes exactly analyze(index: ProjectIndex) -> AnalysisResult.
  2. Immutable ProjectIndex: frozen dataclass — no analyzer may mutate it.
  3. No print() in analyzers: all output goes through report/formatter.py.
  4. cli.py is the only cross-layer importer — it imports from both analysis/ and report/; analyzers never import from report/.
  5. NetworkX for all graph workgraph_builder.py returns nx.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:

  1. Create repo_inspector/analysis/my_analyzer.py with analyze(index) -> AnalysisResult
  2. Add the corresponding tests/test_my_analyzer.py
  3. Import and wire up in cli.py
  4. Add it to the scan command's results dict in cli.py

License

MIT — © 2026 Repo Inspector Contributors

About

Offline CLI tool for static analysis of Python repositories — complexity, dead code, coupling, and architecture insights without leaving your terminal.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages