npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@manehorizons/necro

v1.6.0

Published

Local, free, polyglot CLI that finds anti-pattern code and proposes LLM-assisted fixes.

Downloads

909

Readme

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 for AI agents (mcp), SARIF output + --fail-on gating + a GitHub Action for CI. More framework plugins and Python are on the 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 tierscertain / 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-only verdict — 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/
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
  • deadFn is private and unreferenced → certain, safe to remove.
  • lonelyExport is exported but unused internally → likely (might be used externally, so Necro asks you to confirm).
  • testUtil is reached only via tests → the test-only verdict.

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 (verifies each removal with typecheck first, by default; refuses on a dirty git tree; --force to override; --no-verify to skip verification)
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:

  1. Point package.json main/module/bin/exports at your real entry file (add a tsconfig.json outDir/rootDir so Necro can map dist/ back to src/).
  2. Add an entries field to necro.config.json (see Configuration).
  3. 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 <high|medium|low> 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 as necro 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-demandtriage 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, which reuses scan and the worktree verifier without forking their logic.

See the Architecture docs and docs/necro-design-spec.md for the full design.

Documentation

A full documentation site (landing + guide + reference + architecture) lives in 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; 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 of certain-dead code (preview by default, dirty-tree guard, verify-by-default typecheck gate before writing — --no-verify to skip).
  • triage: LLM resolution of maybe findings (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 © manehorizons.