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

科研技能墙 kee0012/dsh-science-skill

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

科研技能中心 for DeepSeek Harness:技能目录、分类、按会话启用门控,以及用户可调用的…

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

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

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

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:待复验
是什么
dsh 原生插件 · skill
装得上吗
曾在 dsh 0.2.0-rc.2 下真实安装成功,但当前 dsh 已更新至 0.2.1-rc.5,本站尚未在新版下复验
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 22 天前
档位由哪些信号合成

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

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

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

读写/删除本地文件发起外部网络请求
查看扫描证据(共 110 处,展示前 79 条)
  • 读取本地文件
    lib/index.js:240
    if (!existsSync(gatePath)) {
  • 读取本地文件
    lib/index.js:245
    const entries = JSON.parse(readFileSync(gatePath, 'utf8'))?.pinned ?? {}
  • 写入本地文件
    lib/index.js:261
    mkdirSync(dirname(gatePath), { recursive: true })
  • 写入本地文件
    lib/index.js:262
    writeFileSync(gatePath, JSON.stringify({ pinned: on }, null, 2) + '\n')
  • 读取本地文件
    lib/index.js:401
    return JSON.parse(readFileSync(join(paths.dataDir, `${id}.json`), 'utf8'))
  • 读取本地文件
    lib/index.js:442
    text = readFileSync(file, 'utf8')
  • 读取本地文件
    lib/index.js:492
    const record = JSON.parse(readFileSync(dataPath, 'utf8'))
  • 写入本地文件
    lib/index.js:506
    writeFileSync(dataPath, JSON.stringify(record, null, 2) + '\n')
  • 读取本地文件
    lib/index.js:579
    files = readdirSync(paths.dataDir)
  • 写入本地文件
    lib/index.js:766
    writeFileSync(filePath, JSON.stringify(record, null, 2) + '\n')
  • 发起网络请求
    lib/client.js:225
    await fetch(`${API_BASE}/skill-gate/session`, {
  • 发起网络请求
    lib/client.js:250
    const data = await (await fetch(`${API_BASE}/skill-gate/state`, { cache: "no-store" })).json();
  • 发起网络请求
    lib/client.js:264
    const data = await (await fetch(`${API_BASE}/skill-gate/pin`, {
  • 发起网络请求
    lib/client.js:493
    return await (await fetch(url, {
  • 发起网络请求
    lib/client.js:771
    const data = await (await fetch(`${API_BASE}/skills/import`, {
  • 发起网络请求
    lib/client.js:834
    const res = await fetch(`${API_BASE}/skills/refresh`, {
  • 读取本地文件
    lib/lib/catalog-runtime.mjs:40
    if (!existsSync(dataDir)) return { ok: false, error: 'no catalog data dir' }
  • 读取本地文件
    lib/lib/catalog-runtime.mjs:42
    let hasSchema = existsSync(join(catalogDir, 'schema.json'))
  • 读取本地文件
    lib/lib/catalog-runtime.mjs:43
    if (!hasSchema && typeof bundledSchema === 'string' && existsSync(bundledSchema)) {
  • 读取本地文件
    lib/lib/catalog-runtime.mjs:47
    writeFileSync(join(catalogDir, 'schema.json'), readFileSync(bundledSchema, 'utf8'))
  • 写入本地文件
    lib/lib/catalog-runtime.mjs:47
    writeFileSync(join(catalogDir, 'schema.json'), readFileSync(bundledSchema, 'utf8'))
  • 读取本地文件
    lib/lib/catalog-runtime.mjs:57
    for (const f of readdirSync(dataDir).filter((x) => x.endsWith('.json')).sort()) {
  • 读取本地文件
    lib/lib/catalog-runtime.mjs:59
    try { record = JSON.parse(readFileSync(join(dataDir, f), 'utf8')) } catch { errors.push(`${f}: unparsable`); continue }
  • 写入本地文件
    lib/lib/catalog-runtime.mjs:103
    writeFileSync(indexOut, JSON.stringify(index, null, 2) + '\n')
  • 读取本地文件
    lib/lib/categories-runtime.mjs:330
    return JSON.parse(readFileSync(path, 'utf8').replace(/^ FEFF/, ''))
  • 读取本地文件
    lib/lib/categories-runtime.mjs:340
    if (!existsSync(path)) return []
  • 写入本地文件
    lib/lib/categories-runtime.mjs:359
    mkdirSync(dirname(path), { recursive: true })
  • 写入本地文件
    lib/lib/categories-runtime.mjs:367
    writeFileSync(path, `${JSON.stringify(stored, null, 2)}\n`, 'utf8')
  • 读取本地文件
    lib/lib/categories-runtime.mjs:394
    if (existsSync(legacyPath)) {
  • 删除本地文件
    lib/lib/categories-runtime.mjs:412
    rmSync(legacyPath, { force: true })
  • 读取本地文件
    lib/lib/import-settings.mjs:56
    if (!existsSync(path)) return { defaultSkillDir: '' }
  • 读取本地文件
    lib/lib/import-settings.mjs:58
    const parsed = JSON.parse(readFileSync(path, 'utf8').replace(/^ FEFF/, ''))
  • 写入本地文件
    lib/lib/import-settings.mjs:76
    mkdirSync(dirname(path), { recursive: true })
  • 写入本地文件
    lib/lib/import-settings.mjs:77
    writeFileSync(path, JSON.stringify({ defaultSkillDir }, null, 2) + '\n')
  • 读取本地文件
    lib/lib/skill-naming.mjs:69
    if (!existsSync(path)) return undefined
  • 读取本地文件
    lib/lib/skill-naming.mjs:72
    text = readFileSync(path, 'utf8')
  • 读取本地文件
    lib/lib/skill-naming.mjs:234
    text = readFileSync(join(root, '.credentials.yaml'), 'utf8')
  • 发起网络请求
    lib/lib/skill-naming.mjs:394
    const response = await fetch(`${route.baseURL.replace(/ +$/u, '')}/chat/completions`, {
  • 读取本地文件
    lib/lib/skills-runtime.mjs:104
    if (!existsSync(skillMd)) return { ok: false, error: '目录中没有 SKILL.md' }
  • 读取本地文件
    lib/lib/skills-runtime.mjs:106
    try { md = readFileSync(skillMd, 'utf8') } catch { return { ok: false, error: 'SKILL.md 无法读取' } }
  • 删除本地文件递归
    lib/lib/skills-runtime.mjs:183
    rmSync(staging, { recursive: true, force: true })
  • 删除本地文件递归
    lib/lib/skills-runtime.mjs:190
    rmSync(staging, { recursive: true, force: true })
  • 读取本地文件
    lib/lib/skills-runtime.mjs:197
    const finalExists = existsSync(finalTarget)
  • 读取本地文件
    lib/lib/skills-runtime.mjs:205
    const adoptedExists = adoptedPath !== undefined && existsSync(adoptedPath)
  • 删除本地文件递归
    lib/lib/skills-runtime.mjs:207
    rmSync(staging, { recursive: true, force: true })
  • 写入本地文件
    lib/lib/skills-runtime.mjs:212
    mkdirSync(installRoot, { recursive: true })
  • 删除本地文件递归
    lib/lib/skills-runtime.mjs:213
    rmSync(finalTarget, { recursive: true, force: true })
  • 删除本地文件递归
    lib/lib/skills-runtime.mjs:214
    if (adoptedPath !== undefined && adoptedPath !== finalTarget) rmSync(adoptedPath, { recursive: true, force: true })
  • 写入本地文件
    lib/lib/skills-runtime.mjs:224
    renameSync(staging, finalTarget)
  • 删除本地文件递归
    lib/lib/skills-runtime.mjs:229
    rmSync(staging, { recursive: true, force: true })
  • 写入本地文件
    lib/lib/skills-runtime.mjs:253
    mkdirSync(dataDir, { recursive: true })
  • 写入本地文件
    lib/lib/skills-runtime.mjs:254
    writeFileSync(join(dataDir, `${finalId}.json`), JSON.stringify(record, null, 2) + '\n')
  • 读取本地文件
    lib/lib/skills-runtime.mjs:302
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    lib/lib/skills-runtime.mjs:349
    if (!existsSync(dataPath)) return undefined
  • 写入本地文件
    lib/lib/skills-runtime.mjs:390
    mkdirSync(installRoot, { recursive: true })
  • 写入本地文件
    lib/lib/skills-runtime.mjs:461
    writeFileSync(dataPath, JSON.stringify(record, null, 2) + '\n')
  • 读取本地文件
    lib/lib/skill-acceptance.mjs:97
    entries = readdirSync(dir, { withFileTypes: true }).map((entry) => entry.name)
  • 读取本地文件
    lib/lib/skill-acceptance.mjs:110
    return { path, text: readFileSync(path, 'utf8').replace(/^ FEFF/, '') }
  • 读取本地文件
    lib/lib/skill-acceptance.mjs:211
    entries = readdirSync(skillsDir, { withFileTypes: true })
  • 读取本地文件
    lib/lib/skill-provider.mjs:95
    entries = readdirSync(dir, { withFileTypes: true })
  • 读取本地文件
    lib/root.mjs:79
    return existsSync(bundledSchema) ? bundledSchema : undefined
  • 读取本地文件
    lib/lib/skill-frontmatter.mjs:217
    return repairFrontmatterText(readFileSync(path, 'utf8'))
  • 读取本地文件
    lib/lib/skill-frontmatter.mjs:320
    let body = readFileSync(path, 'utf8')
  • 写入本地文件
    lib/lib/skill-frontmatter.mjs:345
    writeFileSync(temporary, body, 'utf8')
  • 写入本地文件
    lib/lib/skill-frontmatter.mjs:346
    renameSync(temporary, path)
  • 写入本地文件
    lib/lib/skill-frontmatter.mjs:360
    writeFileSync(temporary, plan.after, 'utf8')
  • 写入本地文件
    lib/lib/skill-frontmatter.mjs:361
    renameSync(temporary, path)
  • 读取本地文件
    lib/lib/skills-migrate.mjs:31
    if (statSync(pathA).size !== statSync(pathB).size) return false
  • 读取本地文件
    lib/lib/skills-migrate.mjs:32
    return readFileSync(pathA, 'utf8') === readFileSync(pathB, 'utf8')
  • 读取本地文件
    lib/lib/skills-migrate.mjs:41
    if (!existsSync(path)) return undefined
  • 读取本地文件
    lib/lib/skills-migrate.mjs:43
    return JSON.parse(readFileSync(path, 'utf8').replace(/^ FEFF/, ''))
  • 读取本地文件
    lib/lib/skills-migrate.mjs:62
    if (!existsSync(skillsDir)) return { renamed: [], conflicts: [] }
  • 读取本地文件
    lib/lib/skills-migrate.mjs:65
    for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
  • 删除本地文件递归
    lib/lib/skills-migrate.mjs:77
    rmSync(join(skillsDir, entry.name), { recursive: true, force: true })
  • 删除本地文件
    lib/lib/skills-migrate.mjs:79
    if (existsSync(staleRecord)) rmSync(staleRecord, { force: true })
  • 写入本地文件
    lib/lib/skills-migrate.mjs:87
    renameSync(join(skillsDir, entry.name), target)
  • 写入本地文件
    lib/lib/skills-migrate.mjs:97
    mkdirSync(dataDir, { recursive: true })
  • 写入本地文件
    lib/lib/skills-migrate.mjs:102
    writeFileSync(join(dataDir, verdict.name + '.json'), JSON.stringify(moved, null, 2) + '\n', 'utf8')
  • 删除本地文件
    lib/lib/skills-migrate.mjs:104
    rmSync(join(dataDir, entry.name + '.json'), { force: true })
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

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

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

功能亮点

  • 技能目录与分类
  • 按会话启用门控
  • 用户调用技能栏

README

由 DeepSeek 最新模型翻译生成
dsh-science-skill 科研技能中心:把散落在磁盘上的技能,变成 DSH 里一面可浏览、可分类、可一键调用的技能墙。 这是一个 DeepSeek Harness 插件,不依赖 Science Agent。装上之后你会得到:技能设置面板、分类标签栏、卡片式技能墙、搜索、卡片内直接改分类、开关、导入与刷新,以及用 /命令 调用技能的能力。 功能特性 左侧栏入口与技能墙 - 侧栏注册一个「技能」入口,点开在本区展开技能墙:顶部是分类标签栏(分类多了会自动排到下一行),下面按 4 列铺开卡片。 - 卡片上有三个彼此独立的目标:点卡片正文打开该技能的详情弹窗;点右上角「召唤」才把它的 /命令 引用插入当前会话的输入框(不新建会话);点右下角带 ▾ 的分类标签能直接把技能换到别的分类,改完立即重排,设置面板里的同一个技能同步。 - 技能墙用官方 sidebar.panellist 一行入口 + main 一个面板实现。 技能详情与「试试这样用」 - 详情弹窗自上而下是:分类图标、中文名、/命令、「去试试」(等同召唤)、一句简介,然后是「试试这样用」——几条中文示例提问,点任意一条就把这句话插入当前会话的输入框,不会覆盖你后续的编辑。 - 最下面是「技能详情」:该技能 SKILL.md 的原文,等宽、限高、可滚动。
展开完整文档(README 剩余 1 节)
dsh-science-skill

科研技能中心:把散落在磁盘上的技能,变成 DSH 里一面可浏览、可分类、可一键调用的技能墙。

这是一个 DeepSeek Harness 插件,不依赖 Science Agent。装上之后你会得到:技能设置面板、分类标签栏、卡片式技能墙、搜索、卡片内直接改分类、开关、导入与刷新,以及用 /命令 调用技能的能力。

功能特性

左侧栏入口与技能墙

- 侧栏注册一个「技能」入口,点开在本区展开技能墙:顶部是分类标签栏(分类多了会自动排到下一行),下面按 4 列铺开卡片。
- 卡片上有三个彼此独立的目标:点卡片正文打开该技能的详情弹窗;点右上角「召唤」才把它的 /命令 引用插入当前会话的输入框(不新建会话);点右下角带 ▾ 的分类标签能直接把技能换到别的分类,改完立即重排,设置面板里的同一个技能同步。
- 技能墙用官方 sidebar.panellist 一行入口 + main 一个面板实现。

技能详情与「试试这样用」

- 详情弹窗自上而下是:分类图标、中文名、/命令、「去试试」(等同召唤)、一句简介,然后是「试试这样用」——几条中文示例提问,点任意一条就把这句话插入当前会话的输入框,不会覆盖你后续的编辑。
- 最下面是「技能详情」:该技能 SKILL.md 的原文,等宽、限高、可滚动。
- 中文名、中文简介、分类与示例由你在 DSH 里的默认对话模型在同一次调用里一并生成,不需要给插件单独配模型;模型偶尔没给出示例时只降级为空数组,中文名与分类照常保留。

分类管理与开关

- 分类可创建、改名、删除、排序、恢复默认,并可按名称推荐图标。
- 每个技能可单独开关,开关状态持久化在数据根下。

导入与刷新

- 导入技能目录、校验一个目录能否导入、刷新重新扫描,都在设置面板里完成。
- 重复导入直接覆盖:被替换的旧文件夹直接删除,不再保留任何备份——你选中的那个文件夹就是要用的版本;你在面板里改过的中文名、分类与示例留在记录上,SKILL.md 换成新的。
- 2.0.5 之前的版本会把被替换的文件夹留成 .<id>.old-<时间戳> 备份。旧备份一旦被发现,工具条上会出现「清理旧版本(N)」按钮;点一下就把这些残留删除。重新导入同一个技能时,它自己的旧备份也会顺带清掉。
- 桌面版与 dsh web 都可用。

安装

插件托管在 GitHub(kee0012/dsh-science-skill,未发布到 npm),DSH 支持以 git 依赖方式直接安装:

从 GitHub 安装(推荐,日常使用;拉取默认分支最新提交)
dsh plugin --profile desktop add github:kee0012/dsh-science-skill

安装后需要重启 DSH,新的服务端代码与 client bundle 才会生效。

使用

导入技能

1. 打开 设置 → Science Skill,在「技能默认目录」里指定一个目录(只接受绝对路径,留空=未设置)。
2. 点「+ 添加目录」选择要导入的技能文件夹,确认分类即可。指定了默认目录时,导入的技能一律装到那里;未指定则回落到数据根下的 skills。
3. 技能已经躺在目录里但没被登记?点「刷新」重新扫描:插件会把它们登记进目录记录,并为从未命名过的技能生成中文名、中文简介与示例。

技能目录要求

- 每个技能是一个子目录,内含 SKILL.md。
- SKILL.md 的 frontmatter 必须有 name: 与 description:;name 要能当 kebab-case 标识用(小写字母、数字、单连字符)。
- 子目录名可以随便起,不要求等于 name——不合规的目录名用 frontmatter 的 name 当记录标识,插件不重命名、不移动你的文件夹。

调用技能

- 在技能墙上点卡片的「召唤」,或打开详情弹窗点「去试试」,该技能的 /命令 引用就会被插入当前会话的输入框,回车发送即可。
- 也可以直接在输入框里敲 / 加技能标识调用。

技能目录规则与数据存放

技能根如何解析

- 只加载你在「技能默认目录」里设定的那一个目录,可以用 skillDirs 配置追加额外的根。
- 未设定时回落到 <dataRoot>/skills,以及 $DSH_AGENTS_HOME(缺省 ~/.agents)下的 skills。
- 插件同时以提供者身份接入内核技能注册表,把暴露的目录限定为你选定的那一个,因此面板上显示的技能就是模型能加载的技能;内核没有这个注册表时插件照常工作,只是退化为「仅面板可见」。

登记规则

- 目录名本身合规时,直接用它当记录标识(/命令 另按 frontmatter 的 name 走)。
- 目录名不合规(带大写、空格、点、中文)时,用 frontmatter 的 name 当记录标识,并记住真实文件夹名。
- 两个文件夹声明了同一个 name 时,先扫到的收录,后一个跳过并说明「技能标识已被占用」。
- 点号开头的子目录视为导入残留,一律忽略;既没有合规目录名、SKILL.md 里也没有合规 name 的文件夹会被跳过并给出原因。
- 不再被扫描的旧记录会被排除出索引,但记录文件保留——把默认目录指回去就回来了。

不动你的文件

- 登记只写数据根下的记录,不复制、不修改、不删除技能目录里的任何东西;已有记录也原样保留(只增不改)。

数据放在哪里

数据根按顺序解析:插件配置的 dataRoot(相对路径按 process.cwd() 解析)→ $DSH_HOME → ~/.dsh。数据根下会用到:

| 路径 | 作用 |
| catalog/data/<id>.json | 每个技能一条元数据(中文名、简介、分类、命令、示例提问) |
| catalog/index.json | 聚合索引,面板读它 |
| catalog/categories.json | 分类表,数组顺序就是面板顺序 |
| catalog/schema.json | 记录结构校验;缺失时从插件自带 schema 播种一次 |
| skill-gate.json | 全局技能开关({"pinned": {"<name>": true}}) |
| skill-import.json | 技能默认目录({"defaultSkillDir": "<绝对路径>"},空串=未设置) |

配置

- id: science-skill

name: dsh-science-skill
config:
enabled: true # false 时不注册任何路由 dataRoot: '' # 缺省走 $DSH_HOME
skillDirs: [] # 追加的技能根(相对路径按 dataRoot 解析)
naming: true # 导入技能时用默认模型起中文名;关掉则只做规则推断

HTTP 接口

全部挂在 /api/dsh-science-skill 下。读接口返回 JSON,写接口收 JSON body。

| 方法 | 路径 | 作用 |
| GET | /skills | 技能列表(含中文名、简介、分类、/命令、示例提问) |
| GET | /skills/detail?id= | 单个技能的展示字段 + 示例 + SKILL.md 原文(上限 20000 字符,超出带 truncated;id 非法 400、不存在 404) |
| POST | /skills/refresh | 重新扫描:登记新技能、重建索引,并为「从未命名或没有示例」的技能启动回填(3 条并发) |
| GET | /import-settings | 技能默认目录(未设置时为空串) |
| POST | /import-settings | 设/清技能默认目录:{defaultSkillDir}(只接受绝对路径或空串) |
| GET | /skills/validate?path= | 校验一个技能目录能不能导入 |
| POST | /skills/import | 导入技能目录:{path, overwrite?, category?};不带 overwrite 时同名技能返回 400 already exists,应答含 overwritten 与「已覆盖更新」文案 |
| POST | /skills/update | 改展示信息与分类:{id, displayName?, displaySummary?, category?} |
| POST | /skills/delete | 删除技能(internal 共享包拒绝删除) |
| GET | /categories | 分类表 |
| GET | /categories/suggest?label= | 按名称推荐图标 |
| POST | /categories/create|rename|delete|reorder|restore | 分类增删改序与恢复默认 |
| GET | /catalog | 聚合索引原文 |
| GET | /skill-gate/state?sessionId= | 该会话的激活集合与全局开关 |
| POST | /skill-gate/pin | 改全局开关:{name, enabled} |
| POST | /skill-gate/session | 推某会话的激活集合:{sessionId, names[]} |

兼容性

- 需要 DSH ^0.2.0-rc.1:宿主半边只依赖 ctx.webServer,客户端半边使用官方插槽与 slots / sessions / conversation / remote / locale / inputTriggers 服务。
- 零运行时 npm 依赖,离线机器也能装起来。
- 可选:内核提供 skillGate 挂点时,面板里的开关会真正影响模型可见的技能目录;没有该挂点时插件照常工作,开关只记录状态。

已知限制

- 技能最终能否被模型加载,仍由 DSH 内核决定。同名技能存在多个来源时,按内核的优先级规则胜负,面板上显示的不一定是最终生效的那一份。
- 插件只读写数据根,不接管技能发现,也不 patch 内核。

反馈与贡献

问题、建议与 Pull Request 都欢迎:Issues。

许可证

MIT
预览分享卡片长什么样 ↓kee0012/dsh-science-skill
分享 · DeepSeek Harness Hub
kee0012/dsh-science-skill

科研技能中心 for DeepSeek Harness:技能目录、分类、按会话启用门控,以及用户可调用的…

回到顶部