DSH Hub

HuanLinOTO/dsh-plugin-merge-tool-calls

dsh-plugin-merge-tool-calls

UIWorkflow7 GitHub stars· updated 2026-08-23

Merges consecutive same-tool calls in the WebUI chat flow into a main-card + compact-child-rows tree view, all built-in generic tools by default

Install

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

Restart `dsh web` after install. Bundle APIs can change during the developer preview.

README badge

dsh-plugin-merge-tool-calls DSH Hub badge
[![DSH Hub](https://dshhub.dev/badge/dsh-plugin-merge-tool-calls.svg)](https://dshhub.dev/plugins/dsh-plugin-merge-tool-calls)

Paste this into your README. The star count updates with every catalog sync.

From the README

Excerpt from HuanLinOTO/dsh-plugin-merge-tool-calls, cleaned of badges and images.

<a href="https://dshfind.com/zh/plugins/huanlinoto/dsh-plugin-merge-tool-calls"><img src="https://dshfind.com/api/card/huanlinoto/dsh-plugin-merge-tool-calls?lang=zh" alt="dsh-plugin-merge-tool-calls card"></a>

dsh-plugin-merge-tool-calls

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

合并前:                         合并后:
[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 名均可)。
  • 组件经 useSession 读取聊天快照,用 conversationContextKey('tool-call', callId) 定位自身节点,在 chat.order 上前后扫描连续调用,按 maxGroupSize 分组。一组只合并同名或同家族调用:同名必合;已知家族(grep+globbash+pwshread+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/cordis_package_inspect/cordis_runtime_inspect);非空数组 = 显式白名单(任意 wire 名,如 ['read','todo_write']
groupByadjacentadjacent:流中相邻即可合并;step:仅同 agent step
maxGroupSize8每组最多合并数,超出部分自动另起新组

开发

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

pnpm install            # 安装 registry 依赖(react/vitest/tsdown/cordis/schemastery…)
pnpm run typecheck      # tsc --noEmit;@deepseek-ai/* 类型来自已发布的 0.1.0-rc.6 devDeps
pnpm test               # vitest:纯逻辑 + jsdom 组件 + 注册形态
pnpm run build          # tsdown + tsc → lib/index.js、lib/invariant.js、lib/client.js、lib/types/

注意:@deepseek-ai/* 是宿主提供的 peer,开发期以 devDependencies 安装已发布0.1.0-rc.6 类型(其完整依赖图已发布,可直接安装);测试期经 vitest.config.ts 的 alias 把 dsh 包指向 tests/stubs/ 测试替身,组件测试无需宿主包。 Config 的 schemastery schema 使用 @deepseek-ai/schemastery(与 DSH 仓库同款,支持 z.infer);host bundle 内联 schemastery(与范本 yet-another-subagent 一致), profile 无需额外解析。

Related plugins