面向 AI 编程 Agent 的证据与验证。
构建仓库映射。复用已有内容。完成重构。为 diff 设置把关。
`scip-query` 是一个 TypeScript CLI 和 npm 包,它将 SCIP 索引、git 历史、具备语言感知能力的源码分析,以及代码仓库自身的检查机制,转化为 AI 编程 Agent 在代码变更前、变更中和变更后都可以调用的命令。
Agent 很擅长编辑眼前的代码,但在长任务中保持对整个仓库的全局模型时就不那么可靠了:它们会忽略已有的 helper,基于残缺的上下文制定计划,只迁移了部分调用点,忽视了仅存在历史关联的文件,并且当 diff 中仍然引入了重复代码、死代码或过时的文档时,就草草宣布完成。
`scip-query` 为它们提供了一个可重复的操作循环:构建目标及其影响范围的映射,基于仓库证据制定具体计划,在引入新概念之前检查是否可复用,检测未完成的迁移和隐藏的耦合,并对最终的 diff 进行把关。它不替代编译器、测试或代码审查;它只是让结构化的证据和仓库检查变得易于被 Agent 调用和报告。
## Agent 循环
| 阶段 | Agent 需要确认的内容 | 命令 |
|---|---|---|
| 探索定向 | 已存在什么、定义在哪里、谁依赖它 | `system`, `trace`, `affected`, `plan-context` |
| 计划规划 | 外部调用的接口、变更范围、影响范围以及历史伴生文件 | `surface`, `change-surface`, `co-change` |
| 代码复用 | 对应的 helper、组件、hook 或 composable 是否已经存在 | `similar`, `recent-duplicates` |
| 完成度检测 | 某次抽取或迁移是否覆盖了所有相关的位置 | `incomplete-migration`, `unused-params` |
| 验证检查 | diff 影响了什么,受管覆盖的结构化检查是否发生回归 | `diff-impact`, `doc-drift`, `diff-gate`, `health --baseline` |
| 代码清理 | 候选的移除项以及新暴露的死代码级联 | `cleanup-plan --verify` |
React 和 Vue 仓库会获得额外的框架感知检查,用于发现重复的组件/模板结构、hook/composable 行为,以及过大组件或过大视图带来的压力。这些扩展了同样的复用和完成度工作流;核心的图、历史、规划、清理和 diff 把关命令并不局限于前端。
## 安装
```
npm install -g scip-query@latest
scip-query check-deps
scip-query reindex
```
或者不进行全局安装:`npx scip-query@latest reindex`。
## 从单次变更开始
```
# 编辑前:建立 structure、consumers、history 和 blast radius
scip-query plan-context
# 创建 helper 或 abstraction 前:寻找现有的 concept
scip-query similar
# 执行 extraction 或 migration 后:查找仍然包含旧逻辑的站点
scip-query incomplete-migration
# 宣布工作完成前:刷新 index 并 gate diff
scip-query reindex && scip-query diff-gate
```
对于全仓库范围的清理操作:
```
scip-query health
scip-query recent-duplicates
scip-query cleanup-plan --verify
scip-query health --write-baseline
```
## 证据与置信度
`scip-query` 保持每个答案的来源和强度可见:
```
flowchart LR
A["SCIP graph facts"] --> F["evidence-ranked findings"]
B["semantic augmentation"] --> F
C["source-backed candidates"] --> F
D["git-history signals"] --> F
E["repository checks"] --> F
```
1. 用于定义、引用、导入、调用和依赖关系的 **SCIP 图事实**。
2. 针对 TypeScript 的 **语义增强**(当 SCIP 索引需要更多细节时)。
3. 用于相似度、可维护性和清理检查的 **源码支撑候选集**。
4. 用于代码变动、共同变更、近期修改和文档漂移的 **Git 历史信号**。
5. 针对受支持的清理计划的 **仓库工具链验证**。
启发式的发现结果只是供检查的候选项,不能证明等价性或设计糟糕。运行 `scip-query capabilities` 以查看当前仓库和语言可用的证据和验证层。
## 语言与框架覆盖范围
图导航通过受支持的 [SCIP](https://github.com/sourcegraph/scip) 索引器工作。高置信度的增强和验证因语言和项目工具链而异。TypeScript 目前拥有最丰富的语义增强。React 和 Vue 在核心工作流的基础上增加了内置的框架感知可维护性检查。
## 清理 AI 生成的代码
这些检查专门针对 AI 辅助开发导致代码库腐化的特定方式。完整的目录以及每个检测器的预防配置详见 [docs/AI_FAILURE_MODES.md](docs/AI_FAILURE_MODES.md):
**1. 寻找回声。** Agent 会重新实现它们不知道已经存在的 helper、hook、composable 和前端组件。`recent-duplicates` 使用 git 文件年龄使相似度具有 *方向性* —— 哪一方是既定的原始版本,哪一方是近期的回声(重复项):
```
91% ECHO react-component src/components/ProjectCardVisual.tsx ProjectCardVisual (added 62 commits ago)
duplicates established src/pages/HomePage.tsx RecentProjectRow()
basis: jsx-structure
shared: component:ProjectCard, prop:title, event:click
100% TWIN src/workflows/a.ts ensureAccessible() / src/workflows/b.ts ensureAccessible()
(both new - one agent session duplicated itself; consolidate before they diverge)
```
**2. 完成未完成的抽取。** Agent 抽取了一个 helper,重新连接了一两个调用点,然后放弃了其余部分——被抽取的逻辑在它们错过的每个位置上仍然以内联方式存在。`incomplete-migration` 会找出在 diff 中新出现的 helper,确认它们已在某处被接入,并列出仍然包含该 helper 逻辑但从未调用它的既有位置(使用包含度评分,因为错过的位置包含该 helper 的逻辑 *加上* 其自身原有的逻辑):
```
src/utils/priceLabel.ts priceLabel()
wired into: src/cards/price-summary-a.ts
un-migrated: 100% buildReportB() (src/cards/price-summary-b.ts)
un-migrated: 100% buildReportC() (src/cards/price-summary-c.ts)
```
**3. 揪出说谎的标准文档。** 如果你在仓库内存放了供 Agent 在实现前阅读的标准,那么过时的标准比没有标准更糟糕。`doc-drift` 会读取每个文档的文件引用 *以及* 它的共同变更历史,然后标记出代码已经演进但文档未同步更新的情况——包括指向已不存在文件的 **失效引用**:
```
staleness 94 product/domain-model.md
BROKEN REFERENCE: cites src/api/servicePlans.ts — that file no longer exists
22 change(s) since doc update src/workflows/serviceTasks.ts (referenced by doc)
```
**4. 在项目检查中执行删除。** `cleanup-plan` 会将死代码分析运行到 *不动点* —— 删除第 0 批会使第 1 批变成死代码,该计划会展示这种级联反应。`--verify` 会在一次性的 git worktree 中应用每一批删除,并运行为你的项目检测到的受支持检查器(采用差分方式,因此预先存在的错误不会淹没信号):
```
── Batch 0: deletable now (graph-fact, 67 LOC) ──
── Batch 1: dead once batch 0 lands (cascade, 21 LOC) ──
Batch 0: COMPILER-VERIFIED
```
当验证 *失败* 时,错误信息会明确指出静态证据遗漏的确切引用——这种失败机制已经捕捉到了真实的检测器错误,并阻止了会导致构建破坏的删除操作。
**5. 削减投机性的通用性。** `unused-params` 会查找没有任何实现体会使用的末尾参数(经典的“为以后留的选项”),并将范围限制在结构上类型安全的移除操作中。
**6. 保持前端复用的诚实性。** React 和 Vue 拥有专门的前端卫生检查:component-duplicate 命令比较 JSX/模板结构,hook/composable 命令比较状态/副作用/请求行为,而 large-component/view 命令会标记出集中了太多变更理由的文件。`health` 将这些作为卫生压力纳入指标,而 `incomplete-migration` 仍然是直接检查某个 hook/composable/helper 抽取是否已接入部分(而非全部)位置的手段。
**7. 揭示隐藏耦合。** `co-change` 查找在相同的 commit 中反复更改但 *没有* 依赖边的文件对 —— schema ↔ 生成的清单 ↔ 文档的三角关系,后端 schema ↔ 前端状态库,`.env.example` ↔ 其解析器。引用图无法看到这些;变更图可以。
**8. 为每个 diff 把关。** `diff-gate` 运行一组明确定义的检查,范围仅限于变更 *引入* 的内容 —— 既有代码的回声、未完成的迁移、缺失的共同变更伙伴、引用了被更改文件的文档、新出现的未使用参数、新增的死符号、基线回归 —— 并在发现每个问题时以非零状态码退出,附带修复文本说明:
```
[co-change-partner] schema.prisma changed, but scripts/scope-inventory.mjs did not — they change together 12x (86% of the time)
-> Update scripts/scope-inventory.mjs alongside this change, or confirm the coupling no longer holds.
```
**9. 在 CI 中应用渐进收紧策略。** `health --write-baseline` 将发现的问题标识快照保存到一个可提交的文件中;`health --baseline` 遇到任何 *新* 发现的问题都会以状态码 1 退出。“不要变得更糟”是一个客观的关卡,任何分数算术都无法对其作弊。
已接受的问题发现可以被记录下来,而不会削弱其余的关卡:
```
scip-query suppress SQABC123DEF456 --check echo --reason "intentional compatibility shim"
```
这会在 `.scipquery.json` 中追加一条附带理由的条目;`config-validate` 会拒绝没有标识和理由的抑制项,而 `diff-gate --json` 会报告处于活动状态和被抑制的发现结果。
在进行任何编辑之前,`plan-context ` 会打包结构化的图景 —— 定义、引用、调用图、影响范围 —— 外加一个 HISTORY 部分:代码变动、修复提交的密度,以及通常与目标一起更改的文件(“编辑这个通常意味着也要编辑这些”)。
## 经得起推敲的健康度评分
`scip-query health` 拒绝成为一个虚荣的数字指标:
```
Codebase Health Score: 95/100
Risk: 95/100 (history-correlated signals: graph facts + change graph)
Hygiene: 100/100 (tidiness candidates)
Score Breakdown (100 minus the following):
- 5 hidden-coupling: 5 co-changing pair(s) without a dependency edge
Axes:
Deletable: 1,027 LOC across 89 symbols
Change amplification: 5 files/commit median, 23 p90
Evidence quality: 5 graph-fact, 150 heuristic, 0 user-suppressed
Validation: flagged fix-density 0.12 vs baseline 0.20 (0.6x)
```
- **风险 vs. 卫生** 是两个独立的声明:风险组件与图事实和仓库历史信号挂钩;卫生组件代表整洁度。将它们混为一谈正是评分变得毫无意义的原因。
- **每一项扣分都有明细记录** —— 标量是可审计的,而不是凭感觉。
- **验证轴是一个可证伪性循环**:它测量 *在你的仓库中*,被标记的文件是否真的比其他文件吸引了更多的修复提交,并按每个检测器分别统计。在某些代码库中,某个检测器能追踪到反复出现的修复;在另一些代码库中,它主要是噪音 —— 该工具会报告具体是哪种情况,而不是妄加假设。
- **抑制项也是数据**:每个 `// scip-query: ignore-*` 注释都是一个精确的标签,会被统计和报告。
## 准确度模型
证据层级保持明确,最强证据排在前面:
1. 来自 SCIP 数据库的 **编译器支撑的事实**(`trace`, `refs`, `deps`, `outline` 等)。
2. 通过 `ts-morph` 针对 TypeScript 的 **语义增强** —— 在 SCIP 单独使用不够完整时,提供经过验证的引用、调用方和被调用方。
3. 用于清理信号的 **源码支撑的启发式方法** (AST/文本)。始终带有标签:*“这些是候选项,而非确切的编译器事实。”*
4. 用于删除操作的 **编译器验证** —— 唯一配得上“安全”一词的层级。
因为你不去衡量的准确度只是一种感觉,`self-audit` 会对符号进行抽样,并根据 TypeScript 编译器对低成本路径进行评分:
```
references precision 1.0 recall 0.9 (the cheap path doesn't fabricate; it occasionally misses)
```
启发式检测器内置了从真实代码库中学到的护栏:已发布的 `package.json` 对外接口豁免于“未使用”的建议,`contracts/` 和 `types/` 模块豁免于“定义者从不使用它”的检测,测试文件和组件的同级文件不计入隐藏耦合,而基于策略强制要求的 changelog 也不算作漂移。
## Agent 技能
`scip-query install-skills` 会将现成的技能符号链接到 Claude Code、Codex 和共享的 Agent 根目录(`~/.agents/skills/`)—— 它们会随软件包自动更新。`scip-query` 路由技能在任何代码库工作时触发,并分派给合适的专家模块:探索(`scip-explore`)、瘦身去重(`scip-debloat`)、目录架构审查(`scip-directory-architecture`)、可维护性审查(`scip-maintainability`)、React 前端可维护性审查(`scip-react-maintainability`)、Vue 前端可维护性审查(`scip-vue-maintainability`)、变更后验证(`scip-verify`)、文档核对(`scip-doc-reconcile`)、AI 腐化清理(`scip-ai-cleanup`)、特定语言指导(`scip-language-playbook`)以及脚踏实地的规划(`concrete-plan`)。
然后,在每个项目中执行一次,`scip-query setup-agent` 会植入 `AGENTS.md` 指导块(外加一个 `CLAUDE.md` 导入垫片,因为 Claude Code 原生并不读取 AGENTS.md),而 `--git-hook` 会添加一个 pre-commit 的 diff 把关钩子,无论哪个 Agent —— 或是人类 —— 编写了变更代码,它都会被触发。
## 快速开始
```
scip-query check-deps # verify indexers are runnable
scip-query install-skills # optional: agent skills
scip-query reindex
scip-query stats
scip-query system src/auth
scip-query plan-context login
scip-query health
scip-query cleanup-plan --verify
scip-query health --write-baseline # start the ratchet
```
## 前置条件
- Node.js >= 18
- `scip` CLI,来自 [Sourcegraph SCIP 发布版](https://github.com/sourcegraph/scip/releases)
- 针对你的项目的特定语言的 SCIP 索引器
| 语言 | 索引器 | 安装 |
|---|---|---|
| TypeScript / JavaScript / Vue | scip-typescript | `npm install -g @sourcegraph/scip-typescript` |
| Java / Scala / Kotlin | scip-java | [发布版](https://github.com/sourcegraph/scip-java/releases) |
| Rust | rust-analyzer | 随 rust-analyzer 附带:`rust-analyzer scip` |
| Python | scip-python-plus | `npm install -g scip-python-plus` |
| Go | scip-go | `go install github.com/sourcegraph/scip-go@latest` |
| Ruby | scip-ruby | [发布版](https://github.com/sourcegraph/scip-ruby/releases) |
| C / C++ | scip-clang | [发布版](https://github.com/sourcegraph/scip-clang/releases) |
| C# / VB | scip-dotnet | [版](https://github.com/sourcegraph/scip-dotnet/releases) |
| Dart | scip-dart | [发布版](https://github.com/nicovince/scip-dart/releases) |
| PHP | scip-php | [发布版](https://github.com/nicovince/scip-php/releases) |
对于 Python,可执行文件可能是 `scip-python`、`scip-python-plus` 或两者皆有。`scip-query` 接受这两种名称中的任何一个。
Vue 单文件组件通过 JavaScript/TypeScript 索引器进行处理。`scip-query` 还会提取 `