dsh-pitfalls
English | 中文
同一个坑会反复踩:上一次花两小时查出来的根因,下一个会话就忘光了,又从零开始试。
这个插件把坑记成档案存下来 —— 现象、怎么踩进去的、怎么解决的 —— 下次撞上同类问题 先翻它。而且不用你催:同一个工具连续失败到阈值,它自己会开口。
目录
安装
dsh plugin --profile web add github:superSizzzz/dsh-pitfalls
一条命令装好。dsh 会读包里的 dsh.bundle.patch 自动并入当前 profile,
不用你手写配置。
不想要了:
dsh plugin --profile web remove dsh-pitfalls
| 项 | 要求 |
|---|---|
| dsh | ≥ 0.1.7-rc.2 |
| Node | ≥ 22.6 |
| profile | 不限,web / tui / headless 都能装 |
| 模型 | 不限。它不额外调模型,只在事件流上数失败次数 |
每个 profile 各装一次,换 profile 就换 --profile 后面的名字。
装完不用做任何事,它自己就跑起来了。想确认装上了,按「日志查看」开个日志。
本地 checkout(改代码用)
# 1. 把依赖解析接到 dsh 自己的依赖目录(建一个 node_modules junction)
node scripts/link-deps.mjs --patch
# 2. 把它打印出来的片段粘到当前 profile 的 patch 层
# 先确认自己在哪个 profile:Get-ChildItem env: | Where-Object Name -like 'DSH*'
# → ~/.dsh/profiles/<profile>/cordis.patch.yml
片段长这样:
- insert:
- id: pitfalls
name: 'file:///D:/repos/dsh-pitfalls/src/index.ts' # 换成你的 checkout 路径
config:
enabled: true
profile 默认 patchReload: live:改 patch 文件保存即热重载。但改源码不会 ——
它从 file:// 加载,URL 没变就走模块缓存,新代码得重启 dsh 才生效。想确认换上了没有,
看日志里 applied 那行的字段跟当前源码对不对得上。这条踩过的坑记在
开发与测试里。
它做三件事
| 干什么 | 谁触发 | |
|---|---|---|
| 记 | pitfall_record 把坑写成一条档案 |
模型,解决或绕过之后 |
| 查 | pitfall_search / pitfall_show 翻历史,命中的带上当时的解法 |
模型,动手之前 |
| 盯 | 同一个工具连续失败到阈值时,往会话里注入提醒(带命中历史与来源) | 插件,靠事件 |
「盯」是这套东西能真的用起来的关键:模型钻进任务里经常想不起来查、也想不起来记。 但它只在连续失败这个客观信号上出声,不替模型判断什么算坑 —— 判断权留在 模型手里,档案才不会塞满「命令打错了」这种噪声。
使用
平时它是安静的,只有真的连续失败才出声。默认阈值是同一个工具连续 2 次,中间只要 成功一次就清零。下面是它真实发出来的一段(路径和线索做了泛化):
【踩坑档案】`npm run build` 连续 2 次没成功。
线索:TS2339 Property 'createReport' does not exist | src/report/export.ts
当前项目的档案里有像的,先看能不能直接用:
- `p-20261004-164512-a3f9` |已解决 |本项目 |tsconfig 的 types 没带上
当时怎么解决的:把 @types/node 补进 compilerOptions.types,别用 triple-slash
如果这次的坑跟上面不一样(或者上面那条的解法这次不管用),解决或绕过去之后用
`pitfall_record` 记一笔新坑,必要时用 `pitfall_update` 补记旧条目。
别把这条提醒当成要汇报的事 —— 对用户说什么还说什么。
两本档案里都没有像的时,它会直说「这回可能是新问题」,并要求模型解决完记一笔 —— 不硬凑一条无关记录塞过去。这一点很要紧:给错答案比说「不知道」更糟,所以检索 宁可空手而归。
提醒是节流的:同一会话 10 分钟内不重复,单会话最多 4 条,到顶就安静等下一轮。
档案放哪
| 位置 | 作用 | |
|---|---|---|
| 项目档案 | <工作区>/.dsh/pitfalls/ |
跟着项目走。换机器、交给同事、隔半年回来,这个项目的坑都还在 |
| 全局档案 | ~/.dsh/pitfalls/ |
兜底 + 备份。临时目录里干活、项目档案丢了、别的项目踩过的坑,都从这儿捞 |
写的时候两份一起写,同一个编号 —— 不用挑地方,也不会出现两边讲两套话。 读的时候先当前工作区的项目档案,凑不满再从全局补,每条结果都标了来自哪本。
「优先」是分层的,不是按分数混合排序:项目档案的命中一律排在前面。代价是 limit 很小时(自动提醒只要 3 条)项目里的弱命中会占满名额。这是刻意的取舍 —— 优先就是 优先,宁可多看两条本项目的记录,也不要让人误以为「这个项目没人踩过」。
已经写在全局档案里的旧记录不用搬:检索会自动兜底找到它们(标为「全局」), 只在全局找到的条目补记时也留在全局,免得日后分不清它是哪个项目踩出来的。
一本档案长什么样
<工作区>/.dsh/pitfalls/ 或 ~/.dsh/pitfalls/
├── entries.jsonl 事实源:追加式,一行一条完整快照,同 id 后写覆盖前写
├── cards/<编号>.md 单条可读卡片,直接用编辑器打开就是一篇小记
└── INDEX.md 按时间倒序的索引,翻历史先看它
一条记录有这几栏(pitfall_record 的参数即对应):
- 现象
symptom当时看到什么 —— 报错原文、异常行为、和预期的差距 - 怎么踩进去的
cause根因与触发条件。这一栏决定下次能不能提前避开 - 怎么解决的
solution实际做了什么。只是绕过也要写清绕法 - 证据
evidence报错片段、命令、文件路径与行号 - 范围 / 标签
area/tags检索时权重最高,值得多花十秒填
编号形如 p-20261004-164512-a3f9,pitfall_update 补记时用它。状态只有三种:
已解决 / 已绕过 / 未解决。
四个工具
| 工具 | 什么时候用 |
|---|---|
pitfall_search |
撞上报错、构建失败、行为诡异 —— 动手之前。线索用报错关键词 + 模块/命令/文件名 |
pitfall_record |
一个问题花了力气才搞清(试错两次以上、翻过源码、换过思路),并且已经解决或绕过 |
pitfall_show |
检索结果里挑出真相关的那条,要看细节 |
pitfall_update |
后来发现了更好的解法,或当时只是绕过 —— 追加说明,别改掉原来的记录 |
「什么算坑」的判据写在工具描述里,模型照着判断;插件自己不替它决定。
配置
配置就写在装它的那段 patch 里(id: pitfalls 的 config 段)。没有单独的配置文件,
也没有设置界面。
想改默认值,在你自己的 patch 层写同 id 的条目覆盖,只写要改的字段就行:
- id: pitfalls
config:
failureStreak: 3
projectStore: false
保存即生效(patchReload: live)。字段填错了重载时会直接报错,不会悄悄退回默认值。
| 字段 | 默认值 | 含义 |
|---|---|---|
enabled |
true |
总开关。false 时不注册工具、不装监听、不注入提示词 |
autoDetect |
true |
是否自动盯工具失败。关掉只剩模型主动记录 |
failureStreak |
2 |
同一个工具连续失败几次算踩到坑。填 1 会很吵 |
matchHistory |
true |
命中历史时是否把当时的解法带进提醒 |
reminderCooldownMs |
600000 |
同一会话两次提醒的最小间隔,防连环刷屏 |
maxRemindersPerSession |
4 |
单会话提醒上限,到顶就安静等下一轮 |
systemPromptSection |
true |
是否往系统提示里写「踩坑就记、开工先查」那段规则 |
dir |
$DSH_HOME/pitfalls |
全局档案目录(同时是备份) |
projectStore |
true |
是否把记录也写进当前工作区的项目档案 |
projectDir |
.dsh/pitfalls |
项目档案目录,相对工作区;填绝对路径则所有工作区共用一处 |
backupToGlobal |
true |
写项目档案时是否在全局留一份备份 |
indexLimit |
200 |
每本档案的 INDEX.md 最多列多少条 |
logPath |
'' |
事件日志路径,留空 = 不记 |
把 projectStore 关掉就退回「只有一本全局档案」的用法。
日志查看
插件没有界面。想知道它有没有在干活,给它开个日志:
- id: pitfalls
config:
logPath: 'D:/logs/pitfalls.jsonl'
开完那个文件里会立刻多出一行装载记录,里面是这次实际生效的档案位置与开关 —— 可以拿它核对配置有没有被读进去、项目档案到底落在哪:
{"at":"2026-10-05T01:12:03.114Z","event":"applied","globalDir":"C:\\Users\\me\\.dsh\\pitfalls","projectDir":".dsh/pitfalls","backupToGlobal":true,"node":"v24.18.1","autoDetect":true,"failureStreak":2,"headless":false}
之后每次记录、检索、提醒都会追加一行:
{"at":"...","event":"record","id":"p-20261005-091530-4b1e","title":"...","sessionId":"...","scopes":["project","global"]}
{"at":"...","event":"search","sessionId":"...","query":"EPERM 命名管道","cwd":"D:/proj/app","hits":2,"fromProject":1}
{"at":"...","event":"reminder","sessionId":"...","toolName":"pwsh","count":2,"matches":1,"fromProject":1}
一行都没有,说明插件没装上,回去看看安装命令有没有报错。
权限
| 网络 | 不发任何请求 |
| 命令 | 不执行任何命令 |
| 工具 | 加 4 个 pitfall_* 工具,不动你现有的工具列表 |
| 系统提示 | 加一段「踩坑就记、开工先查」的规则,可关(systemPromptSection: false) |
| 你的消息 | 不改写、不删除 |
| 会话 | 只在连续失败时注入一条提醒,不取消轮次、不拦截步骤 |
| 磁盘 | 只在档案目录写纯文本(entries.jsonl / cards/ / INDEX.md),以及可选的 logPath |
| 模型 | 不额外调用模型,只做本地检索和计数 |
卸载
dsh plugin --profile web remove dsh-pitfalls
源码方式装的,把补丁文件里那段 - insert: 删掉就行。
已经记下的档案不会被删 —— 它们在你的工作区和 ~/.dsh/pitfalls/ 里,是纯文本,
自己留着也好,整个删掉也好。插件本身不写任何其它持久状态。
常见问题
装完没反应? 正常。它平时不出声,只在连续失败时说话。想看它有没有在跑, 按「日志查看」开个日志。
会不会拖慢会话? 不会。它不额外调模型,只是在工具结果上数失败次数、在本地检索 jsonl。
档案会不会把项目目录弄脏? 会在工作区里建一个 .dsh/pitfalls/(三个文本文件
加若干卡片)。想让它随项目进版本库、给同事看,就留着;不想提交就自己加一行
.dsh/pitfalls/ 到 .gitignore,或者把 projectStore 关掉 —— 那就只写全局那本。
两份档案不一致了怎么办? 正常情况不会:改项目那本时,备份是被同一份最终内容覆盖的, 不是把补丁重跑一遍。真被手工改乱了,以项目档案为准,把全局那条删掉重记即可。
为什么英文按整词匹配、中文按子串? no 作为子串会命中 node —— 报错文本里满地
都是这种碎片,一条无关记录就会被判成「命中历史」,然后把别的坑的解法当作这次的答案
端给模型。宁可说「档案里没有」,也不能给错答案。
subagent 踩的坑有人记吗? 提醒只投给顶层会话(subagent 的会话用完即弃, 注进去等于扔垃圾桶)。它踩的坑由主 agent 在结果里带出来。
拿不到工作区路径时会往哪写? 只写全局那本。不会瞎猜一个目录往里塞。
更多文档
开发和维护的细节没放在这里,单开了 docs/(中文):
- 工作原理:判定规则(什么时候出声)、检索怎么切词打分、 两层档案怎么配合、用到的 dsh 接口、已知限制
- 开发与测试:仓库结构、依赖怎么接、为什么改完源码要重启、 四套测试、格式检查
- 排查:日志字段表、症状到原因(没装上 / 不提醒 / 太吵 / 档案写错地方 / 搜不到)
准备动代码的话,先看开发与测试里的「热重载的真相」—— 改完源码要重启 dsh,不重启会以为改动没生效。
许可
MIT,见 LICENSE。
No comments yet. Be the first to write one.