🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

插件开发工具箱 eighteentang/dsh-plugin-dev-tools

DeepSeek Harness
eighteentang35 天前有提交已提供中文文档扫描:含敏感能力

定位界面元素并注入开发经验与自检清单

29.7/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/9/29 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add eighteentang/dsh-plugin-dev-tools
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · dev
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
更新放缓:最近一次提交在 35 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 48 处,展示前 45 条)
  • 发起网络请求
    client.js:442
    promise = fetch(`${CONTROL_BASE}/dev-mode`, {
  • 发起网络请求
    client.js:496
    const res = await fetch(`${CONTROL_BASE}/experience`, {
  • 发起网络请求
    client.js:546
    const r1 = await fetch(`${CONTROL_BASE}/self-check`);
  • 发起网络请求
    client.js:548
    const r2 = await fetch(`${CONTROL_BASE}/experience`);
  • 发起网络请求
    client.js:552
    const r3 = await fetch(`${CONTROL_BASE}/log?lines=200`);
  • 发起网络请求
    client.js:558
    const r4 = await fetch(
  • 写入剪贴板
    client.js:1189
    await navigator.clipboard.writeText(text);
  • 执行外部命令固定命令
    app-control.js:126
    const out = execFileSync('powershell', [
  • 写入本地文件
    app-control.js:164
    appendFileSync(LOG_FILE, `${new Date().toISOString()} ${message}\n`, 'utf8');
  • 读取本地文件
    app-control.js:358
    const text = readFileSync(new URL('./templates/requirement-template.md', import.meta.url), 'utf8');
  • 执行外部命令来自配置/变量
    app-control.js:480
    const helper = spawn(process.execPath, ['-e', helperScript], { detached: true, stdio: 'ignore' });
  • 读取本地文件
    experience.js:205
    if (!existsSync(path)) {
  • 读取本地文件
    experience.js:209
    const text = readFileSync(path, 'utf8');
  • 读取本地文件
    experience.js:566
    if (!existsSync(path)) return { ok: false, error: `经验库文件不存在:${path}` };
  • 读取本地文件
    experience.js:614
    const before = readFileSync(path, 'utf8');
  • 写入本地文件
    experience.js:676
    writeFileSync(tmpPath, `${before.trimEnd()}\n${block}`, 'utf8');
  • 读取本地文件
    experience.js:689
    const body = readFileSync(tmpPath, 'utf8').replace(
  • 写入本地文件
    experience.js:693
    writeFileSync(tmpPath, body, 'utf8');
  • 写入本地文件
    experience.js:696
    copyFileSync(path, `${path}.bak`);
  • 写入本地文件
    experience.js:697
    renameSync(tmpPath, path);
  • 读取本地文件
    experience.js:700
    if (existsSync(tmpPath)) {
  • 删除本地文件
    experience.js:701
    try { rmSync(tmpPath); } catch { }
  • 读取本地文件
    self-check.js:49
    return createHash('sha256').update(readFileSync(file)).digest('hex').slice(0, 16);
  • 读取本地文件
    self-check.js:66
    if (!existsSync(full)) continue;
  • 读取本地文件
    self-check.js:67
    LOADED_SNAPSHOT.set(rel, { hash: hashOf(full), mtimeMs: statSync(full).mtimeMs });
  • 读取本地文件
    self-check.js:163
    const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
  • 读取本地文件
    self-check.js:207
    mtimeMs = statSync(full).mtimeMs;
  • 写入本地文件
    self-check.js:265
    mkdirSync(dirname(logFile), { recursive: true });
  • 写入本地文件
    self-check.js:266
    appendFileSync(logFile, `${JSON.stringify({ time: new Date().toISOString(), ...report })}\n`, 'utf8');
  • 读取本地文件
    log-channel.js:54
    if (!existsSync(file)) return;
  • 读取本地文件
    log-channel.js:55
    if (statSync(file).size < MAX_BYTES) return;
  • 删除本地文件
    log-channel.js:57
    try { rmSync(old, { force: true }); } catch { }
  • 写入本地文件
    log-channel.js:58
    renameSync(file, old);
  • 写入本地文件
    log-channel.js:72
    mkdirSync(dir, { recursive: true });
  • 写入本地文件
    log-channel.js:74
    appendFileSync(file, `${stamp()} [${level}] ${String(msg)}\n`, 'utf8');
  • 读取本地文件
    log-channel.js:87
    if (!existsSync(file)) return { ok: true, file, lines: [], total: 0, truncated: false, note: '还没有日志' };
  • 读取本地文件
    log-channel.js:88
    const raw = readFileSync(file, 'utf8');
  • 读取本地文件
    session-digest.js:193
    if (!existsSync(candidate)) continue;
  • 读取本地文件
    session-digest.js:195
    try { mtime = statSync(candidate).mtimeMs; } catch { }
  • 读取本地文件
    session-digest.js:211
    try { return readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name); }
  • 读取本地文件
    session-digest.js:228
    if (!existsSync(join(dir, '.git'))) {
  • 执行外部命令固定命令
    session-digest.js:235
    const log = execFileSync('git', args, { encoding: 'utf8', timeout: 8000, windowsHide: true });
  • 执行外部命令固定命令
    session-digest.js:240
    const status = execFileSync('git', ['-C', dir, 'status', '--porcelain'], {
  • 读取本地文件
    session-digest.js:269
    const buf = readFileSync(logPath);
  • 读取本地文件
    session-digest.js:270
    sizeKb = Math.round(statSync(logPath).size / 1024);
扫描于 2026/10/31(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 界面元素定位
  • 随插件分发经验库
  • 注入自检清单

DSH 开发工具箱:界面元素定位(Alt+悬浮)、随插件分发的开发经验库、开发模式自检清单注入

README

由 DeepSeek 最新模型翻译生成
DSH 开发工具箱 给 DSH(DeepSeek Harness) 插件开发者的一小组工具,外加一份随插件分发的开发经验库。 装一次,之后你自己开关里面的每个小工具。 它解决什么问题 开发 DSH 插件时会遇到很多文档里没写、只能踩出来的坑,比如: - 复制到剪贴板为什么静默失败 - 为什么改了插件代码、重启插件却没生效(其实是必须重启客户端) - 为什么自己建的 agent 收到消息却永远不执行 - 为什么悬浮框会疯狂闪烁 每个人各踩一遍、各花几小时,是纯浪费。 所以这个插件做三件事: 1. 界面定位 —— 按 Alt 悬浮任意界面元素,显示它的名字与完整路径,一键复制 2. 开发经验库 —— 23 条实测踩出来的坑,装插件就自动获得;而且索引会注入到 AI 助手的提示词里,让它也少犯同样的错 3. 两道关卡注入到 AI 的工作流里 ——动手前的强制自检(先查官方文档 / 查框架实现 / 找现成写法),开工前的需求对齐(四要素确认 → 规划 → 再确认才动手) 安装 前置 - 已安装 DSH 桌面客户端 - 会把插件装进你的 DSH profile 步骤(推荐:从 GitHub 直接装,一行地址) 1. 打开 DSH → 侧边栏 插件 页面 → Add plugin 2. 填入:
展开完整文档(README 剩余 1 节)
DSH 开发工具箱

给 DSH(DeepSeek Harness) 插件开发者的一小组工具,外加一份随插件分发的开发经验库。

装一次,之后你自己开关里面的每个小工具。

它解决什么问题

开发 DSH 插件时会遇到很多文档里没写、只能踩出来的坑,比如:

- 复制到剪贴板为什么静默失败
- 为什么改了插件代码、重启插件却没生效(其实是必须重启客户端)
- 为什么自己建的 agent 收到消息却永远不执行
- 为什么悬浮框会疯狂闪烁

每个人各踩一遍、各花几小时,是纯浪费。

所以这个插件做三件事:

1. 界面定位 —— 按 Alt 悬浮任意界面元素,显示它的名字与完整路径,一键复制
2. 开发经验库 —— 23 条实测踩出来的坑,装插件就自动获得;而且索引会注入到 AI 助手的提示词里,让它也少犯同样的错
3. 两道关卡注入到 AI 的工作流里 ——动手前的强制自检(先查官方文档 / 查框架实现 / 找现成写法),开工前的需求对齐(四要素确认 → 规划 → 再确认才动手)

安装

前置

- 已安装 DSH 桌面客户端
- 会把插件装进你的 DSH profile

步骤(推荐:从 GitHub 直接装,一行地址)

1. 打开 DSH → 侧边栏 插件 页面 → Add plugin
2. 填入:

github:eighteentang/dsh-plugin-dev-tools

3. 确认插件列表里出现 dsh-plugin-dev-tools 且为启用状态
4. 重启一次客户端(DSH 按文件路径缓存已加载的模块,必须重启才加载新代码)

不需要构建授权。 这个插件是纯 JavaScript,没有 build/prepare 脚本,所以 pnpm 没有脚本要跑,也就不会要求你在 pnpm-workspace.yaml 里加 allowBuilds。
(带 TypeScript 构建的插件才需要那一步。)

备选:本地路径安装

想改代码、或者网络不通时用这个:

1. 下载本仓库(Code → Download ZIP,或 git clone)
2. 把 dev-tools 这个文件夹放到一个长期保留的位置(路径里不要有中文和空格,例如 D:\dsh-plugins\dev-tools)
3. 插件页 → Add plugin → 填那个文件夹的绝对路径,例如 D:\dsh-plugins\dev-tools
4. 同样需要重启一次客户端

⚠️ 用本地路径装时,DSH 会把它记成 link: 形式 —— 这种方式不会安装该包的依赖。
本插件零依赖,所以没影响;但如果你在改它、并加了依赖,记得手动补。

确认装好了

打开 设置 → 开发工具,应该看到一个总开关和几个小工具。

用法

界面定位(Alt + 悬浮)

1. 把鼠标放到想定位的界面元素上,按一下 Alt
2. 浮框出现在光标旁,显示它属于哪个区域、完整 CSS 路径、尺寸
3. 浮框不会跟着鼠标走 —— 你可以从容地把鼠标移到「复制」按钮上
4. 也可以按 Ctrl+Alt+C 直接复制,不用瞄准按钮
5. 点浮框外面收起;再按 Alt 重新定位

复制出来的内容长这样,可以直接贴给 AI 助手或同事:

【界面定位】侧边栏底部动作区 slot: sidebar.footer.action 元素: div.dvt-controls 路径: body > div.app > div.sidebar > div.dvt-controls 尺寸: 120×26 top=720 left=12 说明: 💀 结束进程按钮渲染在这里

看到「(未收录的元素)」说明那个位置还没登记过名字 ——把复制到的路径发给我们,就能给它起个名字,以后大家都用那个叫法。

结束进程按钮(💀)

侧边栏底部的一个 💀 按钮:结束 DSH 宿主进程。

点它 → 确认 → DSH 弹出恢复框 → 在那里选「重启」(干净的自动重启,约 7 秒)或「退出」。

那个框不是报错,是正常流程 —— DSH 外壳把"宿主进程终止"一律当异常处理,所以任何结束宿主的操作都会经过它。

为什么不做成"直接重启"按钮:DSH 没有对外的重启接口。
它的重启原语是 app.relaunch() + quitWithoutConfirmation(),写在 lib/main.js 的崩溃恢复对象里,只被 fail() 调用,没有任何 IPC 暴露(42 个 preload 通道里没有 restart / quit / shutdown)。
自己 kill 进程再拉起要慢 9 倍(实测 64 秒 vs 7 秒),所以不如把选择交给官方那个框。

开发模式总开关

打开后三件事生效:

1. 注入"动手前的强制自检清单" —— AI 每次动手前要先查官方文档、查框架实现、找现成写法可抄;并且明确"搜不到不等于不存在"
2. 注入"开工前定需求"流程 —— 用户提出"我想做一个 XX"时,先对齐四要素(名字 / 用途 / 使用场景 / 工作空间),确认后才规划,规划再确认才动手
3. 注册 record_experience 工具 —— AI 踩到新坑时可以直接记进经验库

关掉后这些立即撤下(不是隐藏,是真正注销),注入的内容变成 0 字符。

注入的体量:开着时约 5.5 KB/次(自检清单 + 工作流约 1.8 KB,经验索引约 3.7 KB)。
经验库全文(约 17 KB)不进上下文,AI 需要细节时自己去读。

开发经验库

这是本插件最有价值的部分。

它在 experience/经验库.md,随插件分发 ——你装插件就自动获得。而且它的索引会被注入到 AI 助手的提示词里,所以 AI 也会知道"有这些坑存在",而不是重新踩一遍。

当前版本

打开 设置 → 开发工具,页面里会显示:

开发经验库 [⟳ 刷新]
版本 2026-09-26 条数 23 条更新于 2026-09-26 指纹 b941c37b 那个 ⟳ 是「刷新」按钮(重新读取经验库),不是重启按钮。
它存在的意义:你替换掉经验库文件后,不用重启客户端就能看到新数字。

想要新版怎么办

1. 到本仓库看 experience/经验库.md 有没有更新
2. 有就下载、直接替换掉插件文件夹里的那一份
3. 回到设置页点「⟳ 刷新」—— 不用重启客户端

怎么判断“是不是有更新”:比对设置页显示的条数与指纹。
只看日期不够 —— 有人可能只是修正了一条旧经验(条数没变,但内容变了)。

怎么贡献一条经验

踩到新坑后:

1. 在 experience/经验库.md 末尾追加一条(格式照抄现有的)
2. 更新文件顶部注释里的 entries(条数)、updatedAt(日期)、fingerprint(指纹)
3. 提 PR

格式(固定格式是为了能被脚本解析并注入提示词):

E15 · 一句话标题(写症状,不要写“要注意 XX”)

- 范围:通用 | 项目特定 —— 可选,缺省为“通用”
- 症状:你会看到什么现象
- 解法:该怎么做
- 细节:可选补充(为什么、怎么查、实测案例)

写作标准:

| 要 | 不要 |
| 写具体症状(“点复制按钮没反应,粘贴是空的”) | 写原则(“要注意剪贴板权限”) |
| 写可执行的解法(给命令、给函数名) | 写方向(“建议参考框架实现”) |
| 一条一个坑 | 一条混好几个问题 |

原则 AI 本来就知道,具体症状才能让它对上号。

标注 范围:项目特定 的条目不会被注入到别人的提示词里 ——它们只留在文件里作范例(讲的是某个具体产品的架构取舍,对别人没参考价值)。

目录结构

dev-tools/
├─ package.json 插件清单(声明“我是插件”、入口、浏览器半) ├─ cordis.patch.yml 加载器行声明 ├─ index.js 宿主半(跑在 DSH 主进程里) ├─ client.js 客户端半(跑在页面里,必须单文件、零 import) ├─ app-control.js 结束进程接口 + 经验库查询 + 诊断接口 ├─ dev-rule.js 提示词注入(自检清单)+ 开发模式状态 ├─ workflow.js “开工前定需求”那段注入文本 ├─ experience.js 经验库解析、索引生成、追加条目 ├─ experience-tool.js record_experience 工具 └─ experience/ ├─ 经验库.md ← 23 条实测踩坑(随插件分发) └─ README.md 经验库的使用与贡献说明

已知限制与设计取舍

- 结束进程会弹一次恢复框:DSH 桌面外壳把“宿主进程被终止”一律当异常。
插件侧无法消除(框架没有对外的重启接口)。所以这里索性不做“重启按钮”,只做“结束进程”,把重启/退出交给那个框选
- 经验库的项目特定条目不进提示词:这是有意的(避免占别人的 context)
- 经验索引里的“解法”只截 60 字(“症状”给 100 字):症状用来“对上号”,解法只用来判断值不值得去读全文。这样索引从 4.4 KB 降到 3.7 KB
- Config 无法被内省:因为外部插件 import 不到 asar 内部的 @deepseek-ai/schemastery,只能手写 Standard Schema,于是 Config.listConfigs 显示 status: "unsupported"。功能不受影响
- 本地路径安装是 link: 形式,不会安装依赖:本插件零依赖所以没影响

关于“为什么有些做法看起来奇怪”

源码里那些注释不是装饰。它们记录了实测踩出来的坑,比如:

- 为什么 ctx.inject(deps, cb) 不能用 ctx.get(deps) 代替
- 为什么 tools.register 的 parameters 必须是 JSON Schema 而不是简写
- 为什么开关状态放内存而不是文件
- 为什么 useEffect 的依赖数组里不能放它自己 setState 的 state
- 为什么 single 槽位可以用更低的 priority 遮蔽 shipped 的注册
- 为什么 CSS 注释里不能出现反引号、也不能让 markdown 加粗紧贴路径(前者截断模板字符串,后者凑出 */ 提前结束注释 —— 都实测踩过)

改代码前读一下注释,能省下你重新踩一遍的时间。
更详细的事故记录在 experience/经验库.md,23 条,每条都是踩出来的。

许可证

MIT
预览分享卡片长什么样 ↓eighteentang/dsh-plugin-dev-tools
分享 · DeepSeek Harness Hub
eighteentang/dsh-plugin-dev-tools

定位界面元素并注入开发经验与自检清单

回到顶部