manehorizons/necro
GitHub: manehorizons/necro
一款基于证据链与置信度分级的 TypeScript 死代码取证与代码质量分析 CLI 工具。
Stars: 2 | Forks: 0
# Necro
**通过证据而非猜测来寻找死代码。** Necro 是一个本地、免费、多语言的
CLI,它能找出反模式代码并提供 LLM 辅助修复建议——并且在纯静态工具无法确定时拒绝猜测。
## 为什么选择 Necro
每个分析维度都已有强大的主流工具,但没有一个免费、本地的工具能结合它们并实现跨语言的修复推理。Necro 正是瞄准了这一空白:
**免费 + 本地 + 多维度 + LLM 辅助修复 + 多语言。**
死代码是指“从任何入口点都无法到达的代码”。纯静态工具必须做出非生即死的二元判定,并在不确定时承受误报。Necro 的优势在于**拒绝猜测**:
- **置信度分级** — `certain` / `likely` / `maybe`。歧义代码会被隔离在 `maybe` 中,而不会被错误地清除。
- **证据链** — 每个发现都附带其推理过程(静态引用、package 导出、动态导入污染),以便您审核判定结果。
- **`test-only` 判定** — 仅靠测试维持活跃的生产环境死代码,这是任何现有工具都无法清晰呈现的信号。
- **语义分析而非文本分析** — 死代码检测运行在 TypeScript 编译器 API 上(通过 [ts-morph](https://ts-morph.com)),能够追踪重新导出、仅类型导入以及 barrel 文件。
## 安装
需要 **Node.js ≥ 20**。
从 npm 全局安装:
```
npm install -g @manehorizons/necro
necro scan src/
```
或者无需安装直接运行(方便用于代理和 CI):
```
npx -y @manehorizons/necro scan src/
```
## 快速开始
将 `necro scan` 指向一个目录(默认为 `.`):
```
necro scan src/
```
您将首先看到一行摘要,随后是每个发现的**证据链**,按严重程度从高到低排序:
```
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`** 是私有的且未被引用 → `certain`,可以安全移除。
- **`lonelyExport`** 已导出但内部未使用 → `likely`(可能被外部使用,因此 Necro 会要求您确认)。
- **`testUtil`** 仅通过测试到达 → `test-only` 判定。
在死代码发现的下方,`scan` 还会打印 **Complexity**(过度复杂的函数——嵌套、圈复杂度、认知复杂度、上帝函数)、**Risk hotspots**(CRAP 分数 × git churn,按严重程度排序)和 **Duplication**(Type-2 复制粘贴克隆)——当某部分内容为空时会被省略。
### 处理发现结果
```
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` 和 `refactor` 是可选的,它们会调用 Anthropic API(需设置 `ANTHROPIC_API_KEY`);`scan` 和 `fix` 则完全本地且免费。`refactor` 仅打印建议——它绝不编辑您的文件——并且每条建议在您看到之前,都会在一个一次性的 git worktree 中进行验证(类型检查 + 测试)。
#### `fix` 退出代码
`fix` 使用稳定的退出代码分类法,以便脚本和 CI 可以根据结果进行分支处理,而无需解析输出:
| 退出代码 | 含义 |
|---|---|
| `0` | 已写入、预览,或者没有需要修复的内容 |
| `1` | 意外错误 |
| `2` | 拒绝执行 —— git 工作区包含未提交的更改(传递 `--force` 可覆盖) |
| `3` | 拒绝执行 —— 解析到 0 个生产环境入口点(参见下文的 **Fail-closed 入口解析**) |
如果两个条件同时满足(工作区不干净 *且* 未播种可达性),退出代码 `3` 优先——您需要先修复入口解析, dirty-tree(脏工作区)覆盖才有意义。
#### Fail-closed 入口解析
Necro 从您 package 的生产环境入口点(`package.json` 的 `main`/`module`/`bin`/`exports`,当清单指向构建输出时,通过 `tsconfig.json` 的 `outDir`/`rootDir` 将 dist→src 进行映射,`package.json` 的 `scripts` 值,诸如 `src/index.ts` 的常规名称,以及 workspace 成员入口)来为其死代码扫描播种。如果在非空代码库中**没有一个**能解析成功,可达性将无法播种——Necro 无法判断什么是真正的死代码——因此它会 fail-closed(闭合失败):每个死代码发现都会降级为 `maybe`(永远不符合自动修复条件),一条警告横幅会解释原因,并且 `fix --write` 会以退出代码 `3` 拒绝执行,而不是进行猜测。`necro scan` 总是会在 `diagnostics.entryResolution` 下报告它解析到了什么以及来源(在 `--json` 和 `--sarif` 输出中同样包含,位于 `runs[0].properties.entryResolution`)。
要消除此警告,请执行以下任一操作:
1. 将 `package.json` 的 `main`/`module`/`bin`/`exports` 指向您的真实入口文件(添加 `tsconfig.json` 的 `outDir`/`rootDir`,以便 Necro 将 `dist/` 映射回 `src/`)。
2. 在 `necro.config.json` 中添加 `entries` 字段(参见**配置**)。
3. 使用常规的入口文件名(`index.ts`、`src/index.ts`、`main.ts`、`src/main.ts`)。
### 输出模式
```
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
```
成功的扫描无论有无发现都会以 `0` 退出(仅在发生内部错误时为非零退出),**除非**设置了 `--fail-on `——在这种情况下,当存在达到或超过该严重级别的发现时,它将以 `1` 退出。有关 SARIF + GitHub Action 的配置,请参阅 [CI 集成](https://github.com/manehorizons/necro)。
## 从 AI 代理使用 (MCP)
Necro 通过 stdio 作为只读的 [MCP](https://modelcontextprotocol.io) 服务器运行,因此代理(Claude Code、Cursor、Codex、Windsurf)可以调用 necro 基于证据的判定,并隔离验证其自身的编辑——necro **绝不编辑您的文件,也绝不包装 LLM**:
```
necro mcp # serves over stdio
```
暴露了四个只读工具:
- **`necro_scan`** — 与 `necro scan --json` 相同的发现结果(死代码分级 + 证据链、复杂度、热点、重复项)。
- **`necro_verify`** — 在一次性的 git worktree 中应用一组 `{file, content}` 编辑,运行检查(默认:类型检查 + 测试),并报告 `{ok, output}`。您的工作区永远不会被触碰。
- **`necro_verify_removal`** — 为每个指定的符号规划删除,并在其各自独立的一次性 worktree 中进行验证;返回每个符号的判定结果(green/red/unresolved),以便您在应用之前确认移除死代码是安全的。
- **`necro_explain`** — 追踪某个符号为何是活跃的、test-only 或死代码(与 `necro explain --json` 相同的 JSON);设置 `narrate: true` 可获取附加的 LLM 纯文本英文解释(需要 API key,在没有的情况下会优雅降级)。
将其注册到您的代理中(Claude Code 示例):
```
{
"mcpServers": {
"necro": { "command": "npx", "args": ["-y", "@manehorizons/necro", "mcp"] }
}
}
```
## 配置
Necro 支持零配置运行。要自定义它分析的文件,请在您的项目根目录添加 `necro.config.json`:
```
{
"include": ["**/*.ts", "**/*.tsx"],
"ignore": ["**/node_modules/**", "**/dist/**"],
"entries": ["src/server.ts"]
}
```
您设置的每个键都会**替换**其默认值。声明文件(`*.d.ts`)以及 `node_modules`、`.git`、`dist`、`build` 和 `coverage` 目录始终会被跳过。
`entries` 是用于直接声明生产环境入口点的 globs(相对于扫描目标)——当 Necro 的所有自动解析方式(清单、dist→src 映射、脚本、常规名称、workspaces)均找不到入口时,这是解决 fail-closed 警告横幅的终极方法。匹配到的文件将作为源为 `"config"` 的生产环境根节点添加到 `diagnostics.entryResolution` 中。
## 工作原理
扫描是由多个小型、独立测试过的阶段组成的流水线:
```
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) ─┘
```
静态分析始终开启、具有确定性且免费。**LLM 层是混合的且按需调用的**——`triage` 和 `refactor` 仅针对您询问的发现结果调用 Anthropic API,因此成本随请求的修复数量扩展,而不是随代码库大小扩展。重构建议在展示之前会在一次性的 git worktree 中进行验证(类型检查 + 测试),并且 necro 自行计算 diff(模型返回的是代码,而不是补丁)。
**核心不变性**:特定语言的代码仅存在于 symbol-graph 适配器中。可达性、分类、评分和报告都是与语言无关的——因此添加一种语言(已计划支持 Python)意味着只需编写一个新的适配器,而无需改动引擎。测试文件会根据您真实的测试运行器配置(jest `--showConfig` / vitest)进行识别,因此测试基础设施永远不会被标记为死代码。同一个引擎支撑着 [MCP server](#use-from-an-ai-agent-mcp),它复用了 `scan` 和 worktree 验证器,而无需重构其逻辑。
完整设计请参阅[架构文档](#documentation)和 [`docs/necro-design-spec.md`](docs/necro-design-spec.md)。
## 文档
一个完整的文档站点(落地页 + 指南 + 参考 + 架构)位于 [`website/`](website/),使用 [Astro Starlight](https://starlight.astro.build) 构建。
在本地运行:
```
cd website
nvm use 22 # the docs site requires Node ≥ 22 (Astro 6)
npm install
npm run dev # → http://localhost:4321/necro/
```
或者构建并预览静态站点:
```
npm run build # outputs static HTML to website/dist/ (with search)
npm run preview
```
## 项目布局
```
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
```
## 开发
需要 **Node.js ≥ 20**(`website/` 下的文档站点需要 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 是以**测试优先**(red → green → refactor)的方式构建的,并通过 [CADENCE](https://github.com/manehorizons/cadence) 的草稿 → 构建 → 沉淀 工作流进行规划;阶段性产物存放在 `.cadence/`。伴随测试和明确验收标准的贡献方式与该代码库的构建理念完全契合。
## 路线图
**今天可用**(TypeScript):
- 语义化**死代码**检测(通过 ts-morph 调用 TS 编译器 API)、置信度分级、证据链、`test-only` 判定、测试运行器感知(jest/vitest)以及 lcov **覆盖率提取**。
- **Complexity** 检测器(嵌套、圈复杂度、认知复杂度、上帝函数),支持可配置的阈值。
- **Risk hotspots**:CRAP 分数(complexity² × (1 − coverage)³ + complexity)× git churn,按严重程度排序。
- **Duplication**:Type-2(重命名)克隆检测,限定在函数边界内——无需依赖 jscpd。
- **`fix`**:安全移除 `certain` 级别的死代码(默认预览,带有 dirty-tree 防护)。
- **`triage`**:利用 LLM 解决 `maybe` 发现(可选,Anthropic API)。
- **`refactor`**:LLM 上帝函数拆分和提取重复代码,在临时 worktree 中进行验证。
- **`explain`**:追踪符号为何活跃、仅限测试或已死,带有可选的 `--narrate` LLM 纯英文说明层(可选,Anthropic API)。
- **`verify-removal`**:在一次性 worktree 中针对单个符号进行构建通过(build-green)检查——在您应用移除之前确认其是安全的。
- **`mcp`**:为 AI 代理提供只读的 MCP 服务器(`necro_scan`、`necro_verify`、`necro_verify_removal`、`necro_explain`)。
- **框架插件**:Next.js(根 App-Router 入口导出)和 monorepo 工作区边缘解析。
- 输出:终端、`--json`、`--top N`。
**计划中**(尚未实现):
| 领域 | 计划功能 |
|---|---|
| 检测器 | 跨语言和模糊(Type-3)克隆;上帝函数职责聚类 |
| 评分 | 按行和近期权重计算的 churn、所有权权重 |
| 修复 | `test-only` 自动应用;修复后的级联重新分析 |
| 框架 | NestJS (DI)、基于模板的插件 |
| 语言 | Python(复用检测器,新增 symbol-graph 适配器) |
## 许可证
[MIT](LICENSE) © manehorizons.
或者从源码安装
``` 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/ ```标签:MITM代理, TypeScript, 云安全监控, 安全插件, 弱口令爆破, 死代码检测, 自动化攻击, 静态分析