seek://map/graphlint
graphlint — Dead Code Detection for AI-Generated Codebases
#static-analysis #dead-code #dependency-graph #code-quality #cli
TL;DR
Per its README, graphlint is dead-code detection for AI-generated codebases: it builds a dependency graph, finds entry points, and flags components unreachable from any of them so agents can self-clean. Python is built in; Rust, C#, C/C++ and TypeScript/JavaScript come via optional extras. A DSH bundle ships in integrations/dsh, and the PyPI and npm (dsh-graphlint) packages are published.
AI coding agents generate code fast — and leave dead, redundant code behind, and graphlint attacks exactly that residue: build a dependency graph, find the entry points, and flag everything unreachable from any of them as dead code. Python analysis is built in, and Rust, C#, C/C++ and TypeScript/JavaScript plug in through tree-sitter extras; warnings run from circular references to write-only variables. The DSH bundle under integrations/dsh wires the analyzer into graphlint_query / graphlint_build / graphlint_config behind a hard guard that refuses scan roots outside the session working directory.
Facts
- install
- dsh plugin --profile web add dsh-graphlint
- license
- MIT
- note
- bundle 需 graphlint CLI 在 PATH(pip install graphlint);纯静态分析,getattr、importlib 等动态引用可能误报,按项目惯例配置 entry_rules。
Key points
- Per its README, dead code means components unreachable from any entry point: graphlint builds a directed dependency graph (edges are read, write, call, inherit, decorate) and runs reachability from the detected entry points; the motivation is that dead and redundant code left by AI agents pollutes the LLM's context window and dilutes attention, and cleanup is left to the agents themselves
- Entry-point detection ships built-in rules — Python covers FastAPI, Flask, Django, Click, Typer, Celery and pytest, and the other backends follow their own conventions (main, test frameworks, Next.js pages, NestJS decorators) — while custom rules attach via ast_pattern prefixes (function_def:, decorator:, export:, and more); --public-as-entry treats Rust pub, C# public and C external-linkage symbols as entries for library-mode analysis
- The DSH integration ships in integrations/dsh: graphlint_query (dependency-graph queries with structured JSON results), graphlint_build (index build as a background job, polled with job_output) and graphlint_config (show/get/set for .graphlint/config.json), plus a graphlint skill that teaches the agent when and how to use them; the tools default to the session working directory and hard-refuse any scan root outside it — the README ties this to avoiding a long block from an accidental high-level scan
- All agent channels derive from a single canonical skill document shipped in the package (graphlint/skill.md) so they cannot drift: the skill file under ~/.agents/skills, --targets all into ~/.claude/skills, install prompt injected into opencode, cursor, codex and cc configs, and the DSH plugin
- Languages: Python is built in (stdlib ast); Rust, C#, C/C++ (a unified analyzer that routes a .h by the translation units including it) and TypeScript/JavaScript (JSX, Next.js pages, NestJS decorators, Jest/Vitest tests, import/export analysis) enable tree-sitter backends via pip extras (graphlint[rust] / [csharp] / [c] / [typescript])
- Per its README, warnings come in 11 types — beyond dead_code: circular_ref, unused_import, write_only, deprecated_usage, type_mismatch, unresolved_ref and more; after the first full scan only changed files are re-indexed; the CLI's --fail-on exits 2 on matched warnings for CI gating
- The README's limitations section is explicit: pure static analysis cannot see runtime linkage such as getattr or importlib (mostly affecting Python), so add custom entry rules per your project's conventions — graphlint's own codebase uses function_def:_detect_* and function_def:visit_* to keep getattr-discovered functions out of the dead list. The npm package dsh-graphlint is published — this site's registry check on 2026-09-17 read latest as 0.4.0 (published 2026-09-16); the PyPI package graphlint read 0.7.1 the same day (requires Python >= 3.9)
FAQ
How is this different from ordinary linting?
Warnings like unused_import and unused_variable ask whether a single name is ever used; dead_code asks about structure — the README defines dead code as components unreachable from any entry point, derived from whole-graph reachability analysis. The former flags wasted names, the latter disconnected structure; both live in the same warning table.
Will static analysis produce false positives?
Yes, and the README's limitations section says so plainly: runtime linkage such as getattr, importlib or metaclasses cannot be detected (mostly affecting Python), so tune the entry_rules configuration to your project's conventions; graphlint's own codebase uses function_def:_detect_* and function_def:visit_* patterns so functions discovered via getattr are not flagged dead. Rust macro expansion, C# reflection and C++ virtual dispatch carry similar caveats.
How do I install it in DSH?
Per the README, install straight from npm with dsh plugin --profile web add dsh-graphlint, or fit the CLI first and run graphlint install dsh --profile web. The bundle expects the graphlint CLI on PATH (pip install graphlint); restart dsh web afterwards.
What else do I install beyond Python?
Python analysis needs no extras (stdlib ast); Rust, C#, C/C++ and TypeScript/JavaScript are pip extras — pip install graphlint[rust] / [csharp] / [c] / [typescript] — each pulling the matching tree-sitter grammars.
Official references