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

技能管家 icrefin/dsh-skill-mgr

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

在侧边栏统一管理技能清单,启停与恢复都方便

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · market
装得上吗
本站尚未做安装检查
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 20 天前
档位由哪些信号合成

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

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

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

读写/删除本地文件写入剪贴板(不计风险)
查看扫描证据(共 120 处,展示前 88 条)
  • 写入剪贴板
    src/client/helpers.ts:56
    await navigator.clipboard.writeText(text)
  • 读取本地文件
    src/index.ts:249
    const dropped = await store.pruneDisabled((entry) => existsSync(entry.path))
  • 读取本地文件
    src/operations/catalog.ts:114
    const times = await stat(candidate)
  • 读取本地文件
    src/operations/inventory.ts:47
    await stat(path)
  • 读取本地文件
    src/operations/inventory.ts:60
    const times = await stat(path)
  • 读取本地文件
    src/operations/inventory.ts:90
    const text = await readFile(record.path, 'utf8')
  • 写入本地文件
    src/operations/inventory.ts:240
    await mkdir(trashDir, { recursive: true })
  • 写入本地文件
    src/operations/inventory.ts:242
    await rename(source, target)
  • 读取本地文件
    src/operations/trash.ts:27
    await stat(path)
  • 写入本地文件
    src/provider.test.ts:30
    await mkdir(join(home, 'skills'), { recursive: true })
  • 写入本地文件
    src/provider.test.ts:31
    await mkdir(join(agentsHome, 'skills'), { recursive: true })
  • 删除本地文件递归
    src/provider.test.ts:37
    await rm(dir, { recursive: true, force: true })
  • 写入本地文件
    src/provider.test.ts:55
    await writeFile(join(home, 'skills', 'broken.md'), '# no frontmatter', 'utf8')
  • 写入本地文件
    src/provider.test.ts:62
    await mkdir(join(project, '.git'), { recursive: true })
  • 写入本地文件
    src/provider.test.ts:63
    await mkdir(join(project, '.dsh', 'skills', 'proj-skill'), { recursive: true })
  • 写入本地文件
    src/provider.test.ts:64
    await writeFile(
  • 删除本地文件递归
    src/provider.test.ts:109
    await rm(join(home, 'skills', 'ghost-skill'), { recursive: true, force: true })
  • 删除本地文件递归
    src/provider.test.ts:116
    await rm(join(home, 'skills'), { recursive: true, force: true })
  • 读取本地文件
    src/provider.ts:106
    const info = await stat(entry.path)
  • 读取本地文件
    src/provider.ts:129
    text = await readFile(entry.path, 'utf8')
  • 读取本地文件
    src/provider.ts:156
    text = await readFile(locator.path, 'utf8')
  • 写入本地文件
    src/reconcile.test.ts:19
    await mkdir(join(home, 'skills'), { recursive: true })
  • 写入本地文件
    src/reconcile.test.ts:20
    await mkdir(join(agentsHome, 'skills'), { recursive: true })
  • 删除本地文件递归
    src/reconcile.test.ts:29
    await rm(dir, { recursive: true, force: true })
  • 写入本地文件
    src/reconcile.test.ts:33
    await mkdir(join(home, 'skills', 'paused-skill'))
  • 写入本地文件
    src/reconcile.test.ts:34
    await writeFile(
  • 写入本地文件
    src/reconcile.test.ts:50
    await writeFile(join(home, 'skills', 'flat-skill.md.disabled'), '---\nname: flat-skill\ndescription: Flat one\n---', 'utf8')
  • 写入本地文件
    src/reconcile.test.ts:51
    await writeFile(join(agentsHome, 'skills', 'agent-skill.md.disabled'), '---\nname: agent-skill\ndescription: Agent one\n---', 'utf8')
  • 读取本地文件
    src/reconcile.ts:40
    text = await readFile(path, 'utf8')
  • 读取本地文件
    src/reconcile.ts:50
    disabledAt = (await stat(path)).mtimeMs
  • 读取本地文件
    src/registry.ts:40
    return existsSync(join(dir, 'package.json'))
  • 读取本地文件
    src/registry.ts:41
    && (existsSync(join(dir, 'cordis.yml')) || existsSync(join(dir, 'cordis.patch.yml')))
  • 读取本地文件
    src/registry.ts:92
    raw = readFileSync(path, 'utf8')
  • 写入本地文件
    src/registry.ts:115
    mkdirSync(dirname(path), { recursive: true })
  • 写入本地文件
    src/registry.ts:118
    writeFileSync(tmp, `${JSON.stringify(document, null, 2)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' })
  • 写入本地文件
    src/registry.ts:119
    chmodSync(tmp, 0o600)
  • 写入本地文件
    src/registry.ts:120
    renameSync(tmp, path)
  • 删除本地文件
    src/registry.ts:122
    rmSync(tmp, { force: true })
  • 读取本地文件
    src/skillfs/scan.ts:28
    names = await readdir(base)
  • 读取本地文件
    src/skillfs/scan.ts:38
    stats = await stat(absolute)
  • 读取本地文件
    src/skillfs/scan.ts:66
    names = await readdir(base)
  • 读取本地文件
    src/skillfs/scan.ts:76
    stats = await stat(absolute)
  • 读取本地文件
    src/skillfs/scan.ts:83
    if ((await stat(candidate)).isFile()) paths.push(candidate)
  • 读取本地文件
    src/skillfs/scan.ts:109
    text = await readFile(yamlPath, 'utf8')
  • 写入本地文件
    src/skillfs.test.ts:103
    await mkdir(join(home, 'skills'), { recursive: true })
  • 删除本地文件递归
    src/skillfs.test.ts:107
    await rm(dir, { recursive: true, force: true })
  • 读取本地文件
    src/skillfs.test.ts:112
    const text = await readFile(path, 'utf8')
  • 读取本地文件
    src/skillfs.test.ts:120
    const text = await readFile(path, 'utf8')
  • 读取本地文件
    src/skillfs.test.ts:128
    expect(parseFrontmatter(await readFile(path, 'utf8'))).toMatchObject({
  • 读取本地文件
    src/skillfs.test.ts:141
    await expect(access(original)).rejects.toThrow()
  • 读取本地文件
    src/skillfs.test.ts:144
    await expect(access(original)).resolves.toBeUndefined()
  • 写入本地文件
    src/skillfs.test.ts:149
    await writeFile(flat, '---\nname: flat-skill\ndescription: Flat\n---\n\nBody', 'utf8')
  • 写入本地文件
    src/skillfs.test.ts:168
    await mkdir(join(home, 'skills'), { recursive: true })
  • 删除本地文件递归
    src/skillfs.test.ts:172
    await rm(dir, { recursive: true, force: true })
  • 读取本地文件
    src/skillfs.test.ts:180
    await expect(access(file)).rejects.toThrow()
  • 写入本地文件
    src/skillfs.test.ts:188
    await writeFile(flat, '---\nname: flat-trash\ndescription: Flat\n---\n\nbody', 'utf8')
  • 写入本地文件
    src/skillfs.test.ts:212
    await mkdir(trashDir, { recursive: true })
  • 写入本地文件
    src/skillfs.test.ts:213
    await writeFile(join(trashDir, 'SKILL.md'), '---\nname: gone\ndescription: x\n---', 'utf8')
  • 删除本地文件递归
    src/skillfs.test.ts:236
    await rm(dir, { recursive: true, force: true })
  • 删除本地文件递归
    src/skillfs.test.ts:287
    await rm(dir, { recursive: true, force: true })
  • 写入本地文件
    src/skillfs.ts:41
    await mkdir(dir, { recursive: true })
  • 写入本地文件
    src/skillfs.ts:56
    await writeFile(file, body, 'utf8')
  • 写入本地文件
    src/skillfs.ts:71
    await rename(path, disabled)
  • 写入本地文件
    src/skillfs.ts:81
    await rename(disabledPath, original)
  • 写入本地文件
    src/skillfs.ts:95
    await mkdir(trashDir, { recursive: true })
  • 写入本地文件
    src/skillfs.ts:97
    await rename(source, target)
  • 删除本地文件递归
    src/skillfs.ts:140
    await rm(entry.path, { recursive: true, force: true })
  • 读取本地文件
    src/skillfs.ts:155
    const text = await readFile(path, 'utf8')
  • 读取本地文件
    src/store/store.ts:47
    const raw = await readFile(source, 'utf8')
  • 读取本地文件
    src/store/store.ts:76
    if (existsSync(this.file)) return this.file
  • 读取本地文件
    src/store/store.ts:78
    if (existsSync(candidate)) return candidate
  • 写入本地文件
    src/store/store.ts:203
    await mkdir(dirname(this.file), { recursive: true })
  • 写入本地文件
    src/store/store.ts:204
    await writeFile(tmp, JSON.stringify(payload, null, 2) + '\n', 'utf8')
  • 写入本地文件
    src/store/store.ts:205
    await rename(tmp, this.file)
  • 读取本地文件
    src/store.test.ts:21
    return JSON.parse(await readFile(file, 'utf8')) as StoreFile
  • 删除本地文件递归
    src/store.test.ts:36
    await rm(dir, { recursive: true, force: true })
  • 读取本地文件
    src/store.test.ts:86
    const before = await readFile(file, 'utf8')
  • 读取本地文件
    src/store.test.ts:88
    expect(await readFile(file, 'utf8')).toBe(before)
  • 读取本地文件
    src/store.test.ts:101
    const before = await readFile(file, 'utf8')
  • 读取本地文件
    src/store.test.ts:103
    expect(await readFile(file, 'utf8')).toBe(before)
  • 写入本地文件
    src/store.test.ts:116
    await writeFile(file, 'not json at all', 'utf8')
  • 读取本地文件
    src/store.test.ts:146
    const before = await readFile(file, 'utf8')
  • 写入本地文件
    src/store.test.ts:158
    await writeFile(file, JSON.stringify({
  • 写入本地文件
    src/store.test.ts:171
    await writeFile(legacyA, JSON.stringify({
  • 写入本地文件
    src/store.test.ts:175
    await writeFile(legacyB, JSON.stringify({
  • 写入本地文件
    src/store.test.ts:194
    await writeFile(legacy, JSON.stringify({
  • 写入本地文件
    src/store.test.ts:198
    await writeFile(file, JSON.stringify({
  • 读取本地文件
    src/version.ts:31
    const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { name?: unknown; version?: unknown }
扫描于 2026/11/1(0 天前) · 范围 全包扫描(包内未声明入口) · 来源 GitHub 仓库

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

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-api-remotes@deepseek-ai/dsh-client-connection@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-store@deepseek-ai/dsh-client-ui-input-trigger@deepseek-ai/dsh-client-ui-layout@deepseek-ai/dsh-client-ui-plugin-manager@deepseek-ai/dsh-client-ui-renderer@deepseek-ai/dsh-client-ui-settings@deepseek-ai/dsh-client-ui-sidebar@deepseek-ai/dsh-client-ui-slots
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 侧边栏管理技能清单
  • 启用停用技能
  • 可恢复回收站

DeepSeek Harness 的配置档级技能管理器:扁平技能清单,支持启用/停用、发现诊断和可恢复的回收站,收在侧边栏面板里。

README

由 DeepSeek 最新模型翻译生成
dsh-skill-mgr 所有技能,一张清单 —— 在侧边栏面板里看到整个 profile 的技能清单:有哪些已安装、读一读技能正文、启用或禁用它、修复发现诊断,以及把它删进一个可恢复的回收站。 如果这个插件让你不必再去 ~/.dsh/skills 里翻找,欢迎点一个 ⭐ Star,让更多人看到它。 它解决的问题 一个技能就是一个装着发现文件的文件夹 —— 创建很容易,理清楚很难:到底有哪些、哪些当前是关着的(文件改名后是“看不见”而不是“不存在”)、哪一个的 frontmatter 坏了、两个可写根目录之间有哪些重名。本插件就是回答这些问题的唯一去处:不用打开编辑器,也不用手工改文件名。 默认即启用,而禁用首先是文件系统层面的事实。 发现文件被重命名(SKILL.md → SKILL.md.disabled,或 <name>.md → <name>.md.disabled),文件系统 provider 直接看不到它;而注册表记录让面板仍能把它列出来并重新打开。 截图 Skills 面板:扁平的 profile 清单 Skills —— 一张覆盖整个 profile 清单(启用与禁用一视同仁)的扁平表格,带搜索、来源筛选、调用次数与最近使用徽标、发现诊断,以及可恢复的回收站。
展开完整文档(README 剩余 1 节)
dsh-skill-mgr

所有技能,一张清单 —— 在侧边栏面板里看到整个 profile 的技能清单:有哪些已安装、读一读技能正文、启用或禁用它、修复发现诊断,以及把它删进一个可恢复的回收站。

如果这个插件让你不必再去 ~/.dsh/skills 里翻找,欢迎点一个 ⭐ Star,让更多人看到它。

它解决的问题

一个技能就是一个装着发现文件的文件夹 —— 创建很容易,理清楚很难:到底有哪些、哪些当前是关着的(文件改名后是“看不见”而不是“不存在”)、哪一个的 frontmatter 坏了、两个可写根目录之间有哪些重名。本插件就是回答这些问题的唯一去处:不用打开编辑器,也不用手工改文件名。

默认即启用,而禁用首先是文件系统层面的事实。 发现文件被重命名(SKILL.md → SKILL.md.disabled,或 <name>.md → <name>.md.disabled),文件系统 provider 直接看不到它;而注册表记录让面板仍能把它列出来并重新打开。

截图

Skills 面板:扁平的 profile 清单

Skills —— 一张覆盖整个 profile 清单(启用与禁用一视同仁)的扁平表格,带搜索、来源筛选、调用次数与最近使用徽标、发现诊断,以及可恢复的回收站。

单个技能的详情视图

Inspect —— 技能正文与元数据,禁用中的技能还会带上启用/禁用操作。

注册表文件

<profile>/.dsh/dshsk/skills.json the profile's skill management state

这一份注册表保存管理器持久化的全部内容:已禁用技能记录、回收站,以及统计检查点。它在启动时对账 —— 没有记录的 .disabled 文件会被收编(升级过或手工编辑过的安装),而文件已消失的记录会被丢弃。写入一律经过同目录的原子临时文件(wx、权限 0600,然后 rename);读取时若遇到无法解析的 JSON,则拒绝覆盖它并报告原因。

功能

| 界面 | 作用 |
| 侧边栏条目 | 紧挨 Plugins 下方(order: 1),点开即在中栏打开 Skills 页面 |
| Skills 表格 | 一张扁平表格列出 profile 清单 —— Name / Source / Description / Status / Actions —— 带搜索、排序与来源筛选 |
| 启用 / 禁用 | 重命名发现文件并保留记录,因此该行仍然可见(变暗)且可以随时重新打开 |
| Inspect | 不离开 GUI 就能读取技能的正文与元数据 |
| 新增技能 | 在可写根目录里脚手架式地创建一个新技能 |
| 发现诊断 | 列出 provider 看不到的技能,并对可修复项就地自动修复 |
| 可恢复回收站 | Delete 把技能移到该根目录的 .trash;面板把它列出来并提供 Restore,清空回收站则是显式操作 |
| 聊天 / 菜单圆点 | 为每个候选行标出“模型可调”或“仅用户可调”,颜色取自面板读取的同一份设置 |
| 设置卡片 | 插件自己的配置,位于 Plugins 管理页 |
| 署名 | 标题徽标带已安装版本与作者署名 |

环境要求

- Node.js ^22.19.0 || >=24.0.0
- 一套 DeepSeek Harness 0.2.0-rc.2 安装(插件组合进某个 profile)
- pnpm 11.8.0(由清单里的 packageManager 固定)

安装

pnpm install
pnpm check # typecheck → build → tests → verifiers
pnpm pack # dist/dsh-skill-mgr-<version>.tgz
sh tools/install-into-profile.sh desktop

之后重启 DeepSeek Harness,侧边栏条目会出现在 Plugins 正下方。

这个包没有发布到 npm:profile 直接从本仓库安装。脚本用的是 dsh plugin(等价于 pnpm 直通写入 profile 目录),Plugins 页面做的是同一件事。desktop profile 归 Electron 应用所有,所以 dsh --profile desktop 本身拒绝组合它,而 dsh plugin 仍然可用。

从 dsh-skill-hub 升级?两个包不能同时安装 —— 它们会同时抢占插件配置卡。请先移除旧 bundle;新插件会在首次加载时收编旧状态文件,并保持原文件不动。

快速开始

1. 在侧边栏打开 Skills。表格会列出 ~/.dsh/skills 与 ~/.agents/skills 里的每一个技能。
2. 搜索某个技能;选 Inspect 阅读它,或选 Disable 在整个 profile 内关掉它。
3. 选 Add skill 在可写根目录里创建一个新技能。
4. 有东西没被发现时,读一读诊断横幅,并对可修复项使用它的修复操作。

配置

Loader 条目的 config 就是设置卡片;host 通过对应的 volatile ref 读取同一批值。

| 字段 | 默认值 | 含义 |
| enabled | true | 提供技能 provider 与公告;无论如何管理面板都照常工作,因为它正是把插件重新打开的那个界面 |
| announceToAgent | true | 注册告诉 agent 存在这个管理器的 system-prompt 段落 |
| showUseCount | true | 每个技能的调用次数徽标 |
| showUseTime | true | 每个技能的最近使用相对时间 |
| dotModelColor | 未设置 | 表格与聊天 / 菜单里“模型可调”圆点的 #rrggbb |
| dotUserColor | 未设置 | “仅用户可调”圆点的 #rrggbb |
| statsWindowDays | 14 | 统计的滚动窗口天数;0 = 全部历史 |
| statsScanMinutes | 5 | 后台扫描会话日志的间隔(分钟) |

Remote 接口

浏览器半通过唯一的 Typert Remote 命名空间 skillMgr 读写。两条约定覆盖整个接口:snapshot() 是唯一的读(面板在一次渲染内共享一次往返,并从这一次调用加载所有切片),而每个写操作都返回 SkillApplyResult({ok:true} / {ok:false, error}),之后调用方重新拉取。

| 区域 | 方法 |
| 核心 | snapshot |
| profile 清单 | saveGlobalSkill, removeGlobalSkill, setGlobalSkillEnabled, skillDetail, fixDiagnostic, skillStats |
| 回收站 | restoreTrashedSkill, clearTrash |

共 9 个方法。它们的入参与返回类型以 zod schema 的形式写在 src/wire.ts,每个类型都是对应 schema 的 z.infer,因此 host 实现、descriptor 表与浏览器半不可能彼此漂移。

注意事项

- 启用/禁用是按 profile 全局生效的,不是按工作区。 因为开关就是重命名一个发现文件,所以无法做到某个技能在一个工作区开启、在另一个工作区关闭;每个工作区都会被提供 profile 清单中的每一个技能。需要按工作区开关的读者在这里是得不到的。
- 项目技能只被发现,不被管理。 provider 会列出工作区自己的根目录,因此 agent 仍能看到就在它旁边的技能,但它们从来不属于面板管理的 profile 清单:无法被开关、删除或收编。只有 ~/.dsh/skills 与 ~/.agents/skills 可写。
- 可写根目录仅限用户级,拒绝时会指明该技能归属的来源。
- 删除可恢复,但没有自动过期。 Delete 把技能移进该根目录的 .trash,面板把它列出来并提供 Restore;清空回收站则是永久操作。
- 本地安装会固定 tarball 路径。 删掉 dist/dsh-skill-mgr-<version>.tgz 后,下一次安装会以 ENOENT 失败。
- 没有更新检查。 本包相对 dsh-skill-hub 改动很大,跟踪上游 release 只会误导;而且本包也没有可轮询的发布渠道。

许可证与来源

MIT —— 见 LICENSE。本包由 dsh-skill-hub 0.3.18 重构而来(MIT,“dsh-skill-hub contributors”);该版权行保留在 LICENSE 文件中,与上游的差异记录在 docs/ARCHITECTURE.md。

点 Star 不花钱,但能让这个项目被更多人发现。如果它帮到了你,欢迎点一下 ⭐。
预览分享卡片长什么样 ↓icrefin/dsh-skill-mgr
分享 · DeepSeek Harness Hub
icrefin/dsh-skill-mgr

在侧边栏统一管理技能清单,启停与恢复都方便

回到顶部