Files

169 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DSH(DeepSeek Harness)集成
本文是 rev-skills 与 DSH(DeepSeek Harness)集成的长期参考:DSH 在本库语境里是什么、两条集成路线的差别、DSH 强制执行的兼容规则、安装与卸载命令、预设选择方式、验证方法、排错表,以及维护时必须成对同步的文件。
命令行实现见 `bin/dsh.mjs` 与 `bin/install.mjs`;bundle 的补丁本体是仓库根的 `dsh.patch.yml`。
## 1. DSH 在本库语境里是什么
DSH 是一个宿主(host),它按 **Agent Skills 风格**扫描技能目录:每个被扫描根目录下识别 `<root>/<name>/SKILL.md` 或 `<root>/<name>.md`。本库的 `.claude/skills/` 就是这样一个目录,因此内容不需要转换即可被 DSH 读取——需要处理的只是「放到哪个根目录」与「用哪个预设看到它」。
两条能力需要分开理解:
- **技能发现**(`@deepseek-ai/dsh-skill-filesystem`):扫描根目录 → 解析 frontmatter → 把技能注册进当前预设的可用技能列表。
- **预设(agent preset)**:`@deepseek-ai/dsh-agent-preset` 的一行配置,决定该预设挂载哪些插件、看到哪些技能根。预设把「技能目录」和「agent 的工具与提示词」绑定在一起。
DSH 的默认技能根与优先级(数字小者先匹配,同名技能以先命中者为准):
| 优先级 | 根目录 | 说明 |
|---|---|---|
| 100 | `<projectRoot>/.dsh/skills` | 项目级 DSH 技能 |
| 200 | `<projectRoot>/.agents/skills` | 项目级通用 agent 技能 |
| 300 | `customSkillDirs` | 由预设显式声明的目录(本库预设用这一档) |
| 400 | `<dshHome>/skills` | 全局 DSH 技能(Windows 默认 `C:\Users\<用户>\.dsh\skills`) |
| 500 | `<agentsHome>/skills` | 全局通用 agent 技能 |
| 600 | `bundledSkillDir` | DSH 自带技能 |
本机可用 `$env:DSH_HOME`(Windows)确认第 400 档的实际位置。
## 2. 两条集成路线
两条路线不互斥,取哪条取决于「要不要一个只带逆向技能层的固定 agent 配置」。
### 路线一:装进 DSH 技能根(所有预设可见)
把技能拷到第 100 / 400 档,**任何预设**都能看到这 122 个技能,不需要 bundle:
```bash
npx rev-skills install --target dsh # 交互式选择 global / project
npx rev-skills install --target dsh --global # → <dshHome>/skills
npx rev-skills install --target dsh --project # → .dsh/skills
npx rev-skills install --target dsh --dry-run # 只看计划
npx rev-skills uninstall --target dsh --global
```
适用场景:只想让技能在任意会话里可用,不需要独立的 agent 配置。代价是技能与预设的工具集、上下文预算混在一起。
### 路线二:独立预设(自带技能层)
本仓库的**根包自身即 bundle**(包名 `rev-skills`):根 `package.json` 里的 `dsh.bundle.patch` 指向仓库根 `dsh.patch.yml`,该补丁插入一行预设 `preset-rev-skills`。不需要第二个包,也就没有第二处版本号要同步。
- 预设显示 id(`config.id`)为 `rev-skills`,显示名「逆向工程(rev-skills)」,`order` 为 40;
- 该预设自带的 `skill-filesystem` 实例用 `includeDefaultRoots: false`,即**该实例自身**不声明第 100–600 档的根,技能根完全由 `customSkillDirs` 指定;但 DSH 读取技能时会把这个预设层与全局层合并,所以项目/用户技能仍与会话可见(同名条目以更近的预设层为准),其他预设不受影响;
- bundle 自身不带技能副本,技能始终从仓库的 `.claude/skills/` 读取,因此技能增删改不需要重新发布 bundle。
`customSkillDirs` 的解析顺序(取第一个通过校验的候选;校验 = 目录存在且其中至少有一个 `re-` 开头的条目):
| 顺序 | 候选 | 说明 |
|---|---|---|
| 1 | `REV_SKILLS_DIR` | 环境变量,优先级最高;取值是仓库根目录时解析为 `<dir>/.claude/skills`,取值是技能目录本身时原样使用 |
| 2 | `<DSH_HOME>/skills` | 默认 `~/.dsh/skills`(Windows `%USERPROFILE%\.dsh\skills`)——即「路线一」装进去的位置 |
| 3 | `<rev-skills 包目录>/.claude/skills` | 按包名解析;本包即 bundle,装进 profile 的 `node_modules` 时命中 |
候选都不可用时回落到第 2 项;目录暂缺不报错(提供方会持续探测)。
按 DSH `skills` 服务的实际契约,预设作用域的提供方与全局层是**合并**关系而非替换:同名技能以更近的层胜出。因此该预设是把 rev-skills 技能根作为自己的一层加入,用户项目与家目录下的技能仍可见;`includeDefaultRoots: false` 只影响这个预设实例自身的根列表。
**前置要求**:宿主需提供两个包——`@deepseek-ai/dsh-agent-preset`(agent preset 声明能力)与 `@deepseek-ai/dsh-skill-filesystem`(本地技能目录发现能力),官方发行版自带。bundle 自身零运行时依赖,不需要为它单独跑安装。
```bash
node bin/dsh.mjs check [--dir <skillsRoot>] [--json]
node bin/dsh.mjs preset spec
node bin/dsh.mjs preset status [--profile-dir <dir>] [--json]
node bin/dsh.mjs preset install [--profile-dir <dir>] [--force] [--apply]
node bin/dsh.mjs preset uninstall [--profile-dir <dir>] [--apply]
```
`preset install` / `preset uninstall` 默认是 dry-run,打印将要做的编辑;加 `--apply` 才写文件(写入前备份,卸载时存在备份则从备份还原)。
安装这条路线最省事的方式是交给 DSH 自带的包管理器——它的目标参数既可以是 npm 包名(从 registry 装已发布版本),也可以是本地路径(`link:` / `file:` 规格):
```bash
dsh plugin --profile <profile> add rev-skills # registry 上的已发布版本
dsh plugin --profile <profile> add link:/path/to/rev-skills # 本地工作区(改补丁立即生效)
```
成功后它会**自动把 `rev-skills` 追加进该 profile 的 `dsh.profile.bundles`**(实测:从空 bundles 开始,`add` 完成后 bundles 变为 `[..., "rev-skills"]`);失败则还原 `package.json`、`pnpm-lock.yaml` 与 `node_modules`,不留半截状态。GUI 插件管理器走同一套机制。
手工等价写法是把下面两处同时写进 profile 的 `package.json`:
```jsonc
{
"dependencies": { "rev-skills": "link:/path/to/rev-skills" },
"dsh": { "profile": { "bundles": [ "...", "rev-skills" ] } }
}
```
两处必须同时存在:**bundle 行在 `dsh.profile.bundles` 里,且依赖能解析到该包**。profile 的 `package.json` 位于 `$env:DSH_PROFILE_DIR`(Windows 默认 `C:\Users\<用户>\.dsh\profiles\<profile>\package.json`)。
卸载走对称的一条命令,它会同时摘掉依赖与 `dsh.profile.bundles` 里的那一行(实测),随后重启 DSH;已存在的会话保留创建时的插件组合,需新开会话:
```bash
dsh plugin --profile <profile> remove rev-skills
node bin/dsh.mjs preset uninstall --apply # 仓库自带 CLI 的等价路径(有备份时从备份还原)
```
**发布面的一个硬约束**:bundle 的补丁文件必须出现在该包 `package.json` 的 `files` 列表里。pnpm 处理 `file:` 依赖时按 `files` 复制内容,漏列时 `add` 会报 `failed to read overlay …/node_modules/rev-skills/dsh.patch.yml: ENOENT` 并整体回滚(测试里有对应回归断言)。
适用场景:需要一个只见得到本库技能、且带固定工具与提示词的独立 agent 预设。
## 3. DSH 强制的兼容规则
本库 122 个技能已全部满足下列规则;新增或修改技能时按此自检。
1. **目录布局(一层)**:`<root>/<name>/SKILL.md` 或 `<root>/<name>.md`。扫描只深入一层——嵌套的 `**/SKILL.md` 不会被发现,例如 `.claude/skills/re-analyze/references/analysis-contract.md` 不构成独立技能。
2. **frontmatter 分界行必须精确**:开头的行就是 `---`(前后无空格、无 BOM),结尾是同样精确的一行 `---`;闭合行之后才有正文。
3. **frontmatter 必须是合法 YAML 且解析为 mapping**:键值映射,块标量与内联 list 都在 YAML 子集内。解析失败时 DSH 记一条告警并**静默跳过该技能**(技能看起来"消失",不报错中断)。
4. **`name` 语法**:必须匹配 `/^[a-z0-9]+(?:-[a-z0-9]+)*$/`(小写字母、数字、单连字符分段),并且与目录名一致。
5. **`description` 非空**:缺失或空串即不注册。本库另有更严的约束——`description` 必须同时含 CJK 与拉丁字母(`validate.mjs` 强制,保证中英触发词都可命中)。
6. **可选键**:`whenToUse`、`metadata`、`disable-model-invocation`、`user-invocable`;本库技能不需要它们即可被发现。
7. **工具结果裁剪**:标准预设会裁剪超过 **8192 字符**的工具结果。本库有 **32 个** `SKILL.md` 的文件字符数超过该阈值(CRLF 检出下为 34;`node bin/dsh.mjs check` 会逐条列出字符数与正文行数,最长 `re-kernel`,约 19979 字符),这些技能被加载时正文可能被截断——需要完整内容时直接读文件,或把该技能拆分为「正文 + `references/`」。
本机自检 frontmatter 的最短路径:
```bash
node validate.mjs # 结构校验(frontmatter / 命名 / 能力标签 / 死链 / guard / 章节)
node bin/dsh.mjs check # 按 DSH 发现规则校验技能根(一层布局 / 分界行 / name 文法 / 裁剪阈值)
```
YAML 本身按换行解析,DSH 允许分界行尾带 CR;本库随库脚本(`validate.mjs` / `lib/frontmatter.mjs`)按 LF 解析,因此检出的工作区行尾须与仓库声明一致(`.gitattributes` 与 `core.autocrlf` 的取向见仓库根)。
## 4. 验证
1. `node validate.mjs` 输出 `OK: 122 skills validated`——内容层通过。
2. `node bin/dsh.mjs check`——按 DSH 发现规则校验技能根,通过时输出 `OK: N skills DSH-compatible`;`--dir <skillsRoot>` 换根、`--json` 逐条给出检查项与告警。
3. `node bin/dsh.mjs preset spec`——确认解析到的技能根(`REV_SKILLS_DIR` → `<DSH_HOME>/skills` → 包目录,见第 2 节的顺序表)。
4. 新开会话,在预设列表里选中「逆向工程(rev-skills)」:`skill` 工具目录中应出现入口技能 `re-analyze` 与其余 121 个 `re-*` 技能;加载 `re-analyze` 能返回正文即技能根解析正确。
5. 对照检查:切回 `standard` 预设的会话,`re-*` 技能不再由预设层提供——若该会话仍能看到 `re-*`,说明技能同时装进了被扫描的全局技能根(路线一),那属于预期结果而非故障。
## 5. 排错
| 现象 | 常见原因 | 处理 |
|---|---|---|
| 技能全部看不到 | 装错根目录(`--project` 装到了 `.claude/skills` 而非 `.dsh/skills`) | 重跑 `--target dsh --global` 或 `--project`,用 `node bin/dsh.mjs check` 核对根 |
| 只有个别技能看不到 | frontmatter 首尾不是精确 `---`,或 YAML 不合法 | DSH 日志会有告警并跳过该条;跑 `node validate.mjs` 定位;修正分界行/YAML |
| 技能名不合法 | `name` 含大写、下划线、连续连字符或与目录名不一致 | 改为 `<root>/<name>/SKILL.md` 且 `name` 匹配 `/^[a-z0-9]+(?:-[a-z0-9]+)*$/` |
| 技能列表里没有 `references/` 里的文件 | 扫描只深入一层,嵌套 `**/SKILL.md` 不发现 | 属预期行为;把需要独立触发的内容提升为顶层技能 |
| 预设不出现 | bundle 名不在 profile 的 `dsh.profile.bundles`,或依赖未安装 | 跑 `node bin/dsh.mjs preset status`;补 bundle 行与依赖后重装 profile |
| 预设改完没生效 | profile 未重新安装/未应用 | `node bin/dsh.mjs preset install --apply`,或从 GUI 插件管理器重新安装 bundle |
| 旧会话看不到新预设 | 已存在的会话保留创建时的插件组合 | 新开一个会话(旧会话不会热切预设) |
| 技能根解析到了别处 | `REV_SKILLS_DIR` 或 `<DSH_HOME>/skills` 先命中,且其中存在 `re-` 条目 | 用 `node bin/dsh.mjs preset spec` 看命中项,清掉不该命中的目录或变量 |
| 改了技能正文但会话里没变 | 正文按需加载,编辑正文不需重启 | 重新触发该技能即可 |
| 改了技能 frontmatter 但会话里没变 | frontmatter 在启动/加载时解析并缓存 | 重启 DSH 会话(或重载技能根)后生效 |
| 技能正文被截断 | 正文超过 8192 字符被预设裁剪 | 直接读文件,或把长技能拆分为正文 + `references/` |
## 6. 维护同步点
改 DSH 相关文件时,下列配对必须同时更新,否则出现"命令能跑但预设装不上"这类不一致:
| 项 | 一处 | 另一处 |
|---|---|---|
| 补丁文件 | 根 `package.json` → `dsh.bundle.patch` | `dsh.patch.yml` 实际路径,且该路径必须出现在根 `package.json` 的 `files` 里 |
| bundle 名 | 根 `package.json` → `name`(`rev-skills`) | profile `package.json` → `dsh.profile.bundles` 的对应行 + 同名依赖;本文档与 README 的安装示例 |
| 预设 id | `dsh.patch.yml` 的 `config.id`(`rev-skills`) | 本文档的说明 |
| 技能根 | `dsh.patch.yml` 的 `customSkillDirs` 解析顺序 | `REV_SKILLS_DIR` 覆盖逻辑、`bin/dsh.mjs` 的 `resolveLocalSkillRoot` 与本文档第 2 节的候选表 |
版本号不再有第二处:bundle 就是根包,根 `package.json` 的 `version` 与 `.claude-plugin/marketplace.json` 成对即可。
发布流程与其他宿主一致:`npm test` 通过 → git tag → npm publish → 同步 `package.json` 与 `.claude-plugin/marketplace.json` 的版本号。