# get_dead_code

get_dead_code returns a tiered plan for unused code with high, medium and low confidence findings, directory rollups and safe-to-delete estimates. No LLM.

Dead-code detection that's fast (under 10 seconds for any repo size,
no LLM involvement) and conservative by design. Findings are
classified into confidence tiers; `safe_to_delete` requires
`confidence ≥ 0.70` and excludes dynamically-loaded patterns
(`*Plugin`, `*Handler`, `*Adapter`, `*Middleware`). Framework-aware:
Flask, FastAPI, Django, Rails, Laravel, and TYPO3 routes and
convention files don't get flagged as unused. How findings are scored is on
[Dead code](https://docs.repowise.dev/intelligence/dead-code); which kinds are shown by default is
on [Findings and triage](https://docs.repowise.dev/intelligence/findings). The other tools are on
[MCP tools](https://docs.repowise.dev/mcp/overview).

## When to call

- **Before refactor sprints**: `min_confidence=0.7` to prioritize
  safe deletions.
- **Library API audits**: `kind="unused_export"` to find unused
  public symbols.
- **Monorepo cleanup**: `include_zombie_packages=True` to find
  packages no other package depends on.
- **Aggressive cleanup**: `include_internals=True` asks for unused
  private symbols. That kind is currently hidden by the
  [finding-type registry](https://docs.repowise.dev/intelligence/findings#which-finding-types-a-surface-shows),
  so the call returns none and reports the held-back count in
  `summary.withheld_types`. The CLI's `repowise dead-code --include-internals`
  still lists them.

## Parameters

| Name | Type | Default | Description |
|---|---|---|---|
| `repo` | string |  | Repository alias. Pass "all" for a workspace-wide scan. |
| `kind` | string |  | Filter by finding kind: "unreachable_file", "unused_export", "unused_internal", "zombie_package". |
| `min_confidence` | number | `0.4` | Confidence threshold. 0.7 is a good default for actionable cleanups. |
| `safe_only` | boolean | `false` | Return only findings marked safe_to_delete. |
| `limit` | number | `20` | Max findings per tier. Clamped to 25 by the MCP transport budget. |
| `tier` | string |  | Focus on one tier: "high" (≥0.7), "medium" (0.4 to 0.7), "low" (<0.4). |
| `directory` | string |  | Filter to a path prefix (e.g. "src/"). |
| `owner` | string |  | Filter by primary owner name. |
| `group_by` | string |  | "directory" for per-dir rollup, "owner" for ownership hotspots. |
| `include_internals` | boolean | `false` | Ask for unused private/internal symbols. The kind is hidden today, so they are counted in summary.withheld_types, not listed. |
| `include_zombie_packages` | boolean | `true` | Include monorepo packages with no importers. |
| `no_unreachable` | boolean | `false` | Skip file-level reachability findings. |
| `no_unused_exports` | boolean | `false` | Skip public-export findings. |
| `finding_id` | string |  | Expand one finding by the stable id a previous response emitted. |

## Returns

| Field | Description |
|---|---|
| `summary` | `total_findings`, `filtered_findings`, `deletable_lines`, `safe_to_delete_count`, `by_kind`, and `withheld_types`: finding types held back on every surface, each with a count and reason |
| `tiers.{high,medium,low}` | Each: `description`, `count`, `lines`, `safe_count`, `findings`, `truncated` |
| `by_directory` | *(if `group_by="directory"`)* per-dir rollup sorted by lines |
| `by_owner` | *(if `group_by="owner"`)* ownership hotspots sorted by lines |
| `impact` | `total_lines_reclaimable`, `safe_lines_reclaimable`, `recommendation` |
| `limit_note` | Present if the requested `limit` was clamped to 25 |

Each finding contains `kind`, `file_path`, `symbol_name`, `confidence`,
`reason`, `safe_to_delete`, `lines`, `last_commit_at`, `primary_owner`,
`age_days`, and `last_meaningful_change`.

## Example

```python
get_dead_code(min_confidence=0.7)             # safe cleanups only
get_dead_code(kind="unused_export", group_by="owner")
get_dead_code(directory="src/legacy/", include_internals=True)
get_dead_code(repo="all")                     # workspace-wide
```

## Things worth knowing

- **Confidence tiers**:
  - **High** (≥ 0.7): no references found. Strong cleanup candidates.
  - **Medium** (0.4 to 0.7): likely unused, may have indirect references.
  - **Low** (< 0.4): possibly used via dynamic dispatch / reflection.
- **Safe-to-delete heuristic**: zero references **and** stable age
  (not modified in months). The threshold isn't just confidence. A whole
  unreachable file is never marked safe: it always needs a review.
- **Workspace-wide (`repo="all"`)**: repos are scanned concurrently; a
  repo that fails is named in `repos_failed`, and one whose index is
  behind in `repos_stale`.
- **Preview languages**: dead code on Java, C#, C, C++, Kotlin and Swift
  is served with a preview label in `_meta.preview` that carries its
  latest hand-judged measurement.
- **Cross-repo adjustment (workspace mode)**: if another repo's contract
  link (an HTTP route, a generated client) points at the file, confidence
  is cut to 30% of its value and `cross_repo_note` names the consumer. A
  file that co-changes with other repos, or an unused export of a package
  another repo depends on, keeps its confidence and gains a note. A finding
  with a note is never `safe_to_delete`, and the CLI and dashboard read the
  same cut.
- **Zombie package detection**: monorepo packages flagged when no
  other package depends on them.
- **Last meaningful change**: derived from significant commits
  (feature/fix/refactor only, style/chore filtered out).
- **Withheld types**: a finding kind the registry hides is left out of
  every tier and total, even when named with `kind`. When one had rows,
  `summary.withheld_types` gives its count, state and reason.
- **Limit clamping**: requests over 25 are clamped due to the MCP
  transport budget. `limit_note` will suggest tier/directory/owner
  filters to paginate.

> **Note**
>
> Repowise surfaces candidates. Engineers decide. Keep the team in the
> loop on big deletions. `group_by="owner"` makes it easy to route a
> cleanup PR to the right reviewer.
