Skip to content

Releases: Bcy2020/dsh-cc-ecosystem

v0.5.0 — dsh-cc-memory

Choose a tag to compare

@Bcy2020 Bcy2020 released this 24 Oct 14:16
c1b5e61

v0.5.0 — 新增 dsh-cc-memory

新包 dsh-cc-memory@0.1.0:把 Claude Code 的 auto-memory 注入 DSH。

  • MEMORY.md 索引与 pinned 正文进入系统提示,每次请求重读;索引 200 行 / 25,000 字符整行截断 + WARNING。
  • /cc-memory 模态面板,两个开关:启用 Memory(关掉后 DSH 完全看不到)与允许写入(只读 / 可写);第一个关闭时第二个置灰。
  • 工具:cc_memory_list / cc_memory_read / cc_memory_write / cc_memory_edit;可写走「写文件 → 改索引」两步法。
  • 开关持久化到 $DSH_HOME/cc-memory-state.json,改动立即生效,无需重启。
  • 全家桶 dsh-cc-ecosystem 依赖并挂载它。

协同发版:dsh-cc-loader / cc-skills / cc-agents / cc-hooks / cc-mcp / dsh-cc-ecosystem 0.5.0,cc-permissions 0.6.0,dsh-cc-memory 0.1.0。

参考了 Claude Code 的实现(CLI 2.1.275)。 Memory 说明段(Types / What NOT / How to save / When to access / Before recommending / persistence)逐字对齐;仅替换工具名、只读句与 DSH 的 {{…}} 插值约束。

安装(务必带精确版本):

dsh plugin --profile <name> add dsh-cc-ecosystem@0.5.0

v0.4.0 — 按 Claude Code 的逐组件作用域重做发现层

Choose a tag to compare

@Bcy2020 Bcy2020 released this 11 Oct 10:52
355fb15

⚠️ 破坏性变更 —— 升级前请读「破坏性变更」一节。
这一版参考 Claude Code 的实现重做了发现层,有多处会改变已有配置的含义。
协同发版:六包 0.4.0,cc-permissions 0.5.0。

根因:/mcp 面板空白,副标题还显示一个假的 C:\

我们从一开始就在实现一个 Claude Code 里不存在的概念:「项目根」(五个包共用)。它的假设是「所有 .claude 资产都从同一个根往上累积」,而实际上 ——

组件 从会话 cwd 往上读到哪里
技能 / 命令 / 子代理 到 git 根为止(家目录不含)
.claude/rules/** 整条祖先链到文件系统根(不含根本身),根在前
.mcp.json(project) 整条祖先链,近的覆盖远的
settings.json / hooks 只读 <cwd>/.claude/,不向上找
MCP 的 user / local 不在项目里 —— 在 ~/.claude.json

最后一行就是面板空白的直接原因:~/.claude.json 我们从来没读过。在发现这个问题的机器上,它顶层有 6 个 server,旧版一个都没注册。

主要改动

  • 逐组件发现(新增 scopes.js,删掉 findProjectRoot / findClaudeProjectRoot)。技能类走「有界祖先」、规则走「全链祖先」,两种走查的差异是照抄的——那是 Claude Code 自己的行为。
  • MCP 现在读它读的三个作用域:~/.claude.json 的 user / local,加上各层祖先的 .mcp.json 与插件内联。查找优先级照抄 enterprise > managed > local > project > user。
  • 面板显示来源:每条 server 点进去会显示 读自哪个作用域、哪个文件。不再显示项目根(没有那个概念了)。
  • rank 数字(150/160/170)废弃 —— Claude Code 没有数值优先级,顺序就是语义。
  • MCP 连接池化 + 并行拨号:按 (server, 配置) 复用连接,配置没变就不重拨(照抄 Claude Code 的 reconcileMcpServers);闲置 idleTimeoutMs 后自动关闭。修的是两件体感问题 —— 面板一行一行冒出来、以及换个工作区再开 /mcp 又要重来一遍。

破坏性变更

  1. projectRootMarkers 配置项从五个包里删除。 配了它的 profile 需要删掉这一项,否则配置校验会报未知字段。
  2. 项目 / 本地的 settings.json、settings.local.json 与 hooks 只从 <cwd>/.claude/ 读,不再向上找。 这是 Claude Code 的行为,但它意味着从子目录启动的会话拿不到仓库根的 settings 与 hooks。
  3. dsh-cc-mcp 的状态文件改为 <该会话的启动工作区>/.dsh/cc-mcp-state.json。旧位置里的启停决定不会自动搬过来。
  4. 配置项 globalSkillRank / globalSkillSource / skillSource / projectAgentRank / globalAgentRank / pluginAgentRank / pluginSkillRank 删除。
  5. 权限规则里前导 / 的路径锚点(Read(/secrets/**) 这类)对项目 / 本地规则改为 <cwd>。

一处「有意偏离」Claude Code

Claude Code 对 project 作用域的 MCP server 有批准闸,并且会交互式弹窗问你批不批。DSH 没有这个弹窗,照抄的结果是 .mcp.json 里声明的 server 永远不可用(面板列着、调用全被拒、无处说「可以」)。所以这里反过来:默认放行,只有 settings 里显式 disabledMcpjsonServers 才拦;enabledMcpjsonServers / enableAllProjectMcpServers 仍读取但不再决定任何事。

这是有意为之,不是 bug。

升级

dsh plugin --profile <name> add dsh-cc-ecosystem@0.4.0

务必带上版本号,并在装完后重启 DSH。升级就是在 GUI 里按新版本号再装一次 —— 不带版本号再点一次安装不会升级。

⚠️ pnpm 11 的 minimumReleaseAge(默认 24 小时)会让发布不满一天的新版本装不上,而且退出码仍是 0。对存量用户是 T+24h 生效。

完整清单见 CHANGELOG。

v0.3.4 — 规则子目录、.claude 作为项目根标记、hook 的 shell 边界

Choose a tag to compare

@Bcy2020 Bcy2020 released this 11 Oct 10:55
1ba229a

补记的历史版本。 当前版本见 v0.4.0。

⚠️ 本版的「把 .claude 当作项目根标记」已被 v0.4.0 取代 —— 那一版整体改成 Claude Code 的逐组件发现,「项目根」这个概念连同 projectRootMarkers 配置项一起删除了。本版其余改动(规则子目录、Windows hook 三处修复)不受影响。

协同发版:dsh-cc-loader / cc-skills / cc-agents / cc-hooks / cc-mcp / dsh-cc-ecosystem 0.3.4,cc-permissions 0.4.4。

规则子目录现在会被读取(#11)

discoverRules 只看顶层 .md,而规则包常按目录组织(rules/common/、rules/typescript/)。这类项目拿到的是零条规则,注入函数等于没被调用,而且一声不响:没有报错、没有日志、加载照常成功。现在整棵树都走,跳过隐藏目录与 node_modules。

一处偏离报告方案的取法:name 用相对规则目录的路径,而不是裸文件名 —— 报告人自己举的例子就是 common/style.md 与 typescript/style.md 同名,裸文件名会让两条规则在排序键上相撞、并在注入文本里渲染成两个一模一样的 ## style.md 标题。顶层文件的相对路径就是它的文件名,所以扁平规则目录的输出与之前逐字节相同。

.claude 成为默认项目根标记(#11)

projectRootMarkers 默认只有 ['.git'],于是「有 .claude/、没有 git 仓库」的项目根本解析不出项目根。现在 cc-skills / cc-agents / cc-permissions / cc-hooks 的默认值都是 ['.git', '.claude']。

findProjectRoot 现在排除家目录

报告里没提,但不补上就是拿标记换来的新缺陷:一旦 .claude 成为标记,不在任何项目里的会话会一路走上去命中 ~/.claude —— 那是 Claude Code 的全局配置目录 —— 于是把全局技能、规则、设置再当项目级加载一遍。它现在接受 { homeDir } 并跳过该目录,五个包都把家目录传了进去。

Windows 上 command hook 的三处失效(#10)

  • hook 的 shell 请求不带调用会话的沙箱策略 → 执行器拿自己的部署根去解沙箱,Windows 的 ACL 运行器直接拒绝这个 hook。现在按会话解析后随请求传递;新增 sandboxMode 配置可覆盖。
  • ctx.shell 在 Windows 上是 PowerShell,而 hook 命令是照 bash 写的 —— 首 token 是带引号的路径时("C:/Program Files/nodejs/node.exe" x.mjs),bash 读作调用,pwsh 读作字符串表达式并语法报错。ShellExecutor 不暴露自己是哪一种,所以探一次并缓存;探针只在命令首字符是引号时才跑,其余命令零开销。新增 shellDialect 配置(auto / posix / pwsh)。
  • CLAUDE_PLUGIN_ROOT 只被替换进命令文本,没有导出为环境变量 —— 真正去读这个变量的插件 hook 拿到空值。现在与 CLAUDE_PROJECT_DIR 一起导出。

测试

全仓库 356 例。此前 test/project-root.test.mjs、packages/cc-loader/test/frontmatter-lenient.test.mjs、test/hooks-executors.test.mjs 没有被 npm test 登记,一直不在 CI 里跑 —— 已补上。

完整清单见 CHANGELOG。

v0.3.3 — 非严格 YAML frontmatter 不再被丢弃

Choose a tag to compare

@Bcy2020 Bcy2020 released this 05 Oct 15:07

协同发版:dsh-cc-loader / cc-skills / cc-agents / cc-hooks / cc-mcp / dsh-cc-ecosystem 0.3.3,
cc-permissions 0.4.3。代码改动只在 dsh-cc-loader,其余六包随家族同步升版。

  • Claude Code 读得了、DSH 却整个丢弃的 frontmatter 现在能正常加载(#9) —— parseFrontmatter 一遇 yaml.parse 抛错就 return undefined,调用方只报一句 skipped: no frontmatter,于是文件在 DSH 里等于不存在。最常见的触发写法是未加引号的 description 里出现了第二个 : —— 社区 agent 文件在 <example> 块里写 Context: … / user: '…' 就会这样。实测同样会丢整个文件的还有:值换行且续行有缩进、用 Tab 缩进、同一个 key 写两遍、值以 * 开头。
    现在严格解析失败时退回逐行 key: value 读取,并把降级报进 warnings(附上 YAML 的原始报错行),不再静默。技能、斜杠命令、子代理、插件内组件共用这一个解析器,四类一起受益。
    两处实现取舍值得记住:值一律保持字符串,否则 description: 2024 会被转成数字、被 stringField 拒绝,等于拿「丢文件」换了「丢字段」;而 Claude Code 文档里本就是逗号分隔的五个字段(tools / disallowedTools / skills / allowed-tools / disallowed-tools)会按逗号切开,否则 tools: Read, Grep, Glob 会变成一个叫 "Read, Grep, Glob" 的假工具名 —— 文件救回来了,agent 却没有工具。
  • 顺带修掉同一段告警管道里的两条既有缺陷 —— 都是在接这条新告警时撞出来的,不修的话新提示要么看不见、要么看两遍:
    • loadClaude 把 mergeAgentCatalog 的返回值又 push 回它自己刚填充过的那个数组 → 每条 agent 告警打印两遍;
    • collectClaudeDir 忽略了调用方传进来的告警收集器、自己新建一个又没人合并 → skill 与 command 的告警被整个丢弃(包括既有的 skipped: name not kebab-case 之类)。
  • 仍未覆盖:整块 frontmatter 里连一行 key: value 都读不出来时,文件依旧被丢弃,提示依旧是 no frontmatter。对这类文件该措辞是准的,但对「有 frontmatter、只是 YAML 不严格」的文件曾经是误导。

测试:packages/cc-loader/test/frontmatter-lenient.test.mjs(11 例,含经 discoverAgents 与 loadClaude 的端到端;覆盖报告里的原始形状、其余几种严格 YAML 失败、逗号列表拆分、字符串不被数字化,以及「真的读不出来」仍返回 undefined)。做了变异验证 —— 把兜底退回旧行为有 6 例转红、去掉逗号拆分有 2 例转红。全仓库 296 例。

v0.3.2 — 修复 0.2.0 上四处静默失效

Choose a tag to compare

@Bcy2020 Bcy2020 released this 05 Oct 15:07

协同发版:dsh-cc-loader / cc-skills / cc-agents / cc-hooks / cc-mcp / dsh-cc-ecosystem 0.3.2,
cc-permissions 0.4.2。

这四处缺陷在 0.2.0 上都是静默失败 —— 不抛错、日志里一个字都没有、插件照常加载。全部在真机 0.2.0 profile 上复现并验证过,不只是单元测试。

  • 含方括号的权限规则让每一轮都失败(#8) —— glob 转换把任何 [...] 原样当正则字符类,于是文件名叫 [11-04-54] 的规则会生成 1-0 这种反向区间,new RegExp 抛 Range out of order in character class;而规则在每个工具调用上都要编译,所以一条坏规则拖垮整个权限闸,连无关命令一起拦。
    字符类本来就只是 path 方言的特性(文件头注释一直这么写):现在只在 path 模式里解析 [...],命令与域名模式里 [ 是普通字符;即便在 path 模式,不是合法正则的类(如 [11-04-54])也降级成字面量而不是抛错。
  • agent/session-start 在 0.2.0 被移除(#7) —— 0.2.0 把边界挪到 agent/created 并补上了 source。两代的名字不是别名:0.1.5 的 agent/created 只有 {agent},没有 source,直接改名会把 source 悄悄丢掉(而 SessionStart hook 的载荷需要它)。
    新增 packages/cc-loader/src/session-start-compat.js 统一拥有这个边界:双监听、只对带 source 的载荷动作,两代都恰好触发一次。它同时给出 0.2.0 所需的两个保证 —— 永不返回处理器的 promise、永不让抛错逃逸,因为 0.2.0 的 agent/created 是串行事件,监听器拒绝会否决会话发布。
  • 注入消息用了已退役的 V3 source(发版后真机验证才暴露)—— 0.2.0 的会话格式 v4 准入门校验每一条持久化消息的 source:kind 必须是非空字符串且不等于 'plugin',否则抛 format v4 message requires a producer-owned source kind,整轮失败。
    cc-hooks(3 处)与 cc-agents(1 处)一直在用 V3 的 {kind:'plugin', plugin:…},于是每条注入路径都是地雷 —— SessionStart 上下文、hook 注入的上下文、prompt/agent hook 结果、以及 cc-agents 的目录提醒。
    新增 packages/cc-loader/src/message-source.js 集中产出合规 source,并加了一条扫描全部包源码的漂移守卫。
  • cc-hooks 在事件边界裸解引用 agent.session(发版后真机验证才暴露)—— 0.2.0 先 announce agent、后挂 session,所以 agent.session 此时可能还是 undefined。SessionStart 监听器的第一行就抛,而兼容边界按设计吞掉它(监听器不能否决会话发布),于是处理器死在那一行、runPoint 根本没机会跑。
    表现为插件正常加载、其它 hook 点全正常,只有 SessionStart 静默全死 —— 真机会话里 SessionStart 的 hook/invoked 一条都没有。

发布流程:release.yml 末尾新增 npmmirror 同步步骤 —— 发布后逐个等待版本在官方 registry 可见,再显式触发镜像同步。v0.3.1 时镜像漏了 dsh-cc-loader(其余六个都依赖它),安装卡了四小时。

未包含:#2(Windows 上 Bash(...) 规则不覆盖 pwsh)、#9(非严格 YAML frontmatter 的 agent 被丢弃)、#10(Windows 上 command hook 不运行)、#11(rules 不递归 / .claude 不作项目根标记),均仍未修复。

v0.3.1 — 双宿主支持 + 全家桶 meta 包

Choose a tag to compare

@Bcy2020 Bcy2020 released this 05 Oct 15:07

协同发版:dsh-cc-loader / cc-skills / cc-agents / cc-hooks / cc-mcp 0.3.1,
cc-permissions 0.4.1,新增 dsh-cc-ecosystem 0.3.1。DSH 0.2.0 换掉了插件安装路径
并新增一道兼容闸,本次让全部 7 个包同时通过新旧两代宿主。

  • 新增 dsh-cc-ecosystem(全家桶):一条命令装齐 loader + 五个适配器 ——
    dsh plugin --profile <name> add dsh-cc-ecosystem(Web GUI 粘贴包名同理)。
    为什么必须单独一个包:DSH 把 dsh.profile.bundles 与 profile 的直接依赖对账,
    传递依赖不进 bundles、其 patch 永不加载 —— 装全家桶时五个适配器都是它的传递依赖,
    所以只有它自己的 patch 能挂载它们。因此它的 cordis.patch.yml 是五个适配器各自 patch 的
    副本;新增 test/umbrella.test.mjs(5)逐行比对两份配置,任何 config 漂移即失败
    (已做变异验证:改动一个值即可让该断言失败)。本包不声明 dsh-* peer —— 适配器的兼容性
    由宿主顺着 patch 里插入的行逐个读出并校验,再加一道闸只会产生第二份可能互相打架的判定。
  • 通过 0.2.0 的兼容闸:0.2.0 的安装路径新增 evaluatePluginCompatibility —— 对每个
    @deepseek-ai/dsh / dsh-* 开头的 peer,用 semver.satisfies(runtime, range, { includePrerelease: true }) 对照运行时版本,不满足即在安装前拒绝。dsh-llm 与
    dsh-subprocess 的 peer 范围由 ^0.1.5-rc.2 放宽为 ^0.1.5-rc.2 || ^0.2.0-rc.2
    (cc-skills / cc-agents / cc-hooks / cc-mcp)。这四个包此前在 0.2.0 上会报
    Plugin … is incompatible with dsh 0.2.0-rc.2: peerDependencies {…}。
    cc-permissions 与 cc-loader 不含 dsh-* peer,不受这道闸影响。
  • dsh-cc-loader 现在可作插件安装:0.2.0 的插件管理器拒绝任何未声明 dsh.bundle 的包
    (… declares no dsh.bundle)。loader 是纯库、不贡献任何插件行,因此声明一个内容为 []
    的空 patch:它通过宿主的每一道校验(dsh.bundle 须是对象、patch 须是非空字符串、解析后
    的文件须在包目录内且存在、顶层须是 YAML 数组),并激活一个不插入任何行的空层。
    dsh.bundle.patch 允许是字符串或字符串数组,空数组合法。
  • 修复 cc-hooks 在 0.2.0 上 command hook 崩溃:宿主 shell 执行器接口由 run(spec)
    变为 (await execute(spec)).result()。两版 dsh-hook-protocol 的 runHook 除这一行外
    逐字节相同,所以任何依赖版本范围都只能满足其中一代(钉 0.2.x 坏 0.1.x,钉 0.1.x 坏
    0.2.x)。cc-hooks 改为自己持有这次调用,按 execute / run 特性探测
    (src/shell-compat.js,与 loader 的 session-compat.js 同一模式);请求形状、超时、
    stdin 封装与错误语义与原实现逐字保持一致,两代宿主行为相同。新增
    test/hooks-shell-compat.test.mjs(8 用例)分别覆盖两代形状、execute 直接返回结果、
    两者皆缺与执行器抛错时的降级。

v0.3.0 — dsh-cc-mcp /mcp 管理面板(宿主 MCP 行 / 按工作区禁用 / 重连)

Choose a tag to compare

@Bcy2020 Bcy2020 released this 24 Sep 15:16

dsh-cc-mcp 的 /mcp 管理面板是本次发布的功能主体:Web GUI 内列出项目/插件 MCP 与宿主 dsh-mcp-client 行,给出状态、工具列表、Connect 重连、按工作区禁用启用与会话自检提示。

协同版本:dsh-cc-loader / dsh-cc-skills / dsh-cc-agents / dsh-cc-hooks / dsh-cc-mcp 0.3.0,dsh-cc-permissions 0.4.0。其余 5 包无代码改动,仅随家族升版并把 dsh-cc-loader 依赖范围同步为 ^0.3.0。最低 DSH 版本仍为 0.1.5-rc.2。


v0.3.0 — dsh-cc-mcp 管理面板(/mcp)

协同发版:dsh-cc-loader / cc-skills / cc-agents / cc-hooks / cc-mcp 0.3.0,
cc-permissions 0.4.0(其余 5 包无代码改动,仅随家族升版并同步 dsh-cc-loader ^0.3.0 依赖范围)。
最低 dsh 版本仍为 0.1.5-rc.2,宿主契约未变。

  • /mcp 面板(Web GUI):dsh-cc-mcp 现在为每个 MCP 服务器维护一行状态
    (ready / error / disabled / skipped / checking)与工具清单。斜杠命令 /mcp
    打开面板:列表页给出服务器 / 类型 / 状态,失败的行有 Connect 按钮(点击重连,
    仍失败则再弹一次自消失提示),已连接的行点 ✓ 可重新自检;点任意一行进入详情页,
    显示 Connected · N tools、配置来源、该服务器的工具列表,以及
    Disable/Enable 按钮 —— 禁用后该服务器的工具从模型上下文消失,新会话同样继承;
    再次点击即可恢复。
  • 宿主 MCP 行纳入面板:profile 里那些 @deepseek-ai/dsh-mcp-client 行(如 github /
    biorxiv / fetch / chrome-devtools-edge)也会列出,类型为 host,状态与工具清单取自
    宿主当前真实暴露的工具。对它们的操作按工作区生效、且不改写 profile 配置:
    • Disable = 用 tools.restrict({ deny }) 只在本工作区的各会话里隐藏该行的
      mcp__<server>__* 工具(宿主行本身不动,其他工作区不受影响),Enable 时调用
      restrict 返回的撤销器恢复;
    • Connect = 该行暴露不出工具时(它自己连接失败/仍在启动),由本插件用该行自己的
      配置
      连上去,在该会话的 scope 内注册工具把能力补回来(状态标 adopted),Disable
      即撤销这份注册。
    • 新配置 manageHostRows(默认 true)可关掉这部分。
  • 状态改为按工作区落盘:禁用/隐藏决定写在 <项目根>/.dsh/cc-mcp-state.json
    (无项目根时回落到 $DSH_HOME/cc-mcp-state.json,也可用 statePath 固定路径)。
    因此「在这个工作区禁用 github」不会影响别的项目;.mcp.json 与 Claude Code 设置依旧只读。
  • 会话开始的自检:进入会话时后台逐个连接各 MCP(与对话并行),失败的服务器以
    自动消失的提示框报出(每个失败只报一次,会话切换/重连不会重复弹)。
  • 传输:面板走插件自己在宿主 webServer 上注册的同源路由 /cc-mcp/*(与官方插件市场
    dshmarket 同一约定):POST 带 x-cc-mcp 头、校验 Origin === Host、只收 JSON、4 KiB 上限。
    不用 ctx.connection.rpc —— 实测该服务对 profile 顶层插件不可见
    (ctx.inject(['connection']) 永不触发),那条路根本挂不上路由。
  • 迟到接管(late wiring):agent/created 只覆盖插件激活后新建的会话,而宿主重启会在
    用户层插件挂载前恢复上次会话,这类会话改为按需接管:面板/命令用宿主 ctx.agents
    注册表解析活 agent,/mcp 命令直接接管调用者;接管时等待首轮自检完成,首次打开面板
    看到的是已定型的行而不是转圈。
  • 热挂载的两个坑已在代码里规避(见 src/index.js 注释与 DSHCCECO-INSTALL-SKILL.md):
    • 非 insert 的补丁条目里 name 只是断言,与目标行不同会整条跳过 —— 挂本地
      checkout 必须「- id: <原行> + disabled: true」再 - insert: 一个新行;
    • DSH 热挂载只重新 import 入口 URL,相对导入的 manage.js / register.js 会被
      Node 的 ESM 缓存留住 → 新旧混用。现在相对导入继承入口的 ?v=N,三个模块永远同批加载。
  • /mcp 宿主命令:/mcp <任意参数> 与 CLI/headless 场景输出文本版状态报告
    (/mcp 裸调用在 GUI 里打开面板);POST /cc-mcp/diag 返回路由注册状态、已接管会话
    与当前可见的 mcp__* 工具,用于支持与测试。
  • 新增浏览器半边 client/index.js:手写 classic script(懒 CJS 包装,零 require、
    零构建步骤),通过 package.json 的 dsh.client + exports["./client"] 被宿主扫描并
    按需加载。这是在 DSH 里为仓库外插件提供 Web UI 的受支持路径。
  • 新配置:enableManager(默认 true,注册 /mcp 命令与面板路由)、
    manageHostRows(默认 true)、statePath(默认空 = 按工作区)。
  • 修复:watchProject: false 时卸载 agent 会在 detachWatcher 上抛
    Cannot read properties of null(旧代码只在 undefined 上做了保护)。
  • 已知限制:插件热重载后,上一实例在会话 scope 内注册过的工具(例如项目 .mcp.json
    里已连上的服务器)可能残留到该会话被回收为止——面板会把这类行标成 skipped 并列出可见
    工具;宿主重启或开新会话即干净。这也是 skipped 的判据(该命名空间已被别的层注册)。
  • 测试:packages/cc-mcp/test/mcp-manager.test.mjs(29 例:投影/持久化/真实 stdio MCP
    端到端、失败上报、Connect 重试、禁用启用、宿主行的列出/按工作区隐藏/按需接管、
    宿主命令、卸载回收、迟到接管、路由安全边界)、
    packages/cc-mcp/test/client-bundle.test.mjs(9 例打包与传输契约)、
    packages/cc-mcp/test/client-panel.dom.test.mjs(6 例真实 DOM 面板交互);根 npm test
    已纳入(共 249 例)。

v0.2.1 — 版本策略声明(仅升级到 0.1.5 时需要,无代码改动)

Choose a tag to compare

@Bcy2020 Bcy2020 released this 19 Sep 09:16

⚠️ 只有把 DSH 升级到 0.1.5-rc.2 时才需要使用这一代版本

这是升级路径版本,不是功能版本。 升级前请先确认你要升的是哪一边:

你的 DSH 该用哪一代
0.1.5-rc.2 及以上 ✅ 用本代(v0.2.x / cc-permissions v0.3.x)
0.1.0-rc.7 ~ 0.1.1-rc.2 ⛔ 不要升级,留在 v0.1.x(cc-permissions v0.2.x)

两代互斥:本代绑定 DSH 0.1.5 的宿主契约(snapshotEvents() / eventAt()、ToolCallId、^0.1.5-rc.2 的 peer 范围),
在旧宿主上无法工作。旧宿主不满足 peer 范围,包管理器会报 unmet peer dependency,而不是等到 import 时才崩。


本补丁做了什么

v0.2.0 发布时,npm 包页面渲染的 README 里没有上面这条声明(npm 的版本内容不可覆盖),
于是 latest 指向了一个"装到旧宿主上会坏"的版本,却没有任何提示。

v0.2.1 把声明写入根 README、6 个包 README 与 CHANGELOG,使其在 npm 包页面上可见。

代码与依赖范围与 v0.2.0 完全一致,没有任何行为变化。已装 v0.2.0 的用户无需升级。

dsh-cc-loader 依赖范围仍为 ^0.2.0 —— 0.2.1 是文档补丁、互相兼容,没有理由强制升级 loader。

版本

包 版本
dsh-cc-loader 0.2.1
dsh-cc-skills 0.2.1
dsh-cc-agents 0.2.1
dsh-cc-hooks 0.2.1
dsh-cc-mcp 0.2.1
dsh-cc-permissions 0.3.1

安装

dsh plugin --profile <name> add dsh-cc-loader dsh-cc-skills dsh-cc-permissions dsh-cc-agents dsh-cc-hooks dsh-cc-mcp

dsh-cc-loader 会随依赖自动装上;单独安装时需先装它。

v0.2.0 — DSH 0.1.5-rc.2 兼容(仅升级到 0.1.5 时需要)

Choose a tag to compare

@Bcy2020 Bcy2020 released this 19 Sep 09:16

⚠️ 升级前提:宿主 DSH 必须为 0.1.5-rc.2 或更高

本版本不兼容更早的 DSH。 旧宿主(0.1.0-rc.7 ~ 0.1.1-rc.2)请留在 v0.1.x
(cc-permissions v0.2.x)—— 本版本绑定 0.1.5 的宿主契约,装到旧宿主上无法工作。
两代版本号互斥。


修复的四处宿主契约破坏

DSH 从 0.1.1-rc.2 升到 0.1.5-rc.2 后,cc-* 插件启动失败、运行时报错。
四处破坏都是逐个符号与 0.1.5-rc.2 源码比对得出的,不是凭记忆改的:

1. Session.events 已被移除(自 0.1.2-alpha.4)

在 0.1.5 上该属性是 undefined,于是:

  • [...session.events] → TypeError: agent.session.events is not iterable
  • session.events[seq] → TypeError: Cannot read properties of undefined (reading 'NN')

三个包都中招(cc-skills 的 rules 去重、cc-hooks 的 lastTurn / lastAssistantMessage、
cc-permissions 的 approval 自动应答)。

修复:在 dsh-cc-loader 新增集中兼容边界,所有日志读取统一走它 ——
sessionEvents()(优先 snapshotEvents())、sessionEventAt()(优先 eventAt(seq))、
sessionLastEvent();仅在 0.1.2-alpha.4 之前的旧宿主才回退到 session.events。

2. @deepseek-ai/dsh-llm 的 CallId 改名为 ToolCallId

0.1.5 已不导出 CallId。ESM 具名导入不存在的符号是模块求值期 SyntaxError,
所以 dsh-cc-hooks 根本 import 不进来 —— 这是最致命的一处。已改为 ToolCallId(品牌类型构造器,语义等价)。

3. SessionPersistence 公开契约没有 locate

0.1.5 的抽象服务只声明 create / open / flush / stat / list;
JSONL 后端那个 locate 是 TypeScript private(运行时存在,但不在契约内)。

旧写法 ctx.get('sessionPersistence')?.locate(h)?.path 的 ?. 只保护服务、不保护方法;
只要服务被注册(正常情况)就会去读并调用该方法,因此任何没有 locate 的 persistence provider
都会同步抛 TypeError: …locate is not a function —— 而该表达式在每个 hook payload 构造器里
急切求值,SessionStart / PreToolUse / Stop / SessionEnd 会全部失效。

修复:抽出 transcriptPath(ctx, session),typeof 判定 + try/catch,不可用时退化为 ''
(与官方桥 hooks-claude-code 的硬编码一致),可用时仍保留该字段。

4. @deepseek-ai/dsh-host-apiproxy 包已移除

排查确认本仓库从未引用,无需改动。


另外修正了依赖声明(这是"能跑但跑在旧 SDK 上"的根因)

DSH profile 使用 nodeLinker: hoisted + autoInstallPeers: false,其设计意图是让缺失的 peer
回落到宿主安装(profiles/node_modules 扁平 fallback 层),全进程共享宿主的单份 SDK 实例。

此前把 dsh-llm / dsh-subprocess / schemastery 声明为正式 dependencies,导致 profile 里
被装进一份旧 SDK 0.1.0-rc.8,插件实际跑在旧 SDK 上;且进程内存在两份 dsh-llm
(品牌类型 / 服务身份判断可能静默失效 —— 当时能跑纯属解析巧合,pnpm 一 prune 就会崩)。

  • 宿主提供的 SDK → peerDependencies(^0.1.5-rc.2 / ^3.18.1),不带自己副本
  • @deepseek-ai/dsh-hook-protocol → 保留为正式 dependencies。该包零运行时依赖
    (只有 import type,无跨模块 instanceof),私有副本完全惰性;而缺失它会让宿主启动即崩
    (ESM 具名导入失败没有优雅降级),自带副本是更安全的一侧

实测:在干净 profile 中安装后,@deepseek-ai/dsh-llm 与 schemastery 均解析到宿主的
0.1.5-rc.2,profile 内 @deepseek-ai/ 只剩 dsh-hook-protocol 一份。


验证

  • 单元测试 210 用例全绿(新增 34)。两种 Session 形状(0.1.5 的 snapshotEvents()/eventAt()
    与旧的 events 数组)均有回归覆盖,并做过变异验证:把兼容层退回"只读 session.events"后,
    5 个 0.1.5 形状用例全部失败、旧形状用例仍通过
  • 端到端:干净 profile 从 npm 装齐 6 包 → 启动零错误 → 真实对话 + 工具调用
    (tool/call → tool/result,isError:false),日志零错误签名
  • cc-skills rules 只注入一次(session 日志中 source.kind==='cc-skills' 的 user/message 计数 = 1)
  • 真实 hook payload 捕获(证明修复在线上生效):
{
  "session_id": "session-…",
  "transcript_path": "C:\\Users\\…\\session.v3.jsonl.zstd",
  "cwd": "D:\\deepseek-harness",
  "hook_event_name": "Stop",
  "stop_hook_active": false,
  "last_assistant_message": "DONE"
}

last_assistant_message 必须走 Session.events 的替代读取路径才能得到 —— 这正是原来崩溃的地方。

版本与发布顺序

包 版本
dsh-cc-loader 0.2.0
dsh-cc-skills 0.2.0
dsh-cc-agents 0.2.0
dsh-cc-hooks 0.2.0
dsh-cc-mcp 0.2.0
dsh-cc-permissions 0.3.0

dsh-cc-loader 必须先发布,其余包依赖 ^0.2.0。

v0.1.2

Choose a tag to compare

@Bcy2020 Bcy2020 released this 06 Sep 06:19

dsh-cc-ecosystem v0.1.2

Coordinated 6-package npm release.

Highlights

dsh-cc-hooks 0.1.1 -> 0.1.2 — prompt-type Stop hooks upgraded to carry the calling agent's full session context, aligning with CC official prompt-hook semantics:

  • runPromptHook now sends session.deriveMessages() as the message prefix (same projection the agent sends → provider prompt-cache prefix intact), with the hook prompt appended as the final user message. Stop-timing hooks can now see the turn's last assistant message (e.g. "edited README, did not sync hot.md") and correctly return {"ok": false} to block.
  • substituteArguments appends the hook input JSON when the template has no $ARGUMENTS placeholder (CC: "if $ARGUMENTS is not present, input JSON is appended to the prompt").
  • stopPayload / SubagentStop payload carry last_assistant_message (CC Stop input field).
  • The hook LLM prompt gains a JSON-only output contract.

Packages published (dependency order)

package version
dsh-cc-loader 0.1.2
dsh-cc-skills 0.1.2
dsh-cc-permissions 0.2.2
dsh-cc-agents 0.1.2
dsh-cc-hooks 0.1.2
dsh-cc-mcp 0.1.2

Tests

  • hooks-executors 13/13 (history-preserved + JSON-contract case)
  • hooks-stop-payload 2/2
  • full suite 170/170, syntax 24/24