One binary. Nine verbs.
Everything the MCP server exposes is also a command, so CI and humans see exactly what the agent sees. Plugins render --format table as a native markdown table.
deadpath scan [PATH] [--mock] [--format json|md|sarif|table] [--lang LANG]
[--only-new] [--compact] [--min-confidence 0.25]
[--no-memory] [--no-judge]
deadpath judge [PATH] [--format table|md|json]
deadpath plan [PATH] [--format json|md|table]
deadpath workflow [PATH] [--format json|md|table] [--max 12] [--no-triage] [--no-llm]
deadpath triage [PATH] [--format json|md|table] [--max-items 8] [--no-llm]
deadpath explain FINDING_ID [PATH]
deadpath remember FINDING_ID --decision keep|false_positive|resolved [--note ...] [--path PATH]
deadpath memory [PATH] [--clear] [--forget FINDING_ID]
deadpath languages
deadpath mcp
Flags that matter
| Flag | Effect |
|---|---|
--lang LANG | auto (default) or one of py ts go rust java kotlin scala csharp ruby php swift dart elixir c lua perl. See Languages. |
--format sarif | SARIF 2.1.0 for GitHub Code Scanning; confidence and signals travel in properties. |
--only-new | Show only findings not present in memory; exit 1 only for new block findings. This is the CI ratchet. |
--compact | JSON: full evidence for new findings, one-liners for persisting ones, trimmed profile and memory summary, plus tokens_saved_estimate. |
--min-confidence | Drop findings below the threshold (default 0.25). |
--no-memory | Stateless run; do not read or write .deadpath/memory.json. |
--no-judge | Skip the devil's advocate pass; report raw scan confidence. |
--format table | GitHub-flavoured markdown table (the plugin-facing format). deadpath judge defaults to it. |
--no-llm | Force heuristic triage even if an API key is set. |
--max-items | Cap on findings sent to the model per triage run (default 8). Extra findings receive heuristic verdicts and are deferred. |
Exit codes
0— no high-confidence (block) dead code1— one or moreblockfindings (with--only-new: one or more new block findings)2— tool error
Finding schema
Finding(
id: str, # kind:path[:symbol] — stable across runs
kind: str, # unused_export | orphan_file | unused_dep | unreachable
severity: str, # block | warn | note (derived from confidence)
path: str,
symbol: str | None,
why: str,
evidence: list[str],
confidence: float, # 0..1, deterministic
signals: dict[str, float],# named adjustments that produced the score
critique: dict | None # judge verdict, objections, identification, security, effort
)
block ≥ 0.85, warn ≥ 0.55, else note. See Confidence for every scan signal and Judge for the devil's advocate pass. The --min-confidence threshold applies to the scan score so a keep verdict cannot hide the finding.
Scan JSON envelope
{
"tool": "deadpath", "version": "0.1.0", "auto_delete": false,
"profile": { "languages": {"py": 6, "go": 3}, "primary": "py", "frameworks": ["fastapi"],
"monorepo": false, "validate_commands": {...}, ... },
"counts": { "block": 2, "warn": 1, "note": 0, "total": 3 },
"findings": [ ...full Finding objects (new ones by default; all with --format json) ],
"persisting_brief": [ { "id": "...", "severity": "block", "confidence": 0.92 } ],
"delta": { "new": [...], "resolved": [...], "suppressed": [...], "counts": {...} },
"suppressed": [ { "id": "...", "decision": "keep" } ],
"memory": { "runs": 3, "cache_hits": 9, "cache_misses": 0, ... },
"tokens_saved_estimate": 269
}
Workflow JSON
{
"policy": { "block_threshold": 0.85, "warn_threshold": 0.55,
"delete_requires": "explicit user approval after validation steps pass" },
"llm": { "policy": "veto-only counsel on remove then verify; keep/note never sent; ...",
"enabled": false, "called": false, "asked": 0, "cached": 0, "heuristic": 4,
"skipped_keep": 1, "skipped_note": 0, "reviewed_remove": 3, "reviewed_verify": 1 },
"selected_findings": [...], "kept_by_triage": [...], "omitted_findings": 0,
"steps": [
{ "id": "verify-02", "phase": "verify", "finding_id": "...", "grep": "maybe_dead",
"read": ["pkg/hooks.py"], "triage": "verify: dynamic or string reference signal present; ...",
"budget_hint": "Triage flagged a dynamic/string reference: read the grep hits, not the whole file." },
{ "id": "validate-07", "phase": "validate", "command": "pytest -q", ... },
{ "id": "mem-09", "phase": "remember", "tool": "deadpath.remember", ... }
]
}
Triage JSON
{
"verdicts": {
"unused_export:pkg/hooks.py:maybe_dead": {
"verdict": "verify", "reason": "...", "model": "heuristic", "digest": "9f2c…", "at": "2026-09-08T19:40:00Z" }
},
"llm": { "enabled": true, "called": true, "asked": 4, "cached": 5, "heuristic": 0,
"skipped_keep": 1, "skipped_note": 0, "reviewed_remove": 3, "reviewed_verify": 1, "deferred": 0, "model": "gpt-4o-mini",
"prompt_tokens_estimate": 612 }
}
Judge
deadpath judge prints the due-diligence table: prosecution vs devil's advocate, identification, security lens, effort. Default format is table; md adds case-file write-ups; json is the machine envelope. See Judge.
Plan
deadpath plan returns an ordered suggestion list ranked by judge verdict then confidence. Judge keep findings are omitted. Deadpath never deletes files and never writes a patch in v0.
Explain
Optional OpenAI-compatible HTTP if DEADPATH_API_KEY or OPENAI_API_KEY is set; otherwise a heuristic paragraph that names the signals that mattered. Pass --no-llm / MCP llm: false to force the heuristic. No vendor SDKs.