DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

A-2788553793 /

A-2788553793/dsh-find

Verified

Ctrl+F full-text search across every workspace and conversation in DeepSeek Harness, executed entirely locally (no model calls).

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@911a8123

dsh-find

在 DSH 里按下 Ctrl+F(macOS 用 ⌘F),弹出全局查找面板:输入文字就遍历所有工作区、所有对话, 本地找出包含该文字的对话,命中结果分左右两栏显示 —— 左边是思考链,右边是对话文本(用户消息、 助手回复、工具输出),整段显示并高亮关键词,点击结果直接跳到那段话所在的位置。

搜索全程本地执行,不调用任何模型,不消耗 token。

面板有两个标签页:文本(就是上面这个查找界面)和 图片/视频(按工作区 → 对话逐层展开, 点开哪个对话才读取哪个对话的图片和视频,并标出哪些是用户发的、哪些是 AI 生成的)。

用法

操作 说明
Ctrl+F / ⌘F 打开查找面板。若页面上有选中的文字,会先用它作为关键词
/find-text-ui 关键词 在对话输入框里发这条指令,直接打开面板并开始查找该关键词(不经过模型)
对模型说「查找 xxx」 模型可以调用 find_text 工具拿一份文本报告(参数 query / maxChars / perSession / total)。扫描本身仍是本地零 token,但报告会进对话上下文,所以优先用上面的 UI。
输入文字 → Enter 或点蓝色「查找」按钮 手动触发查找:打字不会自动搜,避免半截关键词就扫全部对话
勾「隐藏已归档」 结果里不再列出已归档对话的命中(默认不勾,即照常显示并排在最后)
点击命中条目 跳到该段话所在的位置(面板里没有键盘选择,也没有切栏操作)
点顶部「图片/视频」标签 切到媒体页:先只列工作区,点工作区 → 点对话,才读取那个对话的图片和视频
点「一键展开」 一次读完所有对话,把全部图片和视频列出来
点「一键折叠」 收起所有工作区和已加载的对话,只留目录(不重新读取日志)
再次点已展开的对话 收起它的缩略图,再点又展开(不重新读取)
点「重新扫描」 重新列出工作区与对话,并清空已加载的缩略图
媒体页点缩略图 放大查看,视频可直接播放;Esc 或点空白处关掉
媒体页右键缩略图 弹出菜单:「另存为…」(复制到自选位置)、「打开文件所在位置」(资源管理器里选中该文件 / 打开附件目录)、「删除」(红色,需点两次确认;对话内附件不允许删除)
Esc / 点遮罩 关闭面板

为什么不提供 /find-text 文本回复命令

早期版本有一条 /find-text 关键词,把结果直接写成聊天里的一段文本。它被删掉了,原因很简单:

文本回复本身就是一次 AI 对话 —— 要消耗 token。 同一份结果,走面板是纯本地渲染、零 token; 写进对话就变成上下文,模型还要读一遍再转述,既慢又花钱。所以查找结果统一走 UI 面板 (Ctrl+F 或 /find-text-ui),聊天里不再输出查找报告。

find_text 工具仍然保留,但它的定位是「模型需要时自查」:扫描在本地、零 token,只有返回的那段报告会占上下文。

结果怎么显示

  • 面板分左右两栏:左栏标题 思考链(模型 reasoning 块里的文字),右栏标题 对话文本 (用户消息、助手输出、工具结果)。两栏同时显示、各自独立滚动,标题上显示该栏命中数; 两栏都只靠鼠标点击结果跳转,没有键盘选择,也没有切栏操作。
  • 每栏内一个工作区一张卡片,卡片之间留小间隔:卡片头是工作区名 + 该工作区命中数; 卡片内再按对话分节(对话标题 + 该对话命中数),节下面是命中段落。
  • 每条命中显示命中所在的整段(按空行分段);段落比窗口允许的长度更长时,以命中为中心截取窗口并加省略号, 保证不会撑爆面板。
  • 右栏每条命中带来源徽章:蓝字「用户」= 用户自己说的话,绿字「AI」= 助手回复,紫字「工具」= 工具执行结果, 琥珀字「系统」= 平台或插件注入的内容(长期记忆、runtime context、技能目录、复盘任务等,它们同样以 user 消息入库); 左栏整栏都是模型的思考过程,所以不再逐条标注。
  • 右栏标题旁有来源筛选:用户 / AI / 工具 / 系统 四个可点的小标签,默认全亮,点一下即隐藏该来源, 只显示还亮着的那些;筛选只影响显示,不会重新搜索,左栏不受影响。
  • 字数上限随窗口高度自适应:约 窗口高度 / 2.6,夹在 200–900 字符之间。
  • 关键词用 <mark> 高亮。
  • 已归档的对话照样会被找到:对话标题后带一个红字「已归档」标签,并且这些对话排到卡片底部、整张只含归档对话的卡片排到最底部。 归档对话在界面上不会被挂载出来,所以点它不会跳转,面板底部会给出提示;先取消归档再点即可。

图片/视频 页

切到「图片/视频」标签后,页面先不扫描任何对话,只列出工作区(约 0.8 秒):

  • 点工作区卡片头展开,看到它下面的对话列表(标题、已归档标记、读取状态)。
  • 点某个对话,才去读这一个对话的日志,把它的图片/视频铺成缩略图(约 0.2–0.3 秒); 这个对话没有图片/视频时,行尾直接显示「无」。
  • 已经加载过的对话,再点一下就能收起它的缩略图(再点又展开),不会重新读取日志; 行首的 ▾ / ▸ 表示当前是展开还是收起。
  • 右上角「一键展开」= 读全部对话、载入所有媒体(就是以前的整页wall,较慢); 「刷新列表」只重新读取工作区/对话清单,不读对话内容。

展开之后每个工作区一张卡片,卡片内按对话分块,块内是缩略图网格:

  • 每个工作区一张卡片,卡片内是网格缩略图;卡片头显示工作区名与该卡片的媒体数量。
  • 每个缩略图左上角带来源徽章:蓝「用户」= 用户发来的,绿「AI」= 助手生成或工具产出的。
  • 已归档对话的媒体照常列出,缩略图半透明并带红字「已归档」,排在卡片末尾/最底部; 勾「隐藏已归档」可以不显示它们。
  • 标题旁有「用户 / AI」两个筛选标签,默认全亮,点一下只看另一边(纯前端过滤,不重新扫描)。
  • 点缩略图放大;视频显示 ▶ 角标,点开后可以直接播放。底栏显示已加载了多少媒体、涉及多少个对话。
  • 右键缩略图弹出三项菜单:
    • 另存为… —— 弹出系统保存对话框,把这张图/这段视频复制到你选的位置(原文件不动)。 浏览器不支持保存对话框时,退化为普通下载到默认下载目录。
    • 打开文件所在位置 —— 本地文件会在资源管理器里选中该文件;对话附件没有独立文件名,打开它所在的 attachments/v1/objects/<hex 前 2 位> 目录。
    • 删除(红色字)—— 第一次点击变成「确认删除」,再点一次才真正删除磁盘上的文件,删完直接从列表里移除。 对话附件不允许删除:它属于会话日志本身,删掉会让那段对话的图片永久失效,菜单里会给出说明。

媒体是从哪来的

  • 附件:日志里 {type:'image', attachment:{attachmentId:'sha256:<hex>', mediaType, bytes, width, height, name}} 形式的块,实体文件在 $DSH_HOME/attachments/v1/objects/<hex 前 2 位>/<hex>,由 GET /dsh-find/media/attachment?sha=<hex> 流式回传。
  • 本地文件:视频从来不会变成附件块,只以路径出现在消息文本里(例如 E:/out/promo.mp4, 常被 markdown 转义成 E:////out////promo.mp4)。插件从 text 块里提取 Windows 绝对路径, 校验扩展名白名单 + 文件确实存在 + 不超过 600 MB,再由 GET /dsh-find/media/file?path=<绝对路径> 回传。
  • 同一个文件被用户和助手都提过时,算「用户」。

点击之后发生什么

  1. 切到命中所在的工作区/对话(uiWorkspace.openSession);
  2. 把会话事件加载到命中的那条消息(sessions.using + ISession.loadThrough(seq));
  3. 在页面上轮询找到这段文字的可见节点,滚动到视口中央并脉冲高亮 4 秒。

实现

  • Host 半(index.js):注册同源路由 GET /dsh-find/search、GET /dsh-find/media, 以及媒体文件回传 GET /dsh-find/media/attachment、GET /dsh-find/media/file, 以及懒加载用的 GET /dsh-find/media/tree(只列工作区/对话)、GET /dsh-find/media/session?id=(只读一个对话), 以及右键菜单用的 GET /dsh-find/media/reveal?id=、POST /dsh-find/media/delete?id= (先 GET /dsh-find/health 可探活,返回 version: 6)。 搜索用 ctx.sessionQuery:

    • listSessions() 拿到全部会话(跳过 origin === 'subagent' 的子代理会话);
    • 每个会话 filterEvents(id, [{kind:'text', text}, {kind:'type', values:[...]}]) 先用索引判断有没有命中;
    • 命中的会话再 readSession(id) 取原始事件,从 message.content 里按块拆分: {type:'reasoning'} → channel:'thinking',{type:'text'} → channel:'conversation'。 这样每条命中都带 channel,前端才能分栏(索引里的 text 是 JSON 序列化,无法区分块);
    • readTitleSnapshots() 补对话标题,ctx.workspaceRegistry.list() 用 cwd 匹配工作区名称;
    • ctx.workspaceRegistry.archivedSessionIds(登记表全局的归档集合)给每条命中打 archived 标记, 并把归档会话整体排到普通会话之后;
    • 8 路并发;客户端断开(res.on('close'))时用 AbortController 取消。
  • Client 半(client.js):只用 fetch 调上面的路由,UI 挂在 shell.overlay 槽位(id dsh-find-dialog), 键盘捕获挂在 document 的 keydown(捕获阶段,preventDefault 掉浏览器原生查找)。

  • 命令桥(/find-text-ui):命令只能在 Host 半注册(ctx.commands.register),而面板在页面里, 所以用一个长轮询把手:

    1. 命令 handler 把 {seq, query, at} 写进 Host 内存队列(publishFind)并返回一条成功提示;
    2. Client 半启动 startPendingPoll(),请求 GET /dsh-find/pending?since=<seq>;
    3. 服务端没有新请求就把这条 HTTP 请求挂起最多 20 秒,有新请求立即返回;at 超过 60 秒的旧请求不再下发,避免刷新页面时重放;
    4. 页面收到 query 就调 openPaletteWith(query) 打开面板并开始搜索。

    整个过程没有任何模型调用。

返回结构

{
  "query": "...", "tookMs": 1291, "sessionsScanned": 27, "sessionsMatched": 4,
  "counts": { "thinking": 3, "conversation": 3 },
  "truncated": false,
  "truncatedChannels": { "thinking": false, "conversation": false },
  "archivedHits": 4,
  "totalHits": 6,
  "hits": [
    { "sessionId": "...", "title": "...", "workspaceTitle": "插件", "archived": true,
      "seq": 412, "type": "assistant/message", "role": "assistant",
      "channel": "thinking",          // thinking | conversation
      "snippet": "...", "occurrences": 2,
      "clippedStart": false, "clippedEnd": true }
  ]
}

查询参数

GET /dsh-find/search?q=<关键词>&maxChars=420&perSession=3&total=150

参数 默认 范围 含义
q — ≤ 200 字符 关键词(空则返回空结果)
maxChars 420 120–2000 每条命中片段的字数上限
perSession 3 1–12 每个对话每栏最多展示几处命中
total 150 1–400 每栏命中上限(超过则 truncated: true)

媒体接口

GET /dsh-find/media?total=400 会逐个会话 readSession() 扫一遍媒体,返回:

{
  "tookMs": 3674, "sessionsScanned": 32, "sessionsMatched": 16,
  "total": 282, "truncated": false,
  "counts": { "user": 17, "assistant": 265, "image": 277, "video": 5 },
  "items": [
    { "sessionId": "...", "title": "...", "workspaceTitle": "插件", "archived": false,
      "kind": "image",            // image | video
      "speaker": "user",          // user | assistant
      "source": "attachment",     // attachment = 日志里的 image 块;file = 文本里的本地路径
      "name": "screenshot.png", "mediaType": "image/png", "bytes": 66356,
      "width": 1280, "height": 720,
      "seq": 42, "type": "user/message",
      "url": "/dsh-find/media/attachment?sha=<hex>" }
  ]
}

GET /dsh-find/media/file?path=<绝对路径> 与 GET /dsh-find/media/attachment?sha=<hex> 只接受媒体扩展名 / 64 位 hex,文件不存在返回 404,超过 600 MB 返回 413。

安装

先从 GitHub 取一份源码(或直接用本地工作副本):

git clone https://github.com/A-2788553793/dsh-find.git

再把它以 source link 方式装进当前 profile:

plugin_manager install_bundle  <克隆下来的 dsh-find 目录>

# 改过代码要重装:先移除再安装(重复 install 会报 already-installed)
plugin_manager remove_bundle  dsh-find
plugin_manager install_bundle  <克隆下来的 dsh-find 目录>

装/重装后刷新一次页面(Client 半的 JS 由 bundle 提供)。

已知边界

  • 只在会话事件里搜(用户消息、助手消息、工具结果),不含压缩摘要之外的隐藏状态。
  • 工具调用的参数({type:'tool-call'} 块)不参与匹配,避免搜到一堆命令噪音。
  • 命中定位依赖文字真的出现在页面上;若目标对话被虚拟列表折叠得很远,loadThrough 之后仍需滚动加载, 轮询 7 秒仍未找到时只完成跳转、不高亮。
  • 已归档的对话能搜到但打不开(界面不挂载它),点击只在面板底部给提示、不跳转;它们也总排在结果最后。
  • 面板本身是 shell.overlay 的常驻占用者,未打开时渲染 null。
  • 媒体页只认本机路径:远程 URL 形式的图片(网页截图里的 https://…png)不会列出, 工具回显的网页图片仍会以附件形式出现。
  • 媒体扫描要读全部会话日志,第一次切换约 3–5 秒;结果缓存在面板里,切换标签不会重扫。

更新日志

0.3.9

  • 删除斜杠命令 /find-text(文本回复版)。原因:文本回复本身就是一次 AI 对话,要消耗 token; 查找结果改为统一走 UI 面板(Ctrl+F / /find-text-ui),纯本地渲染、零 token。
  • 移除 0.3.8 为它加的自定义命令卡片渲染(conversation.chat.commandview 里的 find-text 槽位),命令只剩 /find-text-ui 一条。
  • find_text 工具保留:扫描零 token,只有返回的报告会进上下文,供模型在需要时自查。

0.3.8

  • /find-text 关键词 的结果改用自己的聊天卡片渲染(接管 conversation.chat.commandview 里 find-text 这一格):工作区一节、对话一张小卡、每条命中一行,带来源徽章和正常行高,不再是挤成一行的小灰字 + 可展开的 <pre>。默认展开,点标题行可收起。
  • 报告首行改短(只放「找到 N 处、涉及 M 个对话」),扫描耗时与截断提示挪到第二行。

0.3.7

  • 新增 agent 工具 find_text:模型可以自己调用本地索引,参数 query / maxChars / perSession / total,返回按工作区、对话分组的文本报告(含思考链 / 对话文本、说话人、seq、是否归档)。
  • 新增斜杠命令 /find-text 关键词:同样的文本报告,直接在对话里回复;/find-text-ui 仍然负责打开窗口。
  • 文本报告默认更紧凑:每条片段 220 字符、每个对话每栏 2 条、总共 40 条。

0.3.6

  • 斜杠命令 /find-text 改名为 /find-text-ui(避免和别处注册的同名命令冲突)。旧的 /find-text 不再注册。

0.3.5

  • 图片/视频页:「一键展开」和「重新扫描」之间新增「一键折叠」,一次收起所有工作区与已加载的对话。

0.3.4

  • 图片/视频页:已加载的对话可以点击收起/展开缩略图(行首 ▾ / ▸ 指示状态),收起不重新读日志。

0.3.3

  • 修复媒体页右键菜单失效(0.3.2 重构时误删了菜单相关函数,菜单打不开、两项都点了没反应)。
  • 右键菜单新增**「另存为…」**:用系统保存对话框把文件复制到自选位置;不支持时退化为普通下载。

0.3.2

  • 图片/视频页改成懒加载:打开页面只列工作区,点工作区才展开对话、点对话才读取那一个对话的媒体, 没有媒体时显示「无」;新增「一键展开」按钮(等价于旧版的整页扫描)。
  • 新增路由 GET /dsh-find/media/tree 与 GET /dsh-find/media/session?id=;/health 的 version 升到 6。

0.3.1

  • 媒体页缩略图支持右键菜单:「打开文件所在位置」(资源管理器里选中文件 / 打开附件目录)与「删除」(红字,二次确认)。
  • 删除只允许作用于插件这次扫描真实列出过的本地文件(按 id 白名单,路径不来自浏览器);对话附件拒绝删除,避免破坏会话历史。

0.3.0

  • 新增标签页:面板顶部多了「文本」与「图片/视频」两个页签,Ctrl+F 仍默认打开「文本」。
  • 新增图片/视频页:把本机所有对话里出现过的图片和视频列成缩略图墙,按工作区卡片分组, 每个缩略图标出是「用户」发的还是「AI」生成的;支持「只看用户 / 只看 AI」与「隐藏已归档」, 点缩略图放大(视频可直接播放),头部按钮区可重新扫描。
  • 新增路由 GET /dsh-find/media、GET /dsh-find/media/attachment?sha=、GET /dsh-find/media/file?path=; /health 的 version 升到 4。

0.2.0

  • 新增来源标注:右栏每条命中带徽章 —— 蓝「用户」、绿「AI」、紫「工具」、琥珀「系统」。 系统注入(长期记忆、runtime context、技能目录、复盘任务等)同样以 user 消息入库, 这里按消息的 source.kind 判定,只有真正由人输入的消息才算「用户」。
  • 新增来源筛选:右栏标题旁「用户 / AI / 工具 / 系统」四个标签,默认全亮,点一下隐藏该来源; 纯前端过滤,不重新搜索,左栏思考链不受影响。
  • 变更:徽章文案「你」改为「用户」;AI 来源从灰色改成绿色,四种来源各有一种颜色。

0.1.0

  • 首个版本:Ctrl+F 全局查找面板、思考链 / 对话文本双栏、整段展示与高亮、点击跳转定位、 卡片式工作区分组、已归档对话标注与置底、/find-text-ui 命令、隐藏已归档开关、手动「查找」按钮。
—/ 5

No ratings yet

Verified DSH bundle

Commit 911a81233608

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout