Dead-code intelligence for coding agents

Find the code your agents keep rewriting around.

Deadpath builds a deterministic reachability graph of your repository, scores every finding with explainable confidence, and hands Claude Code, Cursor, or Codex a guided workflow — so the model spends tokens on judgement, not on rediscovering your codebase.

No LLM required to detect Never deletes files 16 languages · 45 frameworks Apache-2.0
deadpath scan ./svc app.py entry used.py exports.py hooks.py live_symbol dead_symbol block 0.88 maybe_dead warn 0.58 orphan.py no importers · block 0.92 2 block · 1 warn · exit 1 · auto_delete: false
16languages with reachability graphs
45frameworks recognised from imports and manifests
0files deleted by Deadpath, ever
−58%tokens on a repeat visit (fixture, measured)
The developer problem

Dead code is cheap to write and expensive to trust.

Every team has it: modules nobody imports, exports nobody calls, dependencies nobody needs. Existing tools find it — then drown you in false positives, so you stop reading them. Agents make it worse: they cannot tell dead from dynamically loaded, so they either leave the mess or delete something that mattered.

False positives kill adoption

A Django signal, a FastAPI route, a Spring bean, a Rails model: all "unused" to a naive import scanner. One wrong deletion and the tool is uninstalled.

Agents re-discover the repo every session

Each conversation starts from zero: crawl the tree, re-read files, re-triage the same findings. That is the single largest token sink in agent-driven cleanup.

Polyglot repos, single-language tools

Knip for JS, Vulture for Python, nothing agreed for Go, Java, Rust, C#, Ruby, PHP or Swift. Enterprises need one signal shape across every service.

How it works

One engine, five stages. The model never detects. It only counsels — and only as a veto.

1

Reachability graph

Python AST, TS/JS import graph, and a reference graph for 14 more languages. Deterministic. Offline.

no LLM
2

Confidence score

Named signals — decorators, base classes, string references, config mentions, dynamic imports — adjust a base score.

no LLM
3

Judge

Devil's advocate checks schedulers, orchestrators, entry points, flags, reflection. Identification + security lens. Verdict: remove / verify / keep.

no LLM
4

Counsel + workflow

Optional model reviews remove first, then verify, as evidence packets. Veto only: confirm, escalate, or overturn — never strengthen. Then exact files, greps, validate.

veto-only LLM
5

Memory

Parsed facts, finding history, decisions and verdicts persist in .deadpath/. Next session starts warm.

fewer tokens
Confidence you can read

Every finding explains itself.

Severity is derived from a reproducible score, not a hunch. Framework registration lowers it by 0.60. A string literal naming the module lowers it by 0.35. Only ≥ 0.85 becomes block and fails CI; everything else is a lead for the agent to verify.

  • Same repository, same score — reviewable in a pull request.
  • Name-based graphs (Java, C#, Swift…) are capped below block by design.
  • SARIF export lands findings in GitHub Code Scanning with confidence attached.
Read the scoring model
0.00 0.55 0.85 1.00 orphan_file · 0.92 · block
note < 0.55warn ≥ 0.55block ≥ 0.85
Judge / devil's advocate

It might be a scheduler job. Prove it isn't.

Before an agent proposes a deletion, a deterministic critic checks 30+ named counter-hypotheses against real artifacts — crontab, Dockerfile, serverless handlers, CI scripts, reflection, feature flags — and returns file:line evidence. Then an identification check and a security lens. Verdict: remove, verify, or keep, plus the one next check that settles it.

  • Plugins render a markdown table: severity, judged confidence, devil's advocate, next check, security, effort.
  • Dead and risky code (eval, pickle, verify=False, leftover secrets) is tagged remove_first — unmonitored attack surface.
  • Trivial remove findings are quick wins. Strong objections skip verify/edit so the agent does not re-argue them.
Read the due-diligence layer
deadpath judge ./svc Prosecution no importers scan 0.62 warn Devil's advocate scheduled_job ops/crontab:2 Judge keep · 0.37 (scan 0.62) Open ops/crontab:2. If the schedule is live, remember keep.
Guided, dynamic workflow

The agent gets a checklist, not a codebase.

deadpath.workflow emits verify → edit → validate → remember steps shaped by the detected stack: grep this symbol, read these 40 lines, run go test ./... or pytest -q or dotnet build, then record the decision.

  • Veto-only counsel. Graph and judge never call a model. Stage four reviews judge remove first (the deletions that hurt if a live path was missed), then verify. Keep and note stay offline. The model cannot strengthen a verdict.
  • Cached verdicts. Each verdict is stored by evidence digest; an unchanged finding is never asked about twice.
  • Works offline. Without a key the same workflow runs on deterministic heuristics.
See a generated workflow
Which findings reach the model? remove counsel first · veto only verify ambiguous · counsel once keep / note settled or weak · skip one batched call · ≤ 8 evidence packets id · kind · path · symbol · signals · why — no file bodies verdicts cached by evidence digest → never re-asked
Long-term memory

Session two costs less than session one.

.deadpath/memory.json keeps parsed facts by content hash, finding history, your decisions and triage verdicts. On a repeat visit the agent receives one-liners for known findings, nothing for acknowledged ones, and only the essentials of the profile.

  • Unchanged files are never re-parsed.
  • scan --only-new turns a legacy backlog into a CI ratchet: only new dead code fails.
  • Safe to delete at any time; never stores source or secrets.
How memory saves tokens

Tokens per deadpath.scan packet on the bundled fixture (≈4 chars/token)

First visit, full packet
708
Repeat visit, compact
296
Findings payload only, first
334
Findings payload only, repeat
65

Measured with tests/ fixture deadapp. Savings grow with repository size: the compact packet is O(findings), the full one is O(evidence).

Polyglot by design

16 languages, 45 frameworks, one finding shape.

Python gets full AST analysis. TypeScript and JavaScript (including Vue and Svelte single-file components) get an import graph. Fourteen more languages get a file-level reference graph with declared precision, so confidence stays honest. Frameworks are recognised from imports and manifests to mark entry points and registration patterns.

PythonAST · Django, FastAPI, Flask, Celery, Airflow…
full
TypeScript / JSNext, React, Vue, Nuxt, Angular, Svelte, Nest…
graph
GoGin, Echo, Fiber, Chi, Cobra
path
RustActix, Axum, Rocket, Tokio, Clap
path
JavaSpring, Quarkus, Micronaut, JUnit
name
KotlinKtor, Android
name
C#ASP.NET Core, xUnit, NUnit
name
RubyRails, Sinatra, RSpec
path
PHPLaravel, Symfony
name
SwiftSwiftUI, UIKit, Vapor
name
DartFlutter
path
ElixirPhoenix
name
Scalasbt projects
name
C / C++header include graph
path
Luarequire graph, Neovim plugins
path
Perluse / package graph
path

path imports resolve to files; findings may reach block. name references are type or module tokens; findings are capped at warn. Full support matrix →

Plugins

Thin plugins. One MCP engine.

Claude Code, Cursor and Codex each get a skill that teaches the agent the loop: scan → workflow → verify → remember. All three talk to the same deadpath mcp stdio server.

Claude Code

/plugin marketplace add wolfxops/deadpath
/plugin install deadpath

Skill + MCP manifest. Ask about unused code and Claude runs the loop.

Cursor

{ "mcpServers": { "deadpath": {
    "command": "deadpath", "args": ["mcp"] } } }

Rule + skill stop Cursor from guessing dead code without a scan.

Codex

[mcp_servers.deadpath]
command = "deadpath"
args = ["mcp"]

Add to ~/.codex/config.toml. Same server, same findings.

First win in sixty seconds.

The fixture scan needs no API key and no network. You should see an orphan file and an unused export as block, and an ambiguous export as warn.

pip install -e .
deadpath scan --mock
deadpath workflow --mock
deadpath languages