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

调试模式 rh666666/dsh-plugin-debug-mode

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

在代码里自动埋探针,复现一次即可定位问题

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

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

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

状态速览

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

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

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

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 63 处,展示前 40 条)
  • 发起网络请求
    lib/client.js:118
    const response = await fetch(`${ROUTE_PREFIX}/state?sessionId=${encodeURIComponent(sessionId)}`, {
  • 发起网络请求
    lib/client.js:187
    const response = await fetch(`${ROUTE_PREFIX}/${signal}`, {
  • 写入本地文件
    lib/debug-service.js:85
    await mkdir(dirname(file), { recursive: true })
  • 写入本地文件
    lib/debug-service.js:87
    await writeFile(staged, JSON.stringify(value, null, 2), 'utf8')
  • 写入本地文件
    lib/debug-service.js:88
    await rename(staged, file)
  • 读取本地文件
    lib/debug-service.js:93
    return JSON.parse(await readFile(file, 'utf8'))
  • 读取本地文件
    lib/debug-service.js:104
    const info = await stat(absolute)
  • 读取本地文件
    lib/debug-service.js:106
    return { text: await readFile(absolute, 'utf8'), bytes: info.size, truncated: false }
  • 写入本地文件
    lib/debug-service.js:277
    await mkdir(root, { recursive: true, mode: 0o700 })
  • 写入本地文件
    lib/debug-service.js:278
    await writeFile(probeFile, String(Date.now()), { encoding: 'utf8', mode: 0o600 })
  • 删除本地文件
    lib/debug-service.js:279
    await rm(probeFile, { force: true })
  • 删除本地文件
    lib/debug-service.js:450
    await rm(file, { force: true })
  • 删除本地文件
    lib/debug-service.js:483
    if (file) await rm(file, { force: true })
  • 读取本地文件
    lib/debug-service.js:545
    entries = await readdir(tasksRoot, { withFileTypes: true })
  • 写入本地文件
    lib/debug-service.js:594
    await mkdir(join(dir, 'runtime'), { recursive: true, mode: 0o700 })
  • 读取本地文件
    lib/debug-service.js:694
    return (await stat(absolute)).isFile()
  • 读取本地文件
    lib/debug-service.js:708
    await stat(absolute)
  • 删除本地文件递归
    lib/debug-service.js:1167
    await rm(this.taskAbsDir(task), { recursive: true, force: true }).catch(() => {})
  • 删除本地文件递归
    lib/debug-service.js:1980
    await rm(paths.dir, { recursive: true, force: true })
  • 读取本地文件
    lib/prompt.js:23
    return readFile(fileURLToPath(GUIDE_URL), 'utf8')
  • 读取本地文件
    lib/probe-template.js:34
    return readFile(fileURLToPath(TEMPLATE_URL), 'utf8')
  • 读取本地文件
    lib/probe-template.js:39
    return readFile(fileURLToPath(BROWSER_TEMPLATE_URL), 'utf8')
  • 读取本地文件
    lib/probe-template.js:44
    return readFile(fileURLToPath(HTTP_TEMPLATE_URL), 'utf8')
  • 执行外部命令固定命令
    lib/git.js:67
    const child = spawn('git', ['-C', root, ...args], {
  • 读取本地文件
    lib/git.js:93
    readFile(outPath, 'utf8'),
  • 读取本地文件
    lib/git.js:94
    readFile(errPath, 'utf8'),
  • 删除本地文件递归
    lib/git.js:105
    await rm(dir, { recursive: true, force: true }).catch(() => {})
  • 读取本地文件
    lib/git.js:211
    const info = await stat(absolute)
  • 读取本地文件
    lib/scan.js:30
    const info = await stat(absolute)
  • 读取本地文件
    lib/scan.js:32
    const raw = await readFile(absolute)
  • 读取本地文件
    lib/scan.js:628
    text = await readFile(absolute, 'utf8')
  • 写入本地文件
    lib/scan.js:673
    await mkdir(options.backupsDir, { recursive: true })
  • 写入本地文件
    lib/scan.js:674
    await mkdir(dirname(backup), { recursive: true })
  • 读取本地文件
    lib/scan.js:675
    await writeFile(backup, await readFile(absolute), 'utf8')
  • 写入本地文件
    lib/scan.js:675
    await writeFile(backup, await readFile(absolute), 'utf8')
  • 写入本地文件
    lib/scan.js:679
    await writeFile(staged, plan.text, 'utf8')
  • 写入本地文件
    lib/scan.js:680
    await rename(staged, absolute)
  • 写入本地文件
    lib/scan.js:703
    await mkdir(dirname(file), { recursive: true })
  • 发起网络请求
    src/client.js:108
    const response = await fetch(`${ROUTE_PREFIX}/state?sessionId=${encodeURIComponent(sessionId)}`, {
  • 发起网络请求
    src/client.js:177
    const response = await fetch(`${ROUTE_PREFIX}/${signal}`, {
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-client-ui-slots@deepseek-ai/dsh-host-webserver
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

完全由 DSH 写的 DSH 插件,用于复刻 Cursor 中的 Debug 模式。

README

由 DeepSeek 最新模型翻译生成
dsh-plugin-debug-mode github:rh666666/dsh-plugin-debug-mode · <https://github.com/rh666666/dsh-plugin-debug-mode> DSH(DeepSeek Harness)插件:Cursor Agent 式的 Debug 模式。 让 Agent 别猜——它自己在你的代码里埋探针,把现场取回来;你复现一次,然后二选一。 它解决什么问题 「点了保存之后列表偶尔不刷新。」 传统流程是 Agent 读代码 → 猜一个原因 → 改 → 你试 → 没好 → 再猜一轮。 Debug 模式把这件事反过来:先在代码里装仪表,再让现象自己说话。
展开完整文档(README 剩余 1 节)
dsh-plugin-debug-mode

github:rh666666/dsh-plugin-debug-mode · <https://github.com/rh666666/dsh-plugin-debug-mode>

DSH(DeepSeek Harness)插件:Cursor Agent 式的 Debug 模式。
让 Agent 别猜——它自己在你的代码里埋探针,把现场取回来;你复现一次,然后二选一。

它解决什么问题

「点了保存之后列表偶尔不刷新。」

传统流程是 Agent 读代码 → 猜一个原因 → 改 → 你试 → 没好 → 再猜一轮。
Debug 模式把这件事反过来:先在代码里装仪表,再让现象自己说话。

你:现象描述 └─ Agent 分析 → 在可疑路径埋探针(每处都带可摘除标记) └─ 探针把记录交给宿主,落进 <用户临时目录>/dsh-debug-mode/tasks/<hash>/<hash>.log (临时目录不可写时回退到 <你的仓库>/.dsh/debug-mode/,回退会在面板上明说) └─ 你复现一次 └─ Agent 通过「读取日志」拿到摘要(异常全量、分组计数、省略量显式) └─ 面板弹出,你选: [ 问题已解决 ] → 删日志 + 删掉本次全部探针代码 [ 问题复现,需要进一步修复 ] → 日志保留(Agent 还没读它)

整个流程不需要 Agent 运行任何命令。 它的动作只有两种:给调试服务发信号(dsh_debug_ 工具)+ 在你的源代码里埋点/改代码。「跑起来复现」是你的事。 这不是洁癖——有一次真实调试里,工作区所在的 D:\ 归 SYSTEM 所有、当前账号缺「更改权限」,于是所有 shell 命令在执行前的授权阶段就被系统拒绝了;而同一时刻编辑文件完全正常。能挡住整个流程的,恰恰是流程本来就不需要的那一步,所以现在这条被写成了硬约束。

怎么进入

只有一个入口:斜杠命令。

/debug 保存之后列表偶尔不刷新,十几分钟出现一次

敲完回车就开始了:这一条命令会连同你的描述直接开一轮对话,Agent 立刻开始分析,不需要你再补一句「开始」。描述没写全也没关系——Agent 会先问你现象、期望与复现路径。

不识别对话意图、不做工具栏按钮。埋探针是在改你的代码库,这个动作必须由你显式发起,不能由模型推断出来。dsh_debug_start 工具必须持有 /debug 签发的票据才放行——无票调用一律被宿主拒绝(这是硬校验,不是提示词里的一句叮嘱)。

四个信号

| 信号 | 谁触发 | 做什么 |
| 启动调试 | /debug 之后由 Agent 调用 | 首次进入调试模式;生成唯一哈希,创建日志、探针桩与现成代码片段。同一任务的哈希此后不变 |
| 汇报埋点 | Agent | 交卷:记录分析结论与两份清单,然后把球交回给你复现 |
| 进一步调试 | Agent | 开下一轮:这时才清空日志(哈希不变)。注意顺序——用户点「复现」时日志是保留的,Agent 必须先将 dsh_debug_read 读它,再 continue |
| 读取日志 | Agent | 把日志内容以摘要形式输出给 Agent,无需 Agent 自己去翻文件 |
| 结束调试 | Agent(由用户「已解决」驱动) | 删除任务目录,并移除本次任务埋下的全部探针与临时块 |

调试期间的代码改动

调试不只是「埋点 → 读日志」——改代码本身也是调试手段,但轮次有讲究:

- 第 1 轮默认不改代码,只取证。没看到数据之前猜的修法一旦改掉原始行为,后面的日志就再也证不出真相了。(闸门只在出现 dsh-debug:change 标记时拒绝——埋探针本身不算改行为。)
- 第 2 轮起可以「先按假设改,再埋点验证」,但改完必须再埋点,用日志证明「改之前复现、改之后不复现」。
- 探针和修复分开记账:探针用 dsh-debug:probe <hash> 包裹,临时代码用 dsh-debug:temp,改动用 dsh-debug:change <hash> 标记。收尾时只删探针与临时块,绝不回滚你的修复——整体 git checkout 回滚是被明令禁止的收尾方式。

产物放在哪

调试产物默认落用户临时目录(os.tmpdir()/dsh-debug-mode/tasks/<hash>/),
和你的工作区分开——所以它不会跟着 git status、不会被编辑器索引、也不会被当成项目文件; 每次任务结束都随任务一起删除。

- 临时目录不可写时(只读沙箱、受限账号等)自动回退到 <你的仓库>/.dsh/debug-mode/,并且会显示提示——回退同时写进任务的备注、待办清单与 dsh_debug_start 的返回正文,不会让你事后发现「日志不见了」。
- 环境变量 DSH_DEBUG_MODE_TMP 可以把临时根指到别处(工具参数 tempDir 优先级更高)。
- 代价要说清楚:因为用的是系统临时目录,系统清理临时文件时,在途任务的产物可能被删掉。那会让这个任务没法继续(日志没了、桩也没了),需要重新 /debug 开一个。

埋点入口:任何能发 HTTP 的语言都一样

写日志这件事只有宿主一份实现。 探针不需要自己格式化时间戳、自己截断、自己数上限、自己保证字段序——它只要把一条 JSON POST 过去:

GET 是自检:先确认通道活着,再让用户去复现
curl -s 'http://127.0.0.1:<DSH 端口>/dsh-debug-mode/probe?h=<hash>'

POST 写一条(只有 probe 是必填)
curl -s -X POST 'http://127.0.0.1:<DSH 端口>/dsh-debug-mode/probe?h=<hash>' \
-H 'content-type: text/plain' \
--data-binary '{"probe":"app.py:42:enter","data":{"order_id":"A1"}}'
路径固定:/dsh-debug-mode/probe,任务哈希走查询串 ?h=。
宿主地址与端口由插件自己算出来(DSH_WEB_URL,回退到 webServer.port),所以你不需要告诉智能体地址——它拿到的就是一条可以直接用的完整 URL。
只有目标不在本机时(容器里、另一台机器上)才需要把 127.0.0.1 换成可达地址, 路径与哈希一个字都不要动。

dsh_debug_start 还会把端点与哈希已经填好的现成代码片段生成到任务目录下的 runtime/probe.http.md(绝对路径在 dsh_debug_start 的返回里给出),覆盖 curl / Python / Node / Ruby / Go / PHP / PowerShell / Java —— 复制粘贴就能用。

这条通道带来两件事:

- 语言无关:字段序、带时区的时间戳、8 KiB 单行截断、16 MiB 总量上限、并发追加,全部由宿主强制。所以不存在「某个语言的桩把协议实现错了」这种失败模式。
- 可自检:GET 一眼看出通道通不通。投递是尽力而为的(探针不许拖慢被调试的程序),所以「通道是死的」与「埋点没执行到」的现象本来一模一样——现在它们可区分。

两条桩还会在加载时自己写一条 debug-mode:ready:

- 看得到 ready ⇒ 桩是通的 ⇒ 日志里没有你的探针 = 那段代码没走到;
- 看不到 ready ⇒ 桩根本没被加载 ⇒ 去查 import 路径 / / 进程重启过没有。

什么时候不走 HTTP

| 场合 | 用什么 |
| 热路径(每帧 / 每请求上千次) | HTTP + 调用方聚合(每 N 次或每 X ms 一条) |
| 进程可能硬崩,要求「崩溃前最后几条必须落盘」 | Node 文件桩(同步追加,不缓冲) |
| 运行时没有网络栈 | 按指南 §六 自写文件桩 |

浏览器页面

如果被调试的是在浏览器里打开的 HTML 页面(尤其是 file:// 直接打开的静态页),用宿主给的经典脚本桩,把记录 POST 回宿主(走 DSH 自己的 HTTP 载体,所以你不需要为了调试去起一个本地服务器):

<script src="http://127.0.0.1:<DSH 端口>/dsh-debug-mode/stub.js?h=<hash>">

dshProbe('buggy-demo.html:42:onclick', { list: todos.length })

桩由宿主即时伺服(GET /dsh-debug-mode/stub.js?h=<hash>),所以页面里没有一行指向某个本地文件——经典 不受 CORS 限制,这条通道对 file:// 打开的页面同样能用,你不需要为了调试去起一个本地服务器。若页面自己的 CSP 只允许 'self',把宿主 origin 加进 script-src,或者把落盘副本(dsh_debug_start 返回的 runtime/probe.browser.js)的内容整段粘进页面里。

记录会按同一份行协议落进任务日志(task 由宿主写死,字段序只有一处真相)。

面板:输入框上方那张可折叠的卡片

从任务创建到任务结束,输入框正上方常驻一张小卡片。它在文档流里(不是浮层),所以不会挤动输入框、也不可能挡住它;而且默认是收起来的——只有轮到你做决定时才自动展开:

▸ 调试模式 · #deadbeef · 第 2 轮 · Agent 正在埋点/排查 312 行 · 48 KB

轮到你了,它自己展开,并且只说两件事——结论一句话 + 你要做什么:

▾ 调试模式 · #deadbeef · 第 2 轮 · 轮到你复现并选择 312 行 · 48 KB

total 在 items 为空时算成了 null

请按这个顺序复现:
1. 打开保存对话框
2. 清空标题后点保存
3. 回到列表页

也可以不用按钮:直接在输入框里描述现象,同样算「还在」。
[ 问题已解决 ] [ 问题复现,需要进一步修复 ]

- 点头部那一行即可折叠/展开;折叠之后那一行仍然说清现在轮到谁。
- 那份复现清单是智能体交卷时给出来的——过去它不存在,你面对「请复现」只能凭记忆猜一遍当初怎么触发的。
- 只有等你在面板上做过选择,智能体才被允许收尾(dsh_debug_end 会校验这一点)。
- 输入框始终可用:想补充「只在切换标签页之后才复现」这种信息,直接打字就行。
输入框里有没发出去的内容时,两个按钮会自动禁用——免得同一条描述被送两遍。
- 宿主扫不动你的工作区时(只读沙箱等),面板会直接说「残留检测未生效」,而不是假装还在保护你。

收尾会发生什么

选择「问题已解决」之后,宿主会:

1. 按 dsh-debug:probe <hash> 与 dsh-debug:temp <hash> 标记逐块摘除探针与临时探索代码(摘之前先备份到任务目录的 backups/,但绝不自动还原);
2. 删除任务目录(日志、状态、探针桩一起消失);
3. 在回复里给出三类账:修复(保留)/ 探针(已删)/ 临时改动(已还原)。

两件它不会做的事:

- 不会回滚你的修复。 git checkout / git stash 整体回滚是明令禁止的收尾方式。
- 不会静默保留。 如果扫到「既不是本次探针、也不能确认是修复」的残留(陌生哈希、残缺标记、配对有歧义),收尾会暂停一次,把清单交回来,智能体会逐条问你三选一:保留 / 删除(先看 diff)/ 我自己处理。

另外:智能体可以不进调试

/debug 是你发起的,但它不是契约。如果智能体读完代码就看出根因(空指针、明显拼写错误、缺一次 await……),它应当直接告诉你根因并改代码,而不是为了走流程先埋一圈探针。票据会自己过期,你下次想调试时重新 /debug 即可。

状态

v0.19 · M0–M6 + R1–R6 + HTTP 探针通道全部落地,全部测试全绿: /debug 与票据闸门、5 个信号工具、日志管道与摘要、唤醒闭环、交互面板、收尾对账、防堆屎山机制(R1 工作区索引 + 窗口 diff、残留扫描与隔离区、停止规则、欠账看板)全部就位;浏览器/远程运行时的 HTTP 探针通道也已补齐,整条流程不依赖任何命令。

唯一还没做的是人在真实 GUI 里点一次面板——那一步只有你能做。

先读 CHANGELOG.md(只追加的变更记录)与 AGENTS.md(唯一权威入口:契约、纪律、被推翻的机制都在那儿)。

安装

从 GitHub 直装(仓库已推送;npm 上目前还没有这个包名):

dsh plugin --profile web add github:rh666666/dsh-plugin-debug-mode

另一种写法(标准 git 规范,不依赖 github: 简写):

dsh plugin --profile web add git+https://github.com/rh666666/dsh-plugin-debug-mode.git

GitHub 拉不动时,克隆到本地再挂载:

git clone https://github.com/rh666666/dsh-plugin-debug-mode.git
dsh plugin --profile web add link:<克隆下来的目录绝对路径>

安装期不需要构建,也不需要 node_modules:本插件零运行时依赖,而且 lib/client.js 是提交进 git 的生成物——仓库里已经带着它,所以 git 直装与 link: 本地挂载拿到的东西是一样的,不必先跑构建脚本(只有你改了 src/client.js 才需要重跑 scripts/build-client.mjs,见下面的「开发」)。

装完在任意会话里敲 /debug <现象描述> 即可——命令会自动出现在宿主的命令面板里。

开发

node scripts/build-client.mjs # 生成 lib/client.js(改了 src/client.js 必须重跑)
node --test test/.test.mjs # 全部单测(零依赖,会先自动重建客户端产物)

零运行时依赖:插件本体只用 Node 内置模块,因为探针桩会被复制进你的项目里执行。
浏览器半也只有一份手写的 src/client.js,由一个 30 行的构建脚本包成 DSH 要求的
window.__ModuleLoader__.load({ id, factory }) 形式——没有打包器,也没有 node_modules。

设计要点

- 日志是 JSONL,字段序固定(ts, task, probe, level, msg, data)——追加写天然多进程安全,崩溃最多丢半行,grep 依然好使。
- 探针桩永不抛异常、不持有 fd:可观测性代码不许把被调试的程序搞挂。
单任务日志到 16 MiB 就停止追加(并写一条 debug-mode:overflow),免得一个跑飞的循环把磁盘写满。
- 探针代码用 dsh-debug:probe <hash> start/end 包裹,可被机器整块摘除;临时探索用 dsh-debug:temp,修复用 dsh-debug:change。收尾时宿主会拿「工作区 diff 扫到的东西」和「你自报的清单」对账并点名漏网项—— 以扫描为准,不靠 Agent 自诚实。
- 调试日志永不长期保存:任务关闭即删除,.dsh/ 自动 gitignore。
- 面板常驻在输入框上方(conversation.input.dock,文档流兄弟节点,不会被浮层遮住输入框),按 sessionId 归属,不会串到别的会话去;默认折叠,只在轮到你复现时自动展开。
- 调试期间输入框始终可用:按钮只能表达「还在 / 好了」,而「只在切换标签页之后才复现」这种信息只有打字能说清。输入框里有没发出去的内容时,面板按钮会自动禁用,免得同一条描述被送两遍。
- 闸门不会把人卡死:任何硬闸门连续拒绝同一原因 3 次后自动降级为警告并把降级写进面板;闸门依赖的扫描能力不可用时,面板直接显示「残留检测未生效」。

License

MIT
预览分享卡片长什么样 ↓rh666666/dsh-plugin-debug-mode
分享 · DeepSeek Harness Hub
rh666666/dsh-plugin-debug-mode

在代码里自动埋探针,复现一次即可定位问题

回到顶部