npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@huanlin/dsh-plugin-merge-tool-calls

v0.4.4

Published

Merge consecutive same-tool calls in the DSH WebUI chat flow into one card with compact child rows (all built-in tools by default).

Readme

dsh-plugin-merge-tool-calls

把 WebUI 会话流中连续相邻的同工具调用合并为一个「主卡片 + 紧凑子行」的树状展示,减少连续读文件/搜索/编辑/执行命令时多条卡片对流的占据。默认覆盖所有内置通用行工具(read/grep/glob/edit/write/bash/pwsh/web_search/web_fetch/run_code),也可配置白名单。

合并前:                         合并后:
[Read] foo ▸ 内容                 [Read] · 3 Files  ▸(点击展开子行)
[Read] bar ▸ 内容                   └ foo(子行,点击展开 foo 的内容)
[Read] baz ▸ 内容                   └ bar(子行,点击展开 bar 的内容)
                                    └ baz(子行,点击展开 baz 的内容)

主行只显示数量摘要(Read · 3 Files),具体文件路径全部下沉到子行;子行默认折叠,点击主行以动画展开/收起。单次调用(无合并)保持原行为(主行直接显示路径并展开内容)。

纯展示层合并:会话日志、模型可见数据、详情面板均不受影响;回放确定(合并结果是聊天快照的纯函数)。

工作原理

  • 通过 keyed slot tool.call.toolview 的 shadow 机制(priority: -1,最低者渲染)接管工具的卡片;tools: [] 时接管全部内置通用行工具(见 src/client/tool-names.ts),非空列表为显式白名单(任意 wire 名均可)。
  • 组件经 useChat 读取聊天快照,在 order + node store 中按 call id 定位自身节点,前后扫描连续调用,按 maxGroupSize 分组。一组只合并同名或同家族调用:同名必合;已知家族(grep+glob、bash+pwsh、read+web_fetch)跨名合;未知工具名(variant others)仅与自身合,不相关的工具不会混进同一张卡。
  • 组首座位渲染合并卡片;组内其余座位渲染 null,由注入的样式规则 [data-chat-flow-kind="tool-call"]:has([data-slot="tool.call.toolview"]:empty) { display: none; } 将其从流中收起(与内置 .flowItem:empty 语义一致;渲染器为每个 toolview 包一层 data-slot 容器,故以该容器判空)。
  • 主卡片镜像内置 ToolRow 的行面:按 variant 渲染标题/图标(Read/Search/Bash/Write/Edit/Code/Tool call,工具自定义标题如 Pwsh/Inspect 优先)。合并组的主行摘要改为数量(Read · 5 Files / Search · 3 Queries / Bash · 4 Commands 等,按 variant 选名词),不再显示首个调用的路径;单次调用(无合并)仍显示 args 摘要(读→路径、搜索→query、bash→description/command、未知工具带 工具名 · 前缀),read/write/edit 单行的摘要是可点击文件链接。行状态、错误首行、bash 失败退出码转红点均与内置一致。
  • 子行紧凑(20px 高),结构为主行的尾部(sep 点 + 路径)。合并组内所有调用(含首个)均渲染为子行,所以每个文件路径都在子行上。对齐运行时自动测量:组件挂载后量取主行 sep 点相对卡片的偏移(并用 ResizeObserver 跟随字体/布局变化),以 --mtc-sep-left 自定义属性设置子行缩进——sep 点与主行 sep 点同列、路径与主行摘要同列,不依赖任何手工字体宽度常量,任意工具名均自动对齐。展开的子行卡片以负向 calc 抵消该缩进,左边缘与主卡片内容区对齐(占满卡片宽度,左侧不留白)。无可展开内容(如运行中的 read)的子行渲染为静态行。
  • 子行默认折叠:主行点击展开时,子行块以 grid-template-rows: 0fr → 1fr + 透明度过渡动画滑出(200ms),收起时反向滑回;折叠期间子行仍在 DOM 中(grid 0fr + overflow: hidden 收起,不卸载),所以动画平滑无闪烁。每个子行仍可独立点击展开其内容卡片。

配置

cordis.patch.yml 中的插件行 config:

| 字段 | 默认 | 说明 | |------|------|------| | tools | [](全部) | 空数组 = 合并所有内置通用行工具(read/grep/glob/edit/write/bash/pwsh/web_search/web_fetch/run_code);非空数组 = 显式白名单(任意 wire 名,如 ['read','todo_write']) | | groupBy | adjacent | adjacent:流中相邻即可合并;step:仅同 agent step | | maxGroupSize | 8 | 每组最多合并数,超出部分自动另起新组 |

开发

前置:本机有 DSH checkout(../dsh,只读,仅类型引用)。

pnpm install            # 安装 registry 依赖(react/vitest/tsdown/schemastery…)
pnpm run typecheck      # tsc --noEmit;@deepseek-ai/* 类型来自本机 DSH checkout 的 node_modules junction
pnpm test               # vitest:纯逻辑 + jsdom 组件 + 注册形态
pnpm run build          # tsdown + tsc → lib/index.js、lib/client.js、lib/types/

注意:alpha 版 DSH 未发 npm,@deepseek-ai/* peer 类型在开发期经 node_modules/@deepseek-ai/* junction 指向本机 DSH checkout(../dsh 的 vendor/cordis 与 packages/**);@deepseek-ai/* 在 client half 全部为 type-only import,测试期仅 dsh-client-ui-primitives 经 vitest.config.ts 的 alias 指向 tests/stubs/ 测试替身,组件测试无需宿主包。 Config 的 schemastery schema 使用 @deepseek-ai/schemastery(与 DSH 仓库同款,支持 z.infer);host bundle 内联 schemastery(与范本 yet-another-subagent 一致), profile 无需额外解析。

运行(挂载到 profile)

开发热更新(本地 clone,改源码重建 lib/ 即生效):

dsh plugin --profile web add link:D:\Projects\deepseek-harness\dsh-plugin-merge-tool-calls

分发安装(二选一):

dsh plugin --profile web add "github:huanlinoto/dsh-plugin-merge-tool-calls"   # 源码分发
dsh plugin --profile web add "@huanlin/dsh-plugin-merge-tool-calls"     # npm registry 分发

然后由人类重启 dsh web 进程并硬刷新浏览器(Ctrl+Shift+R)。

检查

  • pnpm run typecheck && pnpm test && pnpm run build 全绿;
  • git -C <dsh checkout> status 干净(零源码 patch);
  • 浏览器验证:连续 3 个 read 主行显示 Read · 3 Files,点击主行以动画展开 3 个 └ 子行(foo/bar/baz 各一行),再点击子行展开内容; grep/glob 同理;write/edit 子行展开显示 diff 卡片、bash/pwsh 显示终端卡片、 路径子行可点击在侧边栏预览;中间隔了其他节点的调用保持单卡片。

边界行为

  • 组内运行中(running)调用:主行显示数量摘要(如 Read · 3 Files),子行折叠时不可见,展开后各子行显示对应调用的 args 摘要路径;结果到达后原地更新。
  • error / interrupted 调用按内置语义着色(错误首行 / 警告状态点);bash 非零退出码按内置语义转为红色错误点。主行错误时摘要回退为错误首行(不显示数量)。
  • read/write/edit 家族的子行摘要是可点击文件链接(与内置行为一致,打开侧边栏预览);grep/glob 的 path 参数是搜索目录而非文件,摘要保持纯文本,不会误开目录。单次调用(无合并)的主行摘要是文件链接。
  • 组被任何其他节点打断即断开;超过 maxGroupSize 的部分另起新组(不丢调用)。
  • 非聊天节点场景(如被 dispatch 为子调用)回退为普通单行,绝不空白。
  • 带自定义行卡片的工具(skill、只读 Cordis 检查 cordis_inspect_list/cordis_inspect_query/cordis_inspect_self 的 details 行,以及摘要格式特殊的 todo_write/ask_user_question)不默认接管,保持内置行;如需合并,用 tools 白名单显式加入(合并行按通用行面渲染)。注:dsh 0.1.7-rc.1 起 tool-cordis 收窄为只读,动态 cordis_define/cordis_run/cordis_stop/cordis_undefine 与 cordis_package_inspect/cordis_runtime_inspect 已不存在。
  • 准备阶段(PreparingToolCall,参数尚未流出):渲染无参数轻量行(variant 标题/图标 + 可选原始参数前缀),不读取 argsRaw。
  • 需要浏览器支持 :has()(Chrome 105+ / Safari 15.4+ / Firefox 121+);不支持时仅退化为空行间距。

v0.3.1(适配 DSH v0.1.2-rc.1):按新版 invariant 规则不再发布空样板 ./invariant 导出(本插件无独立可分歧观察,故不发布 invariant,见 AGENTS.md 新规则)。