Metadata-Version: 2.4
Name: graphlint
Version: 0.7.1
Summary: A static analysis tool for identifying dead code in a codebase. It provides Python API and CLI for use by agents.
Author-email: Yutong Zou <yutong.zou.24@alumni.ucl.ac.uk>
License-Expression: MIT
Project-URL: Homepage, https://github.com/AngelosZou/graphlint
Project-URL: Documentation, https://github.com/AngelosZou/graphlint#readme
Project-URL: Repository, https://github.com/AngelosZou/graphlint
Project-URL: Issues, https://github.com/AngelosZou/graphlint/issues
Project-URL: Changelog, https://github.com/AngelosZou/graphlint/blob/main/CHANGELOG.md
Keywords: graph,dependency-graph,code-analysis,dead-code,circular-dependency,import-checker,static-analysis,lint
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Documentation
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-cov>=4; extra == "dev"
Provides-Extra: rust
Requires-Dist: tree-sitter<0.27,>=0.22; extra == "rust"
Requires-Dist: tree-sitter-rust<0.25,>=0.22; extra == "rust"
Provides-Extra: csharp
Requires-Dist: tree-sitter<0.27,>=0.22; extra == "csharp"
Requires-Dist: tree-sitter-c-sharp<0.25,>=0.23; extra == "csharp"
Provides-Extra: typescript
Requires-Dist: tree-sitter<0.27,>=0.22; extra == "typescript"
Requires-Dist: tree-sitter-typescript<0.25,>=0.23; extra == "typescript"
Requires-Dist: tree-sitter-javascript<0.25,>=0.23; extra == "typescript"
Provides-Extra: c
Requires-Dist: tree-sitter<0.27,>=0.22; extra == "c"
Requires-Dist: tree-sitter-c<0.25,>=0.21; extra == "c"
Requires-Dist: tree-sitter-cpp<0.24,>=0.22; extra == "c"
Provides-Extra: cpp
Requires-Dist: tree-sitter<0.27,>=0.22; extra == "cpp"
Requires-Dist: tree-sitter-c<0.25,>=0.21; extra == "cpp"
Requires-Dist: tree-sitter-cpp<0.24,>=0.22; extra == "cpp"
Dynamic: license-file

# graphlint

[![PyPI](https://img.shields.io/pypi/v/graphlint)](https://pypi.org/project/graphlint/)
[![Python](https://img.shields.io/pypi/pyversions/graphlint)](https://pypi.org/project/graphlint/)
[![License](https://img.shields.io/pypi/l/graphlint)](https://github.com/AngelosZou/graphlint/blob/main/LICENSE)

[English](https://github.com/AngelosZou/graphlint/blob/main/README.md) | [简体中文](https://github.com/AngelosZou/graphlint/blob/main/docs/zh/README.md)

**Dead code detection for AI-generated 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 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.

## Supported Languages

| Language | Status | Parser | Features |
|----------|--------|--------|----------|
| **Python** (`.py`) | Built-in | `ast` (stdlib) | Decorators, type annotations, dynamic imports, framework-aware entry detection |
| **Rust** (`.rs`) | Built-in (opt-in deps) | `tree-sitter` | Attribute macros, traits, `pub` visibility, `macro_rules!` |
| **C#** (`.cs`) | Built-in (opt-in deps) | `tree-sitter` | Partial classes, properties/indexers/events, attributes, `.csproj` awareness, test framework entries |
| **C / C++** (`.c` `.h` `.cpp` `.cc` `.cxx` `.hpp` `.hh` `.hxx`) | Built-in (opt-in deps) | `tree-sitter` | struct/union/enum members, typedefs, macros, `#include` tracking, per-TU `static` linkage, C library entry mode, C++ classes/namespaces/templates, cross-TU member-call resolution |
| **TypeScript / JavaScript** (`.ts` `.tsx` `.js` `.jsx` `.mts` `.cts` `.mjs` `.cjs`) | Built-in (opt-in deps) | `tree-sitter` | JSX/React components, Next.js pages, NestJS decorators, Jest/Vitest tests, import/export analysis |

Install optional language support:

```bash
pip install graphlint[rust]        # adds tree-sitter and tree-sitter-rust
pip install graphlint[csharp]      # adds tree-sitter and tree-sitter-c-sharp
pip install graphlint[c]           # adds tree-sitter + tree-sitter-c + tree-sitter-cpp (C/C++)
pip install graphlint[typescript]  # adds tree-sitter + tree-sitter-typescript + tree-sitter-javascript
```

> C/C++ are analysed by a **single unified C/C++ analyzer** that owns `.c .h .cpp
> .cc .cxx .hpp .hh .hxx`. It uses `tree-sitter-c` for C files, `tree-sitter-cpp`
> for C++ files, and routes a `.h` by the language of the TU(s) that include it
> — exactly as a C/C++ compiler does. One extra, `graphlint[c]`, installs both
> grammars.

## Features

- **Dead code detection** — finds components unreachable from any entry point via graph traversal
- **Multi-language support** — Python, Rust, C#, C/C++ (unified), TypeScript, and JavaScript backends via a language adapter abstraction; Python uses stdlib `ast`, the others use `tree-sitter`
- **Language-specific awareness** — Python decorators, Rust attribute macros (`#[tokio::main]`, `#[test]`), C# attributes (`[Fact]`, `[HttpGet]`), C translation-unit scope (`static` internal linkage, per-TU header `static`s) and library entry mode, C++ classes/namespaces/templates, `enum class` members, and cross-TU member-call resolution, TS/JS JSX elements and ES module imports/exports, trait implementations, `pub`/`public` visibility, partial classes, and more
- **AST/CST parsing** — extracts functions, methods, structs, enums, traits, impls, macros, classes, properties, indexers, events, variables, and fields; aware of type annotations, destructured variables, and generics
- **Dependency graph** — builds directed edges: `read`, `write`, `call`, `inherit`, `decorate`
- **Entry point detection** — built-in rules covering Python frameworks (FastAPI, Flask, Django, Click, Typer, Celery, pytest), Rust conventions (main, async runtimes, WASM, proc macros, FFI, tests, pub API), .NET conventions (console, xUnit, NUnit, MSTest, Web API, Minimal API, Generic Host, WinForms, WPF), C/C++ conventions (`main`/`WinMain`/`wWinMain`/`DllMain`/`_tmain` for C and a free `main` for C++, `class_definition:` patterns, `test_file` by convention, and C library mode via external-linkage symbols), and TS/JS conventions (main, module index, CLI/server listen, Next.js pages, NestJS decorators, React JSX, Jest/Vitest tests) plus custom rules
- **Configurable entry templates** — add custom entry rules via `ast_pattern` prefixes including `function_call:`, `function_def:`, `decorator:`, `class_definition:` (C#), `file_match:`, `file_is_program` (C#), `visibility:pub` (Rust), `visibility:public` (C#), `trait_impl:` (Rust), `macro_def:` (Rust), `jsx_element:` (TypeScript), `export:` (TypeScript), and more
- **`--public-as-entry` flag** — treat all public items (Rust `pub`, C# `public`, C external-linkage symbols) as entry points for library analysis
- **Warning detection** — 11 warning types including circular references, unused imports, write-only variables, and more
- **Incremental updates** — after initial full scan, only changed files are re-indexed; delta-aware reachability analysis avoids full-graph recomputation; incompatible index schema versions are auto-detected and rebuilt
- **Python API + CLI** — integrate into any Tool, CI pipeline, or let agents self-analyze and self-clean

## Installation

```bash
pip install graphlint
```

**Requirements:** Python >= 3.9

For Rust support (`.rs` files), install the optional `tree-sitter` dependencies:

```bash
pip install graphlint[rust]
```

For C# support (`.cs` files), install the optional `tree-sitter` dependencies:

```bash
pip install graphlint[csharp]
```

For C/C++ support (`.c` `.h` `.cpp` `.cc` `.cxx` `.hpp` `.hh` `.hxx` files), install the optional `tree-sitter` dependencies (one extra covers both grammars):

```bash
pip install graphlint[c]   # adds tree-sitter + tree-sitter-c + tree-sitter-cpp (C/C++)
```

For TypeScript/JavaScript support (`.ts` `.tsx` `.js` `.jsx` `.mts` `.cts` `.mjs` `.cjs` files), install the optional `tree-sitter` dependencies:

```bash
pip install graphlint[typescript]
```

## 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:

```bash
# 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](https://github.com/AngelosZou/graphlint/blob/main/docs/en/guide/agent-integration.md).

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](https://github.com/AngelosZou/graphlint/issues) — these requests are typically handled quickly.

### DeepSeek Harness Plugin

A plugin bundle for the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin ecosystem ships in this repository under [integrations/dsh](https://github.com/AngelosZou/graphlint/tree/main/integrations/dsh):

- **Tools** — `graphlint_query` (dependency-graph queries with structured results), `graphlint_build` (index build as a background job, polled with `job_output`), `graphlint_config` (show/get/set for `.graphlint/config.json`).
- **Skill** — a `graphlint` skill teaches the agent when and how to use the tools.
- **Safety** — tools default to the session working directory and hard-refuse any root outside it, so an accidental high-level scan cannot block a turn.

Install the bundle from npm:

```bash
dsh plugin --profile web add dsh-graphlint
```

Then restart `dsh web`. To link a local checkout instead (development):

```bash
# 1. Clone the repository and build the bundle (requires Node.js >= 20)
git clone https://github.com/AngelosZou/graphlint.git
cd graphlint/integrations/dsh
npm install
npm run build

# 2. Link the bundle into a profile (run from the repository root)
cd ..
dsh plugin --profile web add link:./integrations/dsh

# 3. Restart dsh web
```

### CLI

```bash
# 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

# Treat all public items as entry points (library analysis mode)
graphlint query --public-as-entry

# 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](#limitations) for details.

### Python API

```python
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

```bash
# 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/](https://github.com/AngelosZou/graphlint/tree/main/docs/) directory:

- [Getting Started](https://github.com/AngelosZou/graphlint/blob/main/docs/en/guide/getting-started.md)
- [Agent Integration](https://github.com/AngelosZou/graphlint/blob/main/docs/en/guide/agent-integration.md)
- [Configuration Guide](https://github.com/AngelosZou/graphlint/blob/main/docs/en/guide/configuration.md)
- [Entry Point Detection](https://github.com/AngelosZou/graphlint/blob/main/docs/en/guide/entry-detection.md)
- [Warning Reference](https://github.com/AngelosZou/graphlint/blob/main/docs/en/guide/warnings.md)
- [CLI Usage](https://github.com/AngelosZou/graphlint/blob/main/docs/en/cli/usage.md)
- [Architecture Overview](https://github.com/AngelosZou/graphlint/blob/main/docs/en/architecture/overview.md)
- [Python API](https://github.com/AngelosZou/graphlint/tree/main/docs/en/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. This primarily affects Python; Rust's static dispatch model produces fewer false positives. **Mitigation:** add custom entry rules matching your codebase's conventions. For example, graphlint's own codebase uses `function_def:_detect_*` and `function_def:visit_*` patterns to prevent functions discovered via `getattr` from being flagged as dead.
- **Python dynamic imports** — due to Python's dynamic import mechanisms (`importlib`, `getattr`, metaclasses, etc.), the default entry templates may produce false positives in codebases that rely heavily on runtime dispatch. Users should tune the `entry_rules` configuration to match their project's conventions.
- **Rust macro expansion** — tree-sitter parses unexpanded source; procedural macros and `macro_rules!` bodies appear as opaque token trees. Some macro-generated call paths may be missed. `#[derive]` attributes are partially recognized via implicit `inherit` edges.
- **C# partial classes & reflection** — tree-sitter parses each `.cs` file independently; partial class fragments are merged into a single logical node via `part_of` edges, but members called only through reflection (`Activator.CreateInstance`, DI container registration) may be missed, similar to Python's dynamic import limitations.
- **C/C++ static analysis limits** — member calls resolve type-aware across translation units (via a project-wide class→method index), but there is **no virtual/overload/dynamic dispatch**: a call through a `Base*` pointer resolves statically to the base's method, so a derived override reached only that way may be reported dead. The preprocessor is treated as pure AST (`#if` is never evaluated, macros are not expanded); function-pointer indirect call targets are not traced; a `static` symbol in a header is per-TU. A pure-virtual (`= 0`) interface method is exempt from dead-code (dispatched by the vtable). C function parameters are not modelled as nodes, so a parameter named like a caller's local can resolve by flat name.
- **`--public-as-entry` scope** — this flag applies to languages with `public` visibility declarations (Rust `pub`, C# `public`). It has no effect on Python files. Toggling this flag triggers a full re-index. For long-term library analysis, prefer enabling the `rust_pub_api` entry rule via `graphlint config` to persist the setting.
- **Large codebase build time** — on a large codebase with 700+ `.py` files, 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. This cost is one-time, after the initial full scan, subsequent queries use incremental updates.

## License

MIT — see [LICENSE](https://github.com/AngelosZou/graphlint/blob/main/LICENSE) for details.

## Links

- [GitHub Repository](https://github.com/AngelosZou/graphlint)
- [Issue Tracker](https://github.com/AngelosZou/graphlint/issues)
- [PyPI Package](https://pypi.org/project/graphlint/)
