# Skills-Manager 设计文档(DESIGN.md) > 包名:`@dsh-skills-manager/dsh-skills-manager` > 定位:DeepSeek Harness(DSH)的「双面」技能管理插件 ## 1. 概述 Skills-Manager 是一个基于 [Cordis](https://cordis.js.org/) 插件体系的 DSH 插件,用于统一管理来自多个来源的 Agent Skills。它被拆分为两个相互独立、分别构建与加载的「半边」: - **Host 半边(后端 / Node.js)**:扫描、导入、管理来自多源的技能;提供技能注册表、回收站、仓库浏览、企业技能源、更新与差异比对等能力。产物为 `lib/index.js`(CommonJS/ESM 由 `tsc` 产出),类型声明为 `lib/types/`。 - **Client 半边(前端 / 浏览器)**:在 DSH 设置区域渲染统一的技能管理面板。产物为 `client/client.js`(由 `tsdown` 打包的闭包工厂,注入到 `window.__ModuleLoader__`)。 两半通过 **webServer HTTP**(`/api/skills-manager` 前缀路由)通信:Client 端用 `fetch` 调用 Host 端暴露的类型化 HTTP 接口,每次变更后重新拉取快照(详见 §6.1)。Host 端仍会在提交点 `emit` Cordis 事件(`skills-manager/changed` 等,见 §3.6)作为进程内通知,但当前 Client 并不订阅这些事件——跨进程边界由 HTTP 拉取驱动刷新。 ## 2. 架构总览 ``` ┌─────────────────────────────┐ ┌──────────────────────────────┐ │ Host 半边 (Node.js) │ │ Client 半边 (浏览器) │ │ src/index.ts │ │ src/client/index.ts (+.tsx) │ │ │ │ │ │ ┌────────────────────────┐ │ │ ┌─────────────────────────┐ │ │ │ Skill Registry 服务 │ │ HTTP │ │ 设置面板 UI (React) │ │ │ │ listSkills/import/... │◄─┼─ fetch ─┼─►│ 变更后重拉快照 │ │ │ └───────────┬────────────┘ │ │ └─────────────────────────┘ │ │ │ │ │ React.useState 本地状态 │ │ ┌───────────▼────────────┐ │ └──────────────────────────────┘ │ │ JsonFileStore (存储层) │ │ ▲ │ │ ~/.dsh/skills-manager/ │ │ │ 打包 │ └─────────────────────────┘ │ tsdown → client/client.js │ tsc → lib/index.js │ (闭包工厂 + lightningcss) └─────────────────────────────┘ ``` ## 3. 数据模型(`src/types.ts`) 该文件定义了 Host 与 Client 之间通信的「线协议(wire vocabulary)」,是两半的共享契约。 ### 3.1 技能来源 `SkillSource` 标识技能的发现或安装来源,共 9 类: | 值 | 含义 | | --- | --- | | `local` | 本地机器扫描(非 DSH 目录) | | `project` | 当前项目 | | `dsh-global` | DSH 全局目录(`~/.dsh/skills/`) | | `managed` | 本插件管理(导入/创建) | | `repo` | 从仓库安装 | | `company` | 从企业技能源安装 | | `codex` / `claude` / `copilot` | 对应 Agent 的技能 | ### 3.2 核心记录 - **`SkillRecord`**:注册表追踪的单个技能。`id` 为 `source::relativePath` 的哈希;包含 `name`、`description`、`source`、`enabled`、只读的 `originPath`、可选的 `managedPath`、解析后的 `frontmatter`、来源明细 `sourceDetail` 及时间戳/版本。 - **`SkillFrontmatter`**:从 `SKILL.md` 解析的 YAML frontmatter(`name`、`description`、`whenToUse`、`disableModelInvocation`、`userInvocable`、`metadata`)。 - **`SourceDetail`**:来源附加元数据(仓库 URL/分支、Agent 类型、项目路径、企业源 ID)。 - **`TrashRecord`**:回收站条目,包裹删除时的 `originalSkill`,含 `deletedAt`、`trashPath`、默认 30 天后到期的 `expiresAt`。 ### 3.3 仓库与企业源 - **`RepoConfig`** / **`RepoSkillItem`**:配置的 GitHub 仓库及其内部发现的技能项(含 `rawUrl`)。 - **`CompanySkillSource`**:企业技能管理端点,支持 `api` / `git` 两种类型与 `none` / `token` 鉴权。 ### 3.4 更新与差异 - **`UpdateInfo`**:单个技能的可用更新信息(当前/最新版本、是否有更新)。 - **`DiffResult` / `DiffHunk` / `DiffLine`**:统一 diff 结构,支持 `add` / `remove` / `context` 行类型与行号。 ### 3.5 操作参数 - **`ImportParams`**:从文件/目录导入(`zip` / `folder` / `file`)。 - **`CreateParams`**:创建新技能(`name`、`description`、`content`)。 ### 3.6 Cordis 事件 在 `declare module '@deepseek-ai/cordis'` 中扩展 `Events` 接口,声明三个 payload-free 通知事件: | 事件 | 触发时机 | 消费者动作 | | --- | --- | --- | | `skills-manager/changed` | 技能被导入/删除/启用/禁用/从回收站恢复 | 重读 `listSkills()` | | `skills-manager/trash-changed` | 技能被移入回收站/恢复/永久删除 | 重读 `listTrash()` | | `skills-manager/repos-changed` | 仓库被增删或其技能列表刷新 | 重读仓库状态 | > 事件在每个提交点触发;观察者失败被隔离,不能否决注册表的变更。 ## 4. 存储层(`src/file-store.ts`) 基于 JSON 文件的持久化基础设施,所有数据落在 `~/.dsh/skills-manager/`(由 `getBaseDir()` 暴露)。 ### 4.1 `JsonFileStore` 一个文档一个实例,挂载时 `start()`、销毁时 `stop()`: - **修订号(revision)**:单调递增,每次「观察到」或「应用」变更时 `+1`,用于陈旧写入检测。 - **文件监听**:`start()` 通过 `watchFile`(间隔 1000ms)监听文档,外部修改无需重启即可被 `reload()` 拾取。 - **容错读取**:文档缺失或无法解析时读取为 `defaultData()` 默认值。 - **写入**:`save()` 整体替换文档内容(2 空格缩进 + 末尾换行),随后更新 `current`、`rev` 并回调 `onChange`。 ### 4.2 目录/文件工具 `ensureDir`、`listDir`、`pathExists`、`getStats`、`readTextFile`、`writeTextFile` —— 均在基础目录下操作,写入时按需创建父目录。 ## 5. 构建与打包 ### 5.1 Host 半边(`tsc` + `tsconfig.json`) - `rootDir: src` → `outDir: lib`,声明输出到 `lib/types`。 - 严格模式全开:`strict`、`exactOptionalPropertyTypes`、`noUncheckedIndexedAccess`、`verbatimModuleSyntax`、`rewriteRelativeImportExtensions` 等。 - **排除** `src/client`(由 Client 项目单独处理)。 ### 5.2 Client 半边(`tsdown` + `tsdown.config.ts`) 镜像 DSH 外部包的 client 预设,产物为闭包工厂: - **入口**:`src/client/index.ts` → **产物**:`client/client.js`(`outDir: client`,`format: cjs`,`platform: browser`,`target: es2022`)。 - **模块加载**:banner/footer 包裹为 `window.__ModuleLoader__.load({ id: "@dsh-skills-manager/dsh-skills-manager", factory: (require) => { ... return module.exports; } })`。 - **外部依赖**:仅 `react` 走 loader 模块表(组件用 `React.createElement` 编写,不依赖 `react/jsx-runtime`,尽管后者也在 external 列表中以备将来改用 JSX);其余(CSS Modules 等)全部内联(`noExternal`),因为 loader 表无法解析的 `require()` 会导致运行时抛错。 - **CSS Modules**:自定义插件 `dsh-css-modules-inline` 通过虚拟 id(前缀 `\0dsh-css:`、后缀 `.mjs`,避开 tsdown 自身 `.css` 管线)用 `lightningcss` 编译;`import 'x.module.css'` 返回哈希类名映射,并在工厂执行时自动注入 `