Cursor客户端解剖(一)为什么是-VS-Code-Fork
先看应用包里有什么
学习材料对应的是 macOS .app 的 Contents 目录,标准 Electron 布局:
| 路径 | 角色 |
|---|---|
MacOS/Cursor |
原生启动器(Mach-O),几乎不含业务逻辑 |
Frameworks/ |
Electron / Helper 进程 |
Resources/app/out/ |
主程序打包后的 JS(core) |
Resources/app/extensions/cursor-* |
Cursor 内置扩展(Agent 能力的主要落点) |
Resources/app/product.json |
品牌、更新通道、扩展替换表、API proposal 白名单 |
不需要传统「二进制反编译」。 业务逻辑主要在 JS。难点是核心被打成超大 bundle(例如 workbench.desktop.main.js 约 45MB),变量名被 mangle,没有 source map。
所以「学 Cursor」更现实的路径是:对照开源 VS Code 的分层,再读 cursor-* 扩展的清单与小扩展逻辑。
一句话设计理念
包内导读把它概括成:
把「AI 编程代理」做成 IDE 的一等公民,同时尽量复用 VS Code 已验证的编辑器 / 扩展 / 进程隔离模型。
具体是三层叠加:
- 继承:Monaco + Workbench + Extension Host(VS Code 基因)
- 嵌入:Composer / Agent / Retrieval / MCP 作为内置能力
- 演化 UI:
glass工作台尝试用新壳承载 Agent-first 交互
为什么不是「做一个插件」就够了
VS Code 扩展模型很强,但扩展始终是宿主菜单上的客人:
- UI 与渲染主路径受 Extension API 边界约束
- 很难把多文件 Diff、Agent 轨迹、Shadow Workspace 做成一等公民
- 高频能力(检索、工具执行、长连接)不宜全塞进普通扩展生命周期里碰运气
Cursor 的选择是:Fork 内核拿控制权,再把增量能力拆成多个内置扩展。
这是「fork 内核 + 能力插件化 + UI 渐进替换」,不是推倒重来。
Fork 换来的,不只是改皮肤
从包内能直接看到的增量:
双 Workbench 入口
workbench.desktop.main.js— 经典桌面工作台workbench.glass.main.js— Glass 新壳(同量级大 bundle)
UI 可以换壳,底层服务 / 扩展协议尽量共用。
product.json的 proposed API 白名单
大量extensionEnabledApiProposals(如cursor、cursorAgentHost、cursorTracing)。
含义:用 VS Code 的 proposed API 机制合法扩展宿主能力,而不是到处 hack private API。扩展替换表
extensionReplacementMapForImports把社区/微软扩展映射到 Anysphere 发行版(例如语言服务、Remote SSH)。
兼容用户心智,同时控制关键路径的实现版本。
和「手搓一个侧边栏 Chat」的差别
| 路线 | 你得到什么 | 你失去什么 |
|---|---|---|
| 插件 | 上线快、跟随宿主升级 | 交互与隔离上限受 API 约束 |
| Fork | 编辑器级体验、进程与协议可深度改造 | 持续 rebase 上游、自己扛发行通道 |
Cursor 押的是:当编程主路径被 Agent 接管时,编辑器必须跟得上。插件路线很难把「编排 / 执行 / 检索 / 远程权威」做成 IDE 操作系统级能力。
小结
「为什么是 VS Code Fork」在包里的答案很务实:
- 底座已经证明能规模化(编辑器 + 扩展宿主 + 远程权威)
- AI 差异集中堆在 Agent 运行时、检索、MCP、Glass
- 用内置扩展集群表达能力,用 proposed API 扩展宿主,而不是把一切写死在 45MB bundle 的无法维护角落
下一篇把分层摊开:Glass / Workbench Services / Extension Host / Utility / Electron。