Install
$ agentstack add mcp-manehorizons-necro ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Necro
Find dead code with evidence, not guesses. Necro is a local, free, polyglot CLI that finds anti-pattern code and proposes LLM-assisted fixes — and refuses to guess where pure-static tools can't.
> Status: v1.2 — published on npm. > Necro analyzes TypeScript across > multiple axes — dead code (with confidence tiers, evidence chains, and the > test-only verdict), complexity, risk hotspots, and duplication — plus safe > dead-code removal (fix), LLM triage of ambiguous findings (triage), > LLM-assisted refactors (refactor), and a read-only [MCP server](#use-from-an-ai-agent-mcp) > for AI agents (mcp), SARIF output + --fail-on gating + a GitHub Action > for CI. More framework plugins and Python are on the [roadmap](#roadmap).
Why Necro
Every analysis axis already has a strong incumbent, but no free, local tool combines them with fix reasoning across languages. Necro targets that gap:
free + local + multi-axis + LLM-assisted-fix + polyglot.
Dead code means "unreachable from any entry point." Pure-static tools must make a binary alive/dead call and eat the false positive when unsure. Necro's edge is refusing to guess:
- Confidence tiers —
certain/likely/maybe. Ambiguous code is
quarantined in maybe, not falsely killed.
- Evidence chains — every finding ships its reasoning (static references,
package exports, dynamic-import taint) so you audit the verdict.
- The
test-onlyverdict — production-dead code kept warm only by tests, a
signal no incumbent surfaces cleanly.
- Semantic, not textual — dead-code detection runs on the TypeScript
compiler API (via ts-morph), following re-exports, type-only imports, and barrel files.
Install
Requires Node.js ≥ 20.
Install globally from npm:
npm install -g @manehorizons/necro
necro scan src/
Or run it with no install (handy for agents and CI):
npx -y @manehorizons/necro scan src/
Install from source instead
git clone https://github.com/manehorizons/necro
cd necro
npm install
npm run build # bundles the CLI to dist/cli.js
node dist/cli.js scan src/
Quickstart
Point necro scan at a directory (defaults to .):
necro scan src/
You get a summary line followed by one evidence chain per finding, sorted worst-first:
3 findings (1 certain, 1 likely, 1 test-only)
deadFn src/util.ts:2 tier: certain
✓ 0 static references (TS compiler)
• coverage: not available
✓ not in package.json exports
✓ no dynamic-import taint in scope
→ safe to remove
lonelyExport src/util.ts:3 tier: likely
✓ 0 static references (TS compiler)
• coverage: not available
✓ not in package.json exports
✓ no dynamic-import taint in scope
→ exported but unused — confirm no external use, then remove
testUtil src/util.ts:4 tier: maybe
✓ 0 production references
✗ referenced only in test files
• coverage: not available
→ prod-dead — delete fn + test, or wire into prod
deadFnis private and unreferenced →certain, safe to remove.lonelyExportis exported but unused internally →likely(might be used
externally, so Necro asks you to confirm).
testUtilis reached only via tests → thetest-onlyverdict.
Below the dead-code findings, scan also prints Complexity (over-complex functions — nesting, cyclomatic, cognitive, god-function), Risk hotspots (CRAP score × git churn, ranked worst-first), and Duplication (Type-2 copy-paste clones) — each section omitted when empty.
Acting on findings
necro fix src/ # preview removal of certain-dead code (diff only)
necro fix src/ --write # apply it (refuses on a dirty git tree; --force to override)
necro triage src/ # LLM-resolve the quarantined `maybe` findings (opt-in, Anthropic API)
necro refactor src/ --type god-function # propose an LLM refactor, verified in a scratch worktree
necro refactor src/ --type extract-duplicate # lift a shared function out of a clone
triage and refactor are opt-in and call the Anthropic API (set ANTHROPIC_API_KEY); scan and fix are fully local and free. refactor prints proposals — it never edits your files — and each is verified (typecheck + tests) in a throwaway git worktree before you see it.
fix exit codes
fix uses a stable exit-code taxonomy so scripts and CI can branch on the outcome without parsing output:
| Exit | Meaning | |---|---| | 0 | written, preview, or nothing to fix | | 1 | unexpected error | | 2 | refused — the git working tree has uncommitted changes (pass --force to override) | | 3 | refused — 0 production entry points resolved (see Fail-closed entry resolution below) |
If both conditions hold (a dirty tree and unseeded reachability), exit 3 wins — you need to fix entry resolution before a dirty-tree override is even meaningful.
Fail-closed entry resolution
Necro seeds its dead-code sweep from your package's production entry points (package.json main/module/bin/exports, dist→src mapped via tsconfig.json outDir/rootDir when the manifest points at build output, package.json scripts values, conventional names like src/index.ts, and workspace member entries). If none of these resolve on a non-empty codebase, reachability is unseeded — Necro can't tell what's actually dead — so it fails closed: every dead-code finding is demoted to maybe (never auto-fix eligible), a warning banner explains why, and fix --write refuses with exit 3 instead of guessing. necro scan always reports what it resolved and where from, under diagnostics.entryResolution (also in --json and --sarif output as runs[0].properties.entryResolution).
To resolve the warning, do any one of:
- Point
package.jsonmain/module/bin/exportsat your real entry
file (add a tsconfig.json outDir/rootDir so Necro can map dist/ back to src/).
- Add an
entriesfield tonecro.config.json(see Configuration). - Use a conventional entry filename (
index.ts,src/index.ts,main.ts,
src/main.ts).
Output modes
necro scan src/ --json # machine-readable JSON (for CI)
necro scan src/ --sarif necro.sarif # SARIF 2.1.0 for GitHub code-scanning
necro scan src/ --fail-on high # exit non-zero on certain-dead code
necro scan src/ --top 10 # only the 10 worst findings
necro --version
A successful scan exits 0 regardless of findings (non-zero only on internal error) unless --fail-on is set — then it exits 1 when a finding at or above that severity exists. See CI integration for the SARIF + GitHub Action setup.
Use from an AI agent (MCP)
Necro runs as a read-only MCP server over stdio, so an agent (Claude Code, Cursor, Codex, Windsurf) can call necro's evidence-backed verdicts and verify its own edits in isolation — necro never edits your files and never wraps an LLM:
necro mcp # serves over stdio
Four read-only tools are exposed:
necro_scan— the same findings asnecro scan --json(dead-code tiers +
evidence chains, complexity, hotspots, duplication).
necro_verify— apply a set of{file, content}edits in a throwaway git
worktree, run checks (default: typecheck + tests), and report {ok, output}. Your working tree is never touched.
necro_verify_removal— for each named symbol, plan its deletion and
verify independently in its own throwaway worktree; returns a per-symbol verdict (green/red/unresolved) so you can confirm a dead-code removal is safe before applying it.
necro_explain— trace why a symbol is alive, test-only, or dead (the
same JSON as necro explain --json); set narrate: true for an additive LLM plain-English explanation (needs an API key, degrades gracefully without one).
Register it with your agent (Claude Code example):
claude mcp add necro -- npx -y @manehorizons/necro mcp
Or by hand:
{
"mcpServers": {
"necro": { "command": "npx", "args": ["-y", "@manehorizons/necro", "mcp"] }
}
}
Configuration
Necro runs zero-config. To customize which files it analyzes, add a necro.config.json to your project root:
{
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.mts", "**/*.cts"],
"ignore": ["**/node_modules/**", "**/dist/**"],
"entries": ["src/server.ts"]
}
Each key you set replaces its default. Declaration files (*.d.ts, *.d.mts, *.d.cts) and the node_modules, .git, dist, build, and coverage directories are always skipped.
entries is globs (relative to the scan target) declaring production entry points directly — the canonical fix for the fail-closed warning banner when none of Necro's automatic resolution (manifest, dist→src mapping, scripts, conventional names, workspaces) finds one. Matched files are added as prod roots with source "config" in diagnostics.entryResolution.
How it works
A scan is a pipeline of small, independently tested stages:
discover files
→ build symbol graph (ts-morph; the only language-specific part)
→ resolve entries (prod entries + framework plugins)
→ two-color reachability (+ taint) ─┐ dead code → tiers
→ classify into tiers │
→ syntactic detectors (tree-sitter) ─┤ complexity · hotspots · duplication
→ score (CRAP × churn) │
→ render (terminal / JSON) ─┘
Static analysis is always-on, deterministic, and free. The LLM layer is hybrid and on-demand — triage and refactor call the Anthropic API only for the findings you ask about, so cost scales with fixes requested, not codebase size. Refactor proposals are verified (typecheck + tests) in a throwaway git worktree before being shown, and necro computes the diff itself (the model returns code, never a patch).
The core invariant: language-specific code lives only in the symbol-graph adapter. Reachability, classification, scoring, and reporting are language-agnostic — so adding a language (Python is planned) means writing one new adapter, not touching the engine. Test files are recognized from your real test-runner config (jest --showConfig / vitest), so test infrastructure is never flagged dead. The same engine backs the [MCP server](#use-from-an-ai-agent-mcp), which reuses scan and the worktree verifier without forking their logic.
See the [Architecture docs](#documentation) and [docs/necro-design-spec.md](docs/necro-design-spec.md) for the full design.
Documentation
A full documentation site (landing + guide + reference + architecture) lives in [website/](website/), built with Astro Starlight.
Run it locally:
cd website
nvm use 22 # the docs site requires Node ≥ 22 (Astro 6)
npm install
npm run dev # → http://localhost:4321/necro/
Or build and preview the static site:
npm run build # outputs static HTML to website/dist/ (with search)
npm run preview
> The site is wired to deploy to GitHub Pages via > [.github/workflows/docs.yml](.github/workflows/docs.yml); the deploy step is > manual (workflow_dispatch) until Pages is enabled for the repository.
Project layout
src/
├─ cli.ts commander CLI (scan · fix · triage · refactor · mcp)
├─ config.ts necro.config.json loader
├─ discover.ts / glob.ts file discovery
├─ engine/ scan pipeline + prod-entry resolution
├─ graph/ symbol graph (ts-morph) — the language adapter
├─ syntactic/ tree-sitter detectors: complexity, duplication, metrics
├─ plugins/ FrameworkPlugin contract + test-runner plugin
├─ analyze/ reachability, taint, tier classification, hotspots, coverage
├─ fix/ safe certain-dead removal + dirty-tree guard
├─ triage/ LLM resolution of `maybe` findings (Anthropic)
├─ refactor/ LLM refactors + scratch-worktree verification
├─ mcp/ read-only MCP server (necro_scan, necro_verify)
└─ report/ evidence chains, terminal/JSON output, sorting
test/ vitest suite, mirroring src/
website/ Astro Starlight documentation site
docs/necro-design-spec.md the full design reference
Development
Requires Node.js ≥ 20 (the docs site under website/ needs Node ≥ 22).
npm test # vitest, single run
npm run test:watch # vitest watch mode
npm run typecheck # tsc --noEmit
npm run build # bundle the CLI (esbuild)
Necro is built test-first (red → green → refactor) and planned with the CADENCE draft → build → settle workflow; phase artifacts live in .cadence/. Contributions that come with tests and clear acceptance criteria match how the codebase is built.
Roadmap
Available today (TypeScript):
- Semantic dead-code detection (TS compiler API via ts-morph), confidence
tiers, evidence chains, the test-only verdict, test-runner awareness (jest/vitest), and lcov coverage ingestion.
- Complexity detectors (nesting, cyclomatic, cognitive, god-function) with
configurable thresholds.
- Risk hotspots: CRAP score (complexity² × (1 − coverage)³ + complexity) ×
git churn, ranked worst-first.
- Duplication: Type-2 (renamed) clone detection, clamped to function
boundaries — no jscpd.
fix: safe removal ofcertain-dead code (preview by default, dirty-tree guard).triage: LLM resolution ofmaybefindings (opt-in, Anthropic API).refactor: LLM god-function splits and extract-duplicate, verified in a
scratch worktree.
explain: traces why a symbol is alive, test-only, or dead, with an
optional --narrate LLM plain-English layer (opt-in, Anthropic API).
verify-removal: per-symbol build-green check in a throwaway worktree —
confirms a removal is safe before you apply it.
mcp: a read-only MCP server (necro_scan,necro_verify,
necro_verify_removal, necro_explain) for AI agents.
- Framework plugins: Next.js (roots App-Router entry exports) and
monorepo workspace-edge resolution.
- Output: terminal,
--json,--top N.
Planned (not yet implemented):
| Area | Planned capability | |---|---| | Detectors | Cross-language & fuzzy (Type-3) clones; god-function responsibility clustering | | Scoring | Per-line & recency-weighted churn, ownership weighting | | Fixes | test-only auto-apply; cascading re-analysis after a fix | | Frameworks | NestJS (DI), template-based plugins | | Languages | Python (detectors reused, new symbol-graph adapter) |
License
[MIT](LICENSE) © manehorizons.
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: manehorizons
- Source: manehorizons/necro
- License: MIT
- Homepage: https://manehorizons.github.io/necro/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.