dsh-incident-journal
五个技能维护一本可复习的工程事故日志:写不出反事实就不记,从已有条目出题自测,体检条目腐化,按出处收外部资料,并能在改目录名的同时改掉全库引用。
安装
dsh plugin --profile web add dsh-incident-journal
沙箱测试结果
| 阶段 | 结果 |
|---|---|
| 1 安装 | ✓ |
| 2 列装 | ✓ |
| 3 挂载 | ✓ |
| 4 启动 | — |
四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。
README
dsh-incident-journal
给 DeepSeek Harness 用的工程日记:把你踩过的坑、留下的技术点、认下的设计立场写成可复习、可审计的条目,而不是让它烂在聊天记录里。
五个 skill:记录(incident-journal)、复习(incident-review)、体检(incident-audit)、资料(incident-resource)、布局(incident-layout)。
它解决什么问题
大多数人的「踩坑笔记」死于三件事:
1. 写的是流水账 —— 只有「我做了什么」,没有「我原来以为什么」。三个月后重读,学不到东西。
2. 只写不复习 —— 记了一百条,一条没再看。没有复习的笔记等于没写。
3. 只装得下坑 —— 值得留下的技术点和设计立场无处可放,最后都流失了。
第三条靠把条目分成三类解决,前两条靠一条铁律:
kind · 收什么 · 硬筛子(反事实)
坑 · 一个被推翻的判断 · 我原来以为什么,凭什么
技术点 · 一个值得留下的做法 · 没有它我会怎么做,错在哪
思想风格 · 一个设计立场 · 它在什么约束下是对的,换什么条件不成立
每条必须能写出反事实。 写不出来的不收 —— 「今天我学了 Redis」写不出「没有它会怎样」,「他们用了分层架构」写不出「什么条件下它会错」。没有反事实的条目是摘抄,不是条目。
外加一条可迁移判据的要求:如果一条经验只对当时的那个报错有用,它不该进这个库。
五个 skill
skill · 触发时机 · 干什么
incident-journal · 「开始记录」/「停止记录」/「落盘」,或刚发生一件值得留的事 · 检查开关,判断够格,按骨架写条目并同步四处索引
incident-review · 「考考我」「复习」 · 从库里挑 5–8 条出题,按 kind 换角度考,优先考最可能忘的
incident-resource · /incident-resource <文件>、「把这份面经收进资料」 · 先要出处(要不到就拒收),查重后加厚或新建 资料/<slug>.md,更新 资料/README.md 索引,最后只报不改地指出它和哪些条目对得上
incident-audit · 「审一遍笔记」 · 扫全库找退化:索引断链或编号没人声明、缺反事实、[资料] 被写成事实、位置不可校验、同一判据反复出现、三类失衡、该记没记
incident-layout · 「把坑改名成 trouble」「目录叫什么我自己定」 · 改库里的目录名:动目录(git mv)、写 <库根>/.layout.yml 覆盖表、把库里所有引用一起改掉;不带参数时只报当前布局
安装
方式 A:零安装(推荐先试)
skill 就是普通目录。把本仓库 skills/ 下的五个目录复制到 Harness 的用户技能目录:
mkdir -p ~/.dsh/skills cp -r skills/incident-journal skills/incident-review skills/incident-audit skills/incident-resource skills/incident-layout ~/.dsh/skills/
DSH 的 filesystem skill provider 会自动发现 ~/.dsh/skills/<name>/SKILL.md,不需要装插件、不需要重启(改 SKILL.md 会热重载)。
Windows 对应目录:C:\Users\<你>\.dsh\skills\。
方式 B:作为插件包(会真正「注入」到 profile)
方式 A 是「DSH 自己去技能目录里发现」,方式 B 是「这个包自己把五个 skill 注册进 ctx.skills」。后者的意义是分发:换台机器装一次这个包,五个 skill 一起到位,不用逐个目录复制。
这个包本身零运行期依赖(只用 Node 内置模块),已发布到公共 npm:
包名 · dsh-incident-journal
dist-tag · latest
页面 · <https://www.npmjs.com/package/dsh-incident-journal>
源码 · <https://github.com/qw2007/dsh-incident-journal>
先在一个空目录里确认它装得出来(19 个文件,含 skills/ 下全部 references):
npm i dsh-incident-journal
然后让 Agent 把它装进当前 profile —— 用 plugin_manager 的 install_bundle,target 传包名 dsh-incident-journal@latest。如果它只接受本地目录(我实际跑通过的是 tarball 与目录两条路,直接传包名这一步还没实测),就先取 tarball 再传绝对路径:
npm pack dsh-incident-journal@latest
要改代码就 git clone 那个源码仓库 —— 它现在是公开的;只是装来用则完全不需要。
不要手改 profile 的package.json或cordis.patch.yml。 那两步由install_bundle完成;手写的每一处都要单独授权,而且很容易写漂。你只需要给它一个目录。
装进去的三个文件各自干什么:
文件 · 作用
lib/index.js · Cordis provider:name = 'dsh-incident-journal'、inject = ['skills'],在 apply(ctx) 里调 ctx.skills.registerProvider(),把 skills/ 下的四个 SKILL.md 交给 ctx.skills;返回的 resourceBase 是目录形式,所以 SKILL.md 里可以用相对路径引用 references/…
dsh.plugin.json · 插件清单:contributes.skills 列出四个技能名,engines.dsh 声明最低版本
cordis.patch.yml · 一行 loader 配置(insert: [{ id, name }])—— install_bundle 把它并进 profile 的 composition
代价,按会不会影响你的程度排序:每个 plugin_manager 动作都要一次授权;改动影响该 profile 下所有会话;启动期 profile 要重启一次才生效。只想自己用就走方式 A,这些代价一个都不会遇到。
如果你同时留着方式 A 的副本,副本会赢。 skill 注册表把候选按 rank 升序排序后取同名里的第一个(dsh-skill/lib/index.js:519 的 compareIndexedCandidates,用在 :314-322),所以数字越小优先级越高:
项目目录 100/200 < 运行时 250 < 自定义目录 300
< ~/.dsh/skills 400 < ~/.agents/skills 500
< 本包 550 < dsh 自带 600
也就是说,装了这个包之后,~/.dsh/skills/incident-journal 仍然是生效的那个,包里那份被静默忽略,只在日志里留一行 skill "incident-journal" from custom ignored because a higher-priority skill already exists。这条分层是有意的:你自己的副本 > 第三方包 > dsh 自带。所以用方式 B 就不要再复制 skills/。
想核对包没被篡改:npm view dsh-incident-journal dist.shasum会打印当前版本的 tarball SHA-1,和本地npm pack出来的.tgz对一下即可。这里不写死哈希 —— 每次发版都会变。
自检(不需要装任何东西):
node scripts/verify-provider.mjs
它会走一遍 list() / get(),并检查每个 SKILL.md 里用反引号引用的 references/... 路径是否真的存在,外加一条:frontmatter 是不是 YAML 安全的。单行 description: 里出现 ": " 会让宿主静默丢弃整个技能 —— 这个坑真踩过,所以现在有机器检查。
用起来
1. 建库、指定库、启动
库的默认目录结构:
mkdir -p my-journal/资料 my-journal/{技术点,思想风格} my-journal/my-project/坑
my-journal/
.layout.yml 目录改名后的覆盖表(可选,没改过就没有)
资料/ 外部参考资料(面经、文档)—— 用 `/incident-resource` 收进来
技术点/ 跨项目共享 —— 跟人走
思想风格/ 跨项目共享 —— 跟人走
my-project/
README.md 该项目的条目清单(只存链接,不存内容)
坑/ 只属于这个项目
README.md career.log REVIEW.md
坑 跟项目走,技术点 / 思想风格 跟人走。 后两类不属于任何一个项目,所以放在一级目录,靠条目里的 projects: 标签关联项目 —— 那是条目与项目之间唯一的关联。<项目名>/README.md 只存链接,让你进到项目目录就能看全三类;它不复制内容,所以不会出现「指针和正文不一致」。这个文件由写条目那一步顺带更新。
目录名可以改。 上面这四个是默认名。说一句「把坑改成 trouble」,incident-layout 会把每个项目下的 坑/ 一起改名、写 <库根>/.layout.yml 记下这次覆盖、再把库里所有引用一起改掉。覆盖表放在库根里而不是机器配置里 —— 它是库的一部分,git clone 到新机器名字还在。改回去就说「把 trouble 改回坑」。别手改目录名,链接会一起断。
跟 Agent 说「开始记录」,它会问一次库的绝对路径(只问这一次),写进 ~/.dsh/incident-journal.yml:
journal_root: /abs/path/to/my-journal active_workspaces: - /abs/path/to/the/project/you/work/in
之后在每个工作区里说一次「开始记录」,那个工作区就一直记,直到你说「停止记录」—— 跨会话有效。
没启动的时候什么都不会记,但也不会静默:如果那一刻本来该记,Agent 会回一句「(journal 未启动,这次没记)」。看不到这句提示,就是真的没发生值得记的事。
2. 记一条
跟 Agent 说「把刚才那个坑记下来」。它会:
1. 检查开关状态;
2. 按三类各自的反事实筛子判断够不够格;
3. 先 grep 库查重 —— 命中、且这次的新判断是原条目没顾到的,就加厚它,不新建;
4. 拿关键词 grep 一遍 资料/,命中就加一节 ## 与资料的关联;
5. 按 [skills/incident-journal/references/skeleton.md](skills/incident-journal/references/skeleton.md) 的骨架落一条;
6. 同步四处索引(根 README.md / career.log / REVIEW.md / <项目名>/README.md)。
库在工作区之外时,条目先写到 <工作区>/.incident-journal-staging/,你说「落盘」时一次搬完 —— 暂存目录是库根的影子,相对路径一一对应,授权按调用计、不按文件计,所以搬 20 个和搬 1 个一样贵。要动的索引也先复制进暂存目录里改好,落盘时随条目一起盖回去。
加厚最容易失效,所以它被写成了一个硬动作。 不先 grep,你就只有想起来「我记过」的时候才会去加厚 —— 而想得起来的时候,你本来就不需要这条规则。
情况 · 动作
同主题条目已有,新判断是它没顾到的 · 加厚:补进对应那一节、补 projects:,不新建
…(truncated)
以上内容来自 npm 上的 README,我们只做格式化,不改内容。
版本
npm 上共发布:7,最后更新 2026-10-21