内容
<div align="center">
# shipdiff
### API 变更智能,用于编码代理
**差异化已发布内容。了解哪些内容已损坏。自动修复。**
[](https://www.npmjs.com/package/@shipdiff/cli)
[](#roadmap)
[](#requirements)
[](#)
[](#)
[](#testing)
[](#license)
</div>
---
每个文档和上下文产品都服务于**现在时态**:"以下是 Stripe 今天的工作方式。" 没有人能回答 *"v8 和 v12 之间有什么不同,什么会损坏"* —— 因为这是**差异**,而差异并不存在于文档中。
**shipdiff 从已发布的软件包工件中推导出差异** —— 每个提供商发布的版本化 SDK —— 因此它适用于**任何**软件包,无论是否有供应商的配合。 它为编码代理(和 CI)提供了检索无法提供的三个功能:
1. **差异,而不是快照** —— 两个版本之间发生了什么变化,什么被破坏了。
2. **长尾** —— 每个软件包,而不仅仅是前 20 个具有精美文档的软件包。
3. **负面知识** —— 哪些内容**停止**存在。 `charges.create` 永远存在于 LLM 的权重中;shipdiff 可以断言它在 v12 中被删除,并指向替换项。
## 目录
- [为什么选择 shipdiff](#为什么选择-shipdiff)
- [功能](#功能)
- [工作原理](#工作原理)
- [需求](#需求)
- [安装](#安装)
- [快速入门](#快速入门)
- [与编码代理(MCP)一起使用](#与编码代理-mcp一起使用)
- [编程 API(SDK)](#编程-api-sdk)
- [工具参考](#工具参考)
- [CLI 参考](#cli参考)
- [项目自动检测](#项目自动检测)
- [覆盖率模型](#覆盖率模型)
- [整理供应商](#整理供应商)
- [变更排名](#变更排名)
- [配置](#配置)
- [变更模式](#变更模式)
- [开发](#开发)
- [路线图](#路线图)
- [贡献](#贡献)
- [许可证](#许可证)
## 为什么选择 shipdiff
代理被要求**升级**依赖项时,正在对两个世界状态进行考古挖掘,使用为一个状态构建的工具。 文档描述了现在的状态;变更日志是散文,通常缺失,而且越来越多地成为**营销**(提供商的 `llms.txt` 是在域根目录下发布的提示)。 shipdiff 持相反的立场:
> **我们对他们实际发布的内容进行差异化。**
所有事实都源自已发布的工件,并带有 `source: "artifact" | "changelog"` 标签,因此它是可验证的,并且对对抗性攻击具有鲁棒性,这是自我发布的上下文永远无法做到的。 供应商无法操纵他们已经发布的类型签名。
## 功能
- 🔍 **版本差异** —— 删除、重命名、签名更改、弃用,带有破坏性与附加严重性。
- ⚰️ **删除账本** —— 查询某个符号在给定版本下是 `live`、`deprecated` 还是 `removed`,以及什么取代了它。
- 🧹 **生成后 linting** —— 在生成的代码中标记已删除/已弃用的 API 与**确切安装**的版本。
- 🔧 **安全 codemods** —— 自动重写已删除/已弃用的导入到其替换项(导入站点 + 每个用法),并折叠重复项。
- 📦 **两个生态系统** —— npm(通过 TypeScript 编译器 API)和 PyPI(通过静态 `ast` 侧车),一个语言中立的模型。
- 🌍 **适用于任何软件包** —— 整理的供应商获得语义增强;其他所有软件包都按需进行差异化。无需后端。
- 📈 **变更排名** —— 按 `churn × blast radius` 排名软件包以优先覆盖。
- 🔌 **MCP 原生** —— 四个工具可由代理直接调用;还有一个 CLI 和一个编程 SDK。
## 工作原理
```mermaid
flowchart TD
subgraph SRC["已发布的工件 - 不需要供应商合作"]
NPM["npm 注册表<br/>tarball (.tgz)"]
PYPI["PyPI<br/>wheel / sdist"]
end
subgraph RC["@shipdiff/registry-client · fetch → unpack → cache · no backend"]
FETCH["resolve version + download + on-disk cache"]
end
NPM --> FETCH
PYPI --> FETCH
subgraph EX["提取器"]
ENPM["@shipdiff/extractor-npm<br/>TS 编译器 API over .d.ts"]
EPYPI["@shipdiff/extractor-pypi<br/>static ast sidecar (.pyi / .py)"]
end
FETCH -->|package dir| ENPM
FETCH -->|dist dir| EPYPI
SURF["SurfaceDescriptor<br/>language-neutral · @shipdiff/core"]
ENPM --> SURF
EPYPI --> SURF
DIFF["@shipdiff/differ<br/>ChangeRecord[] · severity · rename inference"]
SURF -->|"diff v_from vs v_to"| DIFF
subgraph ENR["精选层 - 可选,升级机械 → 语义"]
VEND["vendors/*.json<br/>replacements · codemods"]
SNAP["snapshot/**<br/>预构建的精选差异"]
end
VEND -.-> DIFF
SNAP -.-> DIFF
subgraph TOOLS["@shipdiff/sdk · the tool surface"]
T1["changes_between"]
T2["symbol_status"]
T3["check_snippet"]
T4["apply_fixes"]
end
DIFF --> T1
DIFF --> T2
CHK["@shipdiff/checker"] --> T3
CHK --> T4
SURF -.-> CHK
subgraph DELIVERY["交付"]
MCP["@shipdiff/mcp<br/>MCP 服务器(stdio)"]
CLI["@shipdiff/cli<br/>diff · status · check · fix · scan · rank"]
end
TOOLS --> MCP
TOOLS --> CLI
MCP --> AGENTS["编码代理<br/>Claude Code · Cursor · …"]
CLI --> CI["CI / pre-commit"]
classDef core fill:#1f6feb,stroke:#1f6feb,color:#ffffff;
classDef tool fill:#238636,stroke:#238636,color:#ffffff;
class SURF,DIFF core;
class T1,T2,T3,T4 tool;
```
两个想法承担了大部分工作:
- **提取器 → 差异核心是同构的**(突出显示为蓝色)。 相同的代码支持按需的客户端差异和离线摄入管道,用于构建精选的快照。 差异器**就是**摄入管道;散文变更日志只是一个增强层。
- **渐进式降级。** 精选的供应商立即以语义方式提供服务,从快照或 `vendors/*.json`;任何其他 npm 或 PyPI 软件包仍然通过按需的工件差异化工作 - 机械的,但真实的。 这就是如何在供应商无需付出任何努力的情况下覆盖长尾 **without the vendor lifting a finger**.
## 需求
| | |
|---|---|
| **Node.js** | ≥ 20 |
| **包管理器** | [pnpm](https://pnpm.io)(工作区) |
| **Python** | 3.9+ — 仅用于 PyPI 提取(`SHIPDIFF_PYTHON` 可覆盖) |
## 安装
```bash
# 运行而不安装
npx @shipdiff/cli --help
# 全局安装 CLI(提供 `shipdiff` 命令)
npm i -g @shipdiff/cli
# 或用作库
npm i @shipdiff/sdk
```
<details>
<summary>从源代码构建(用于开发)</summary>
```bash
git clone <repo-url> shipdiff
cd shipdiff
pnpm install
pnpm build
```
然后使用 `node packages/cli/dist/index.js …`(或 `cd packages/cli && npm link` 以获取全局 `shipdiff`)运行 CLI。
</details>
以下示例假设全局 `shipdiff` 命令;如果您更喜欢不安装,请替换为 `npx @shipdiff/cli`。
## 快速入门
```bash
# OpenAI SDK 升级会发生什么变化?
shipdiff diff openai 4.20.0 latest --breaking
# Stripe SDK 中 charges.create 是否存活到最新版本?
shipdiff status stripe charges.create latest
# 检查当前项目安装版本(自动检测)
shipdiff check bot.ts
# 扫描整个项目中已删除/已弃用的 API 调用
shipdiff scan ./src
# 自动应用安全的升级 codemods
shipdiff fix bot.ts --write
# 按 churn × blast radius 排名供应商
shipdiff rank --vendors
# PyPI 也支持
shipdiff status requests Session latest --pypi
```
## 与编码代理(MCP)一起使用
shipdiff 通过 stdio 公开 [MCP](https://modelcontextprotocol.io) 服务器。 将其连接到编辑器的最快方法是使用以下命令:
```bash
shipdiff mcp install # 自动检测编辑器并添加 shipdiff 服务器
shipdiff mcp install -c cursor,claude-code # 或针对特定编辑器
shipdiff mcp status # 查看其配置位置
shipdiff mcp uninstall # 删除它
```
`mcp install` 检测到已安装的编辑器,并将 `shipdiff` 服务器条目合并到每个编辑器的 MCP 配置中 - **Claude Code, Cursor, Windsurf, Claude Desktop, VS Code, and Codex**。 默认情况下,它是全局的(在每个项目中都有效);传递 `--project` 以编写存储库本地配置,您可以提交和共享。 现有服务器被保留(首先备份文件),并且是幂等的。 安装后请重启编辑器。
<details>
<summary>手动配置</summary>
将以下内容添加到客户端的 MCP 配置中(例如 <code>~/.cursor/mcp.json</code>):
```json
{
"mcpServers": {
"shipdiff": { "command": "npx", "args": ["-y", "@shipdiff/mcp"] }
}
}
```
</details>
代理现在具有完整的 **write → check → fix** 循环,无需离开其工具表面:
```
changes_between → 查看升级前会发生什么变化
symbol_status → 验证符号是否仍然存在 (负面知识)
check_snippet → 检查生成的代码与安装版本
apply_fixes → 自动修复 check_snippet 标记的内容
```
## 编程 API(SDK)
```bash
npm i @shipdiff/sdk
```
```ts
import {
changesBetween,
symbolStatus,
checkSnippet,
applyFixes,
} from "@shipdiff/sdk";
// 1. 会发生什么变化?
const { breaking } = await changesBetween("openai", "4.20.0", "latest");
// 2. 此符号是否仍然存在?
const status = await symbolStatus("stripe", "charges.create", "latest");
// → { status: "removed" | "deprecated" | "live", replacement?, since?, coverage }
// 3. 检查生成的代码与确切的安装版本
const { violations } = await checkSnippet(code, [
{ package: "openai", version: "4.28.0" },
]);
// 4. 自动修复它
const { code: patched, applied, skipped } = await applyFixes(code, [
{ package: "openai", version: "4.28.0" },
]);
```
## 工具参考
MCP 工具名称为 `snake_case`;SDK 以 `camelCase` 导出相同的逻辑。
### `changes_between(package, from_version, to_version)`
差异化软件包的公共 API 两个版本之间。 返回聚合的 `ChangeRecord[]`,一个 `breaking[]` 便捷视图和一个 `coverage` 标签。
```jsonc
{
"coverage": "derived-live",
"breaking": [
{ "symbol": "createChatCompletion", "change": "removed", "severity": "breaking", "source": "artifact" }
]
}
```
### `symbol_status(package, symbol, version)`
**验证门控**:符号在某个版本下是 `live`、`deprecated` 还是 `removed` - 以及什么取代了它。 接受完整路径或叶节点(`charges.create`)。 传递 `baseline` 以确认删除并缩小窗口。
```jsonc
{ "status": "removed", "since": "removed between 3.3.0 and 4.28.0",
"replacement": { "symbol": "chat.completions.create" }, "coverage": "curated" }
```
### `check_snippet(code, manifest)`
静态检查生成的代码。 从跟踪的依赖项中解析使用的每个符号与确切的安装版本,并返回 `violations[]`(带有行/列和 `suggestedFix`)。
### `apply_fixes(code, manifest)`
Codemod 执行。 重写已删除/已弃用的**命名导入**到其替换项,在导入站点和每个用法处,并折叠重复项。 返回修补后的 `code`。 返回修补后的 `code`、一个 `applied[]` 列表和一个 `skipped[]` 列表。结构化(点路径)替换和默认/命名空间导入被报告为建议 - **永远不会**自动重写。
## CLI 参考
```
shipdiff diff <package> <from> <to> Diff 两个版本
shipdiff status <package> <symbol> <version> Symbol 生命周期在某个版本
shipdiff check <file> Lint 一个文件(自动检测安装版本)
shipdiff fix <file> Apply 安全 codemods 到一个文件
shipdiff scan [dir] Scan 一个整个项目
shipdiff rank [package ...] 按 churn × blast radius 排名软件包
shipdiff mcp install|uninstall|status 将 MCP 服务器连接到您的编辑器
```
| 标志 | 适用对象 | 含义 |
|------|-----------|---------|
| `--pypi` | 所有 | 使用 PyPI 生态系统而不是 npm |
| `--json` | 所有 | 机器可读输出 |
| `--breaking` | `diff` | 只显示破坏性变化 |
| `--baseline <ver>` | `status` | 较旧版本以确认删除/窗口 |
| `--pkg name@version` | `check`, `fix` | 显式固定版本(可重复;覆盖自动检测) |
| `--write` | `fix` | 将编辑应用于磁盘(默认为干运行) |
| `--vendors` | `rank` | 排名所有精选供应商 |
| `--no-dev` | `scan` | 忽略 `devDependencies` |
| `-c <editors>` | `mcp` | 目标编辑器(逗号分隔):claude-code, cursor, windsurf, claude-desktop, vscode, codex |
| `--project` | `mcp` | 编写存储库本地配置而不是全局 |
| `--dry-run` | `mcp` | 预览更改而不写入 |
**退出代码** 与输出格式无关(因此 `--json` 仍然控制 CI):`diff` 以 1 退出时有破坏性变化,`status` 以删除的符号退出,`check`/`scan` 以任何违规行为退出。
## 项目自动检测
`check`、`fix` 和 `scan` 不需要手动的版本列表。它们会遍历到最近的 `package.json`,读取每个依赖项,并将其固定为 `node_modules` 中实际安装的版本 - 您生成的代码将运行的真实版本。未安装且未声明的依赖项将被跳过并附带警告,永远不会被猜测。
```console
$ shipdiff check src/app.ts
resolved 1 installed deps from /path/to/project
(1 declared deps not installed, skipped)
✗ src/app.ts:3:15 OldClient does not exist in acme-sdk@2.0.0 (removed or never existed)
```
传递 `--pkg name@version` 以覆盖。自动检测目前仅支持 npm。
## 覆盖率模型
每个结果都带有 `coverage` 字段,因此您始终知道可以信任它的程度:
| 覆盖率 | 含义 |
|----------|---------|
| `curated` | 来自预构建的快照/供应商增强 - 即时,语义(替换,codemods)。 |
| `derived-live` | 从发布的工件差异化需求而来 - 机械,但真实,用于任何软件包。 |
## 整理供应商
任何 npm/PyPI 软件包开箱即用(机械差异)。要升级到 **curated** - 语义替换和 codemods - 请在 `vendors/` 中添加一个文件:
```json
{
"package": "your-sdk",
"ecosystem": "npm",
"enrichment": {
"replacements": {
"oldMethod": { "symbol": "newMethod", "note": "why", "codemod": "hint" }
}
}
}
```
无需代码更改。 这是整个“易于添加供应商”的契约。精选示例位于 [`vendors/`](vendors) (`openai`, `stripe`, `langchain`)。
## 变更排名
`shipdiff rank` 按 **churn × blast radius** 对软件包进行评分 - 构建/覆盖队列 *和* 一个“破坏您代码的 API 最多”的列表在一个计算中。
- **Churn** 由每年的 major-bumps-per-year 代理,来自注册表的发布时历史记录。
- **Blast radius** 由下载量代理,使用 log-damped,因此流行的但稳定的软件包(例如核心 S3)不能仅凭销量主导。
```console
$ shipdiff rank --vendors
churn ranking (npm) — score = breaking/yr × log(downloads)
score maj/yr majors downloads package
11.71 1.5 23 62.0M stripe
8.00 1.0 7 114.3M openai
4.96 0.7 3 11.1M langchain
```
## 配置
所有配置均通过环境变量进行;合理的默认值不需要任何配置。
| 变量 | 默认值 | 目的 |
|----------|---------|---------|
| `SHIPDIFF_CACHE` | `~/.shipdiff/cache` | 工件和提取表面的磁盘缓存 |
| `SHIPDIFF_SNAPSHOT_DIR` | `./snapshot` | 预构建的精选差异 |
| `SHIPDIFF_VENDORS_DIR` | `./vendors` | 供应商增强描述符 |
| `SHIPDIFF_PYTHON` | `python3` | PyPI 提取器的解释器 |
| `SHIPDIFF_NPM_REGISTRY` | `https://registry.npmjs.org` | npm 注册表基准 URL |
| `SHIPDIFF_PYPI_REGISTRY` | `https://pypi.org` | PyPI 基准 URL |
| `SHIPDIFF_NPM_DOWNLOADS` | `https://api.npmjs.org/downloads/point/last-month` | npm 下载端点(用于 `rank`) |
## 变更模式
`ChangeRecord` 是 shipdiff 的公共契约,以 JSON 模式发布在 [`schemas/change-record.schema.json`](schemas/change-record.schema.json) 下。 提供商可能有一天会将其排放到此模式中,而不是我们从中推导出它 - 到那时,跑步机将结束,我们将收到而不是刮擦。
```jsonc
{
"package": "openai",
"ecosystem": "npm",
"symbol": "createChatCompletion",
"kind": "function",
"change": "removed", // removed | added | renamed | signature-changed | deprecated | type-*
"severity": "breaking", // breaking | additive | safe
"from_version": "3.3.0",
"to_version": "4.28.0",
"replacement": { "symbol": "chat.completions.create" },
"source": "artifact" // artifact = 机械导出;changelog = 人类/增强
}
```
## 开发
这是一个 pnpm 工作区,由九个 TypeScript 项目引用组成。
```
packages/
├── core # 语言中立的数据模型 + 已发布的变更模式
├── extractor-npm # .d.ts 表面提取,通过 TS 编译器 API
├── extractor-pypi # 静态 Python 表面提取(ast 侧车)
├── differ # surface pair → ChangeRecord[] 带有严重性
├── registry-client # fetch / cache / diff + 增强 + 快照 + 排名
├── checker # check_snippet, apply_fixes, 项目扫描
├── sdk # 工具作为编程 API
├── mcp # MCP 服务器(stdio)
└── cli # diff · status · check · fix · scan · rank
vendors/ # 每个供应商的增强描述符
schemas/ # 已发布的 JSON 模式
```
```bash
pnpm build # 编译所有软件包(tsc -b)
pnpm watch # 增量重建
pnpm test # 离线测试套件
pnpm test:watch # 监视模式
```
### 测试
```bash
pnpm test # 确定性,无网络 - 播种缓存,使用确切版本
pnpm test:network # + live npm/PyPI,MCP 服务器和 CLI 端到端(选择加入)
```
离线套件使用 **无网络** 练习完整的编排 - 解析、提取、差异化、增强、快照、检查器、codemods 和项目自动检测 - **快速** 且 CI 友好。 `test:network`(设置 `SHIPDIFF_NETWORK=1`,首先运行 `pnpm build`)添加了真正的注册表、MCP 和 CLI 端到端覆盖。
## 路线图
- [x] 将软件包发布到 npm (`@shipdiff/cli`, `@shipdiff/mcp`, …)
- [ ] 从本地 `node_modules` 提取表面(跳过已安装依赖项的重新下载)
- [ ] PyPI 项目自动检测(需求/诗歌/站点包)
- [ ] 跨中间版本聚合多个版本
- [ ] 容器重命名传播(抑制父节点重命名时的成员删除)
- [ ] 捆绑的 Tier-1/2 快照 + 摄入管道
- [ ] CI 工作流和覆盖率报告
## 贡献
欢迎贡献。好的起点:
- **整理供应商** - 添加一个 `vendors/*.json` 增强文件(参见 [整理供应商](#整理供应商))。
- **改进提取** - npm(`.d.ts`)或 PyPI(`ast`)提取器中的边缘情况。
- **添加覆盖率** - 新的夹具和测试(`pnpm test` 在离线状态下保持绿色)。
在打开 PR 之前,请运行 `pnpm build && pnpm test`。
## 许可证
[MIT](LICENSE)
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器