A static analysis tool for identifying dead code in a codebase. It provides Python API and CLI for use by agents.
Project description
graphlint
Dead code detection for AI-generated Python codebases.
AI agents generate code rapidly, leaving behind dead and redundant code that pollutes the LLM's context window and dilutes attention. Graphlint analyzes your Python codebase's dependency graph to identify entry points and detect dead code — components unreachable from any entry point — so agents can self-clean and keep the codebase lean.
Features
- Dead code detection — finds components unreachable from any entry point via graph traversal
- AST parsing — extracts classes, functions, methods, variables, and fields; aware of type annotations and unpacked variables (e.g., loop bindings)
- Dependency graph — builds directed edges:
read,write,call,inherit,decorate - Entry point detection — 10 built-in rules (main, FastAPI, Flask, Django, Click, Typer, Celery, pytest, plus package and test entries) and custom rules
- Multi-language architecture — language adapter abstraction layer, laying the foundation for future multi-language support
- Warning detection — 11 warning types including circular references, unused imports, write-only variables, and more
- Python API + CLI — integrate into any Tool, CI pipeline, or let agents self-analyze and self-clean
Installation
pip install graphlint
Requirements: Python >= 3.9
Quick Start
Agent Integration
Graphlint provides a command to inject its usage prompt into your AI coding tools at the global level, so every project automatically has graphlint's guidance:
# Install graphlint prompt into agent tools (opencode, cursor, codex, cc)
graphlint install
# Copy the prompt to clipboard for manual paste into your agent
graphlint prompt
# Remove graphlint prompt from agent tools
graphlint uninstall
Run graphlint install and select the tools you use — the prompt (usage scenarios, essential commands, and parameters) will be added to their global configuration. For details, see Agent Integration.
If your agent tool is not listed in install, run graphlint prompt to copy the prompt to your clipboard and provide it to your agent manually. For tools you'd like native support for, feel free to submit an issue — these requests are typically handled quickly.
CLI
# Find dead code in current directory
graphlint query --warn-types "dead_code"
# Full analysis with JSON output
graphlint query --json
# View a specific graph detail
graphlint query -g 1 --detail full
# Exit non-zero when dead code or circular refs found (for CI)
graphlint query --json --fail-on dead_code,circular_ref
# Rebuild index
graphlint build --force
# Configure
graphlint config show
graphlint config set --key lang --value en
Exit Codes
| Code | Meaning |
|---|---|
0 |
Success — no warnings matched --fail-on |
1 |
Error — invalid parameters, exception, or config error |
2 |
Warnings found — --fail-on matched specified warning types |
Use --fail-on with a comma-separated list of warning types to make graphlint query return exit code 2 when matching warnings are found. This enables CI pipeline integration without blocking on non-critical warnings.
Graphlint is static-analysis based and cannot recognize certain Python dynamic references (e.g., getattr, importlib), which may produce unexpected exit codes. Only use --fail-on for CI blocking behavior when you're confident in your configuration. Agents are better suited for logic that requires contextual judgment. See Limitations for details.
Python API
from graphlint.api import query
# Find dead code components
result = query(warn_types="dead_code", json_output=True)
# Full dependency graph analysis
result = query(include_tests=True, json_output=True)
Warning Types
| Warning | Description |
|---|---|
unused_import |
Imported module or name is never used |
dynamic_import |
Dynamic import via importlib or __import__ |
circular_ref |
Circular dependency between functions/classes |
syntax_error |
File contains a syntax error |
write_only |
Variable is written but never read |
deprecated_usage |
Usage of a deprecated function/class |
dead_code |
Component unreachable from any entry point |
type_mismatch |
Suspicious type annotations |
unresolved_ref |
Reference to an undefined name |
unused_variable |
Variable is defined but never used |
file_too_large |
File exceeds the configured size limit |
Development
# Clone the repository
git clone https://github.com/AngelosZou/graphlint.git
cd graphlint
# Create a virtual environment
python -m venv env
env/Scripts/activate # Windows
source env/bin/activate # Unix
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Run with coverage
pytest --cov=graphlint
# Run type checking
mypy graphlint/
# Run linting
ruff check graphlint/ tests/
Configuration
Graphlint stores its configuration in .graphlint/config.json within the analyzed directory. Use graphlint config commands to manage settings, or edit the file directly.
See graphlint config show for the full default configuration.
Documentation
Full documentation is available in the docs/ directory:
- Getting Started
- Agent Integration
- Configuration Guide
- Entry Point Detection
- Warning Reference
- CLI Usage
- Architecture Overview
- Python API
Limitations
- Static analysis only — graphlint performs static analysis and cannot detect runtime linkage such as
getattr,importlib, or dynamic dispatch patterns, which may result in false positives. Mitigation: add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase usesfunction_def:_detect_*andfunction_def:visit_*patterns to prevent functions discovered viagetattrfrom being flagged as dead. - Large codebase build time — on a large codebase with 700+
.pyfiles, 1,000+ classes, and 14,000+ functions, a full rebuild takes approximately 200 seconds (actual performance depends on hardware). Small projects (~60 files) complete in ~1 second. Best practice: runquerybefore making changes to plan your work, and avoid invokingqueryduring refactoring to prevent unnecessary index rebuilds.
License
MIT — see LICENSE for details.
Links
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 graphlint-0.2.0.tar.gz.
File metadata
- Download URL: graphlint-0.2.0.tar.gz
- Upload date:
- Size: 156.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
721456e1ba5ea618dae6b9de8082d7615b9221140c00798564d5a716c79ec0c0
|
|
| MD5 |
aeb7be6fa56b267f38164e85cb0a5936
|
|
| BLAKE2b-256 |
89f2e620e1b1864b4876cffd1ee2238b7cadc834d7b893f23e06b833d68acf17
|
File details
Details for the file graphlint-0.2.0-py3-none-any.whl.
File metadata
- Download URL: graphlint-0.2.0-py3-none-any.whl
- Upload date:
- Size: 84.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a34099d43535719af9e0f3c4a629d4b2cfa99b7e4b785ab6c7984cba58f3a65f
|
|
| MD5 |
de86a159765e336be1c235333b454851
|
|
| BLAKE2b-256 |
e023774ab4421210724ffe9b78f05ea8147485be72ab7eb4b7e9f402c41e0302
|