安装
在 DeepSeek Harness 里通过 dsh-market 安装
dsh plugin --profile web add dshmarket
或使用命令行
dsh plugin --profile web add dsh-plugin-codegraph-project
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络。请先审阅源码,并尽量锁定 commit(github:owner/repo#sha)。
README
让 CodeGraph 按项目工作。 每个项目一个 CodeGraph MCP 服务,由
npx启动,被该项目下所有 会话共享;只有 workspace 真正有索引的会话才看得到它的工具。
概述
CodeGraph 本身很好用,但它的 MCP 服务有一个结构性限制:从自己的进程工作目录推断"当前项目"。
所以现有的 DSH 集成都只能全局回答"是哪个项目"——托管一行 MCP、钉一个 cwd,换项目就改文件加
热重载。
本插件把这个问题放到会话维度解决:
| 关注点 | 本插件的决策方式 |
|---|---|
| 这个会话属于哪个项目? | 会话自己的 workspace 目录(agent.session.header.cwd),按 CLI 同款规则向上找到第一个含索引库的 .codegraph/。 |
| 用哪个 CodeGraph 版本? | 由你决定:本插件行的配置项(并可在 DSH settings 里覆盖)。 |
| 起几个服务? | 每个项目根一个,按引用计数被该项目下所有会话共享。同一仓库两个会话共用一个进程,两个仓库各一个。 |
| 什么时候能用工具? | 只在项目确实有索引之后。没有 .codegraph/ 的项目无法调用 CodeGraph:调用会被拒绝,理由里写明"没有索引"并给出 init 命令。 |
| 会话中途才建索引怎么办? | 几秒内自动注入到正在运行的那个会话。不用重开、不用重启。 |
| 进程会不会残留? | 全部通过 harness 的托管子进程服务启动,属于 DSH:会话释放就减引用,harness 退出就终止剩下的。 |
安装
桌面版(DSH 桌面 App)——在 App 侧栏的 Plugins 页里安装:粘包名即从 npm 拉取,粘绝对路径则安装本地检出。
dsh-plugin-codegraph-project
# 或本地检出
/path/to/dsh-plugin-codegraph-project
desktop 这个 profile 归 App 独占——dsh plugin --profile desktop … 会被明确拒绝(profile "desktop" is managed exclusively by the Electron application),所以桌面版走 Plugins 页。它会安装包、把 bundle 追加进 profile,随后该行会出现在设置页的清单里。宿主侧的行会即时加载;客户端界面需要刷新一次页面才会出现。
命令行 profile(dsh web、TUI、headless)——按名字装进对应 profile,或 link 一个检出:
# 从 npm 安装
dsh plugin --profile web add dsh-plugin-codegraph-project
# 或本地检出
dsh plugin --profile web add link:/path/to/dsh-plugin-codegraph-project
然后重启该 profile(dsh web)。包内自带 bundle patch,会自己插入插件行,不需要手改组成文件。
需要 Node.js 20 或更高版本,并且 DSH 部署提供 @deepseek-ai/dsh-subprocess 与
@deepseek-ai/dsh-tools。
两条依赖线都支持:0.1.5 线上由宿主 settings namespace 在本行之上叠一层用户覆盖;0.2.0 线上该 namespace 根本注册不了——0.2.0 移除了插件可注册的设置命名空间——本行的 config 是唯一设置通道(插件会打一行日志说明,见配置)。
除此之外不需要任何准备:服务通过 npx 拉到一个由本插件管理的缓存目录,不需要全局安装 CodeGraph。
快速开始
建索引是你的决定,与上游一致——本插件绝不自己跑 init。需要 CodeGraph 的项目:
npx -y @colbymchenry/codegraph@1.6.0 init -y -- /path/to/project
它只会在项目里创建 .codegraph/codegraph.db,不动源码。会话开着时执行,几秒内工具就会出现;
先建好再开会话,会话一开始就带着工具。(这条「实时出现」以默认 pollMaxMs: 0 为前提;若设了正的
pollMaxMs,超过该时限后才建的索引需要重开会话。)
codegraph uninit -- /path/to/project 可撤销。
配置
在 profile 的 cordis.patch.yml 里给这一行加配置:
- id: codegraph-project
config:
version: '1.6.0'
| 字段 | 默认 | 含义 |
|---|---|---|
enabled |
true |
总开关。关掉后不 spawn、不注册任何工具。 |
version |
'1.6.0' |
全局使用的 CodeGraph 版本,以 @colbymchenry/codegraph@<version> 交给 npx。默认值是本插件实测过的版本。 |
packageSpec |
'' |
逃生口:完整包规格,优先于 version(镜像源、私有 registry、file: tarball)。npx 会加载并执行它指向的代码,因此本地 file:/git+file: 形式必须是绝对路径。 |
cacheDir |
'' → ~/.dsh/codegraph/npm-cache |
npx 使用的 npm 缓存目录,不存在会创建。DSH 的 ~/.npm 不可写时改这里。 |
serverName |
'codegraph' |
MCP 命名空间,模型看到的工具名是 mcp__<serverName>__codegraph_explore。 |
toolCallTimeoutMs |
60000 |
单次查询的超时。 |
pollIntervalMs |
3000 |
未索引 workspace 的检查间隔。 |
pollMaxMs |
0 |
超过这个时长就放弃观察未索引的 workspace;0 = 整个会话周期都观察。 |
telemetry |
未设置 | 只有显式配置时才通过 CODEGRAPH_TELEMETRY 转达(true → 1,false → 0)。未设置时不注入任何值,由 CodeGraph 按用户自己的环境决定;插件不会自作主张开或关遥测。 |
cliProbe |
false |
启动时跑一次 npx … version 并记录结果,顺带预热 npx 缓存。默认关:它会在每次 App 启动都执行,即使根本没人用 CodeGraph;冷缓存时它会一边下载平台包,一边和首个会话自己的 npx 启动争抢同一个缓存。确认这份诊断值这个成本时再打开。 |
usageGuidance |
true |
只对已挂载的会话注入一小段 CodeGraph 用法指引。 |
diagnosticTool |
false |
注册一个 codegraph_project_status 工具,报告本会话状态。 |
allowHomeProject |
false |
允许把就是家目录(或文件系统根)的索引当作项目。相当于 CLI 的 --force;嵌套在家目录里的索引一直都会被正常服务。 |
同样的子集也作为 DSH settings 命名空间 codegraph-project 暴露
(version、packageSpec、cacheDir、toolCallTimeoutMs、allowHomeProject),
用户层优先于行配置。
所有值在使用前都会校验:非法设置会被拒绝,行配置继续生效。enabled 刻意不在这一层里:
行配置的 enabled 由 loader 在插件运行前就生效,而设置层的开关只会在激活时被读一次,
并不能真的把一个正在跑的插件关掉。
DSH 0.2.0 及以后不再提供「插件可注册的设置命名空间」。当某个 build 仍然暴露
settings服务、 但它没有register()时,插件会打一行日志说明,而不是静默忽略这一层。(如果该 build 完全没有settings服务,则该层与这行日志都不会出现。)请改配行配置——同样的键, 写在 profile 的cordis.patch.yml里,这也是 0.2.0 对每个插件设置使用的唯一通道。
切换版本
version 作用于下一次项目连接。已经连上的会话保持它启动时的版本——在一个正在使用的会话里
把服务换掉,会让正在被调用的工具凭空消失。重开该会话(或该项目的最后一个会话)即可切到新版本。
模型看到什么
每个项目连接一个工具,命名与 MCP 桥完全一致:
mcp__codegraph__codegraph_explore
CodeGraph 1.6.0 默认只暴露 codegraph_explore。要开放它其余的具,用上游自己的开关;本插件
原样透传服务端声明的工具集,不改名、不筛选:
# 子进程环境由 harness 环境清洗重建,所以在启动 harness 的地方导出
# (本插件行没有 `env` 配置项):
export CODEGRAPH_MCP_TOOLS=explore,node,search,callers
会话已挂载时,usageGuidance 还会注入一小段指引,写明项目根与可用工具,并告诉模型优先用
codegraph_explore 而不是 grep/read 循环。没有索引的会话不会拿到这段内容。
工作方式
- 没索引就没有 CodeGraph。 判定标准是
.codegraph/里存在*.db,不是目录存在:CodeGraph 把 运行态文件(daemon.sock、codegraph.lock、telemetry*.json)放在~/.codegraph,只装这些的目录 不是项目。任何项目上线之前工具根本没有注册;一旦有项目上线,某个 workspace 没索引的会话仍然调不动 它——守卫会拒绝,拒绝理由里写明该 workspace 与它对应的init命令。 - 家目录与文件系统根永远不算项目,即使里面真的有一个索引。
~/.codegraph既是 CLI 的运行目录,也是codegraph init --force -- ~写入数据库的地方;没有这条规则,一个~/.codegraph/codegraph.db就会 成为$HOME下每一个会话的项目——工具会为一个谁都没建过索引的代码库提供出来。这与 CLI 一致: 它对家目录init会拒绝并提示需要--force(原话是"it looks like your home directory")。而嵌套在 家目录里的真实项目(~/work/api)仍由它自己的索引服务。把allowHomeProject: true设上,就相当于 插件的--force。 - 第一次请求可能赶在连接之前。 会话的工具列表在每次模型请求开始时组装,而连接一个项目约需两秒
(可选的
cliProbe预热过缓存会更快)。交互式会话里这意味着你还没打完字工具就已经就绪;程序化驱动的会话 可能需要在首个 prompt 前等挂载完成。插件会为每个项目挂载打一行日志,写明 pid 与工具名。 - 向上解析。 monorepo 里
packages/app下的会话由仓库根的索引服务,服务进程的cwd就是那个根。 - 每个项目一个进程。 同项目的第二个会话接入已有服务;最后一个会话释放时进程才停止。
- 动态注入。 稍后出现的索引由轮询发现,工具会注册进正在运行的会话的作用域。
- 断线自愈。 服务崩了,下一次工具调用会透明重连;重连失败会作为工具错误返回,而不是假装成功。
- 首次冷启动。 某个版本第一次使用要下载平台包(几十 MB)。会话永远不会被它阻塞——连接就绪后
工具才出现。把
cliProbe设为true可在启动时顺便预热同一个缓存;它默认是关的,免得每次 App 启动 都为一次没人要的下载买单(还和首个会话争抢同一个缓存)。
按会话的工具列表(一个平台限制)
本插件把工具定义注册在根工具注册表上,用守卫实现按会话的访问控制,而不是按会话注册。这是刻意
的选择,也是这个约束的真实形态:DSH 的工具注册表支持 agent 作用域注册,但模型的工具列表是用
scope: agent(Agent 对象本身,而不是注册表分层所用的 scope key)组装的,因此 agent 作用域的定义
永远到不了模型。这是实测结论(0.1.5-rc.2):通过 agent.ctx 注册的工具,会话自己的
schemas(scopeKey) 视图里有它,而 systemPrompt.assemble({ agent, scope: agent })(agent loop 的实际
调用)返回空列表;restrict() 同样是按 scope key 求值的,所以也无法按会话隐藏工具。
实际影响:只要有至少一个项目在线,CodeGraph 工具就会出现在该进程内所有会话的工具列表里,而守卫
负责让没有索引的会话用不了它。如果 DSH 将来改为按 agent 的 scope key 组装模型工具列表,注册就可以移
回 agent.ctx,列表也就真正是按会话的了。
进程归属(不留孤儿)
所有 CodeGraph 子进程都通过 ctx.subprocess(harness 的托管子进程服务)启动,而不是裸
child_process,因此有四层清理:
- 关闭某个项目时先 SIGTERM,宽限期后 SIGKILL,并等待整个进程组消失。
- 插件自身拆卸(卸载、热重载、profile 关闭)会关闭所有存活项目。
- 子进程服务 dispose 时会终止并等待所有托管进程——harness 退出走的就是这条。
- 子进程环境里设置了
CODEGRAPH_HOST_PPID(harness 的 pid),因此即使 harness 被强杀、 完全没机会清理,CodeGraph 自带的孤儿看门狗也会退出服务。
关闭 stdin 是首选路径——实测 CodeGraph 在 stdin EOF 后几毫秒内就退出;信号只是卡死时的兜底。
诊断
| 现象 | 原因与处理 |
|---|---|
没有 codegraph 工具,日志 not-a-project |
.codegraph/ 存在但没有索引库。执行上面的 init 命令。 |
没有 codegraph 工具,日志 missing |
workspace 及其上层没有 .codegraph/,或向上走到了仓库根。给项目建索引。 |
日志 could not resolve an npm launcher |
harness 的 PATH 里没有 npx。从有 Node 的终端启动 DSH,或把 packageSpec 指向仍可拉取的规格。 |
日志 could not create the npx cache directory |
cacheDir 不可写,换一个可写目录。 |
日志里出现 manifest/下载错误 |
npm 拉不到平台包(离线、镜像缺平台包、私有 registry)。把 packageSpec 指向可达来源。 |
日志 cannot read <root>/.codegraph |
.codegraph 条目存在但不是可读目录:EACCES/EIO、断链符号链接(ENOENT)、指向非目录的链接、或普通文件(ENOTDIR)。插件会停止向上认领,而不是去服务祖先项目的索引。修好权限或链接后观察者会自动接手。 |
日志 could not register mcp__<serverName>__… |
通常是另一个 MCP 服务已占用该命名空间(serverName 非法等也可能导致注册失败),插件会静默地失去全部工具。给 codegraph-project 行换一个 serverName(或移除冲突的服务)后重开会话。 |
| 工具调用返回 "the MCP server is not responding" | 服务崩溃且重连失败。日志里有捕获的 stderr 尾部。 |
| 会话还在用旧版本 | 会话保持连接时的版本,重开会话即可。 |
| 私有 registry 需要凭据 | harness 会把凭据类环境变量从子进程中清洗掉。请改用预热的缓存目录,或 file:/tarball 形式的 packageSpec。 |
把 diagnosticTool 设为 true 会注册一个 codegraph_project_status 工具,报告:解析到的项目根、
为什么有/没有工具、每个存活项目的服务 pid,以及当前版本对应的 init 命令。
开发
npm test # 单元 + 集成测试,全部走 stub MCP 服务(不需要网络)
npm run lint # 对每个源文件执行 node --check
# 针对真实 CodeGraph 服务的手工验证(需要热缓存或网络):
node scripts/real-codegraph-e2e.mjs --version 1.6.0
# 多会话共享一个进程、中途建索引、进程回收
node scripts/stdio-probe.mjs /已建索引的项目路径 1.6.0
# 原始 NDJSON 帧、并发调用、stdin EOF 拆卸
运行时在 lib/ 下:locate.js(项目索引解析)、config.js(行配置与 settings 命名空间)、
transport.js(受管 stdio 传输)、pool.js(按项目共享与工具集同步)、agent-tools.js
(工具注册与会话守卫)、poll.js(索引观察者)、index.js(插件装配)。
测试套件用 stub MCP 服务替代真实服务,因此确定、离线;scripts/real-codegraph-e2e.mjs
才是验证真实协议、真实查询、进程共享与进程回收的脚本,需要热缓存或网络。
CI 在 Node.js 20、22、24 上跑 npm run lint 与 npm test。
发布
- 在
CHANGELOG.md里加一条## [<version>](用中文写)。发布工作流拒绝发布 changelog 里没有 记录的版本。 npm version <patch|minor|major>提交版本号并打 tag;推送提交与 tag。.github/workflows/publish.yml会跑测试、校验 tag 与package.json一致、校验 changelog 条目, 然后通过 npm trusted publishing(OIDC)发布并附 provenance 证明,仓库里不保存长期 token。
首个版本必须手动发布一次
唯一的前置条件是该包所属 npm 账号处于可用的登录状态:trusted publishing 授权的是工作流,但在有人
用 npm 凭据发布过一次之前,包并不存在,什么都创建不了。npm whoami 必须能返回账号名;token 过期时它
会以 401 Unauthorized 失败,而所有只读 npm 命令仍然正常——在怀疑包名被占用之前,先查这一点。
trusted publishing 无法创建尚不存在的包——trusted publisher 条目就配在该包自己的设置页上,
所以包必须先存在,工作流才可能被授权发布它。因此 0.1.0 由维护者在干净的 main 上手动发一次:
# 1. 在本机发布 0.1.0。`--provenance=false` 是必须的:provenance 由 CI 生成,
# 本机 publish 会拒绝签发证明。
npm publish --provenance=false --access public
# 2. 把工作流注册为 trusted publisher(需 npm 11.15+,会要求 2FA)。
# 四个值必须与工作流完全一致。
npm trust github dsh-plugin-codegraph-project \
--file publish.yml \
--repo troytse/dsh-plugin-codegraph-project \
--allow-publish -y
此后的每个版本都由工作流全自动发布并带 provenance。如果 tag 推上去后发布失败:修好提交、 移动 tag、重新推:
git tag -f v<version> && git push --force origin refs/tags/v<version>
若 GitHub 对"只更新 tag 对象"不触发新运行,就删掉远端 tag 再推一次
(git push origin :refs/tags/v<version>)。
请在普通终端里执行。若 shell 被沙箱限制为只能写项目目录(例如 workspace-write 文件策略下的 agent 会话),
npm login 与 npm publish 会因 ~/.npm/_cacache 的 EPERM 失败——那是文件策略拒绝了写入,不是 npm
缓存坏了,不需要 chown。
许可
MIT
评论
评论存放在 GitHub Discussions。用 GitHub 账号登录后可发表评论或点表情。