dsh-skill-scoreboard

skilltested: passnpmMIT

Skill usage scoreboard for DSH: counts both per-session deduplicated and per-load usage, scores `skill` tool calls and direct `read` of skill files, and shows the tally in a three-tab settings page (skill ranking, session board, management) with paging and JSON import/export.

下载量 / Downloads
1,044
依赖体积 / node_modules
0.97 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

Install

dsh plugin --profile web add dsh-skill-scoreboard

Sandbox test results

StageResult
1 install✓
2 list✓
3 mount✓
4 boot—

Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).

README

dsh-skill-scoreboard

skill 使用记分板:模型每真正加载一个 skill 就自动记分,无需手动维护 skill-scoreboard.md。运行时数据写在 $DSH_HOME/.dsh/skill-scoreboard/skill-usage.json,默认 git 忽略。
功能要点:设置侧边栏独立页面「Skill 记分板」(三选项卡:Skill 排行 / 会话榜 / 管理;Skill 排行固定按会话去重降序,去重与加载两种次数同列显示并分页;会话榜显示持久化会话标题,可展开、可打开会话);agent/pre-step 把记分榜与每个 skill 的实际文件路径注入给 AI;同时记录两种次数(按会话去重 / 每次加载);read 工具直接读 skill 文件同样记分;管理页可导出 / 导入记分 JSON。

界面预览

Skill 排行(按会话去重降序,去重与加载两种次数同列显示,分页 20 条/页):

会话榜(按去重 skill 数降序,可展开看该会话加载过哪些 skill,可点击打开会话):

管理(导出 / 导入(合并、覆盖)/ 数据概览):

界面模拟页:assets/preview.html(单文件自包含,双击即可打开;跑的是真实 lib/client.js + 假数据,
三选项卡、分页、会话展开、导入导出全部可点,改动只留在页面内)
截图声明:screenshots.json(仓库根、与 package.json 同级,列出 assets/panel-*.png)——
DSH 插件榜单按此文件自动收录截图,作者推自己的仓库即生效,无需到榜单仓库提 PR

目录

  • [界面预览](#界面预览)
  • [架构设计](#架构设计)
  • [文件目录结构及作用](#文件目录结构及作用)
  • [启动脚本](#启动脚本)
  • [API 总览](#api-总览)
  • [数据结构](#数据结构)
  • [页面结构(三选项卡)](#页面结构三选项卡)
  • [版本列表](#版本列表)
  • [注意事项](#注意事项)
  • [开发计划 / 疑难杂症](#开发计划--疑难杂症)

架构设计

核心链路:监听 tools/result → skill 工具执行成功后记分 → 落盘 → 宿主只读 API → 设置页展示;另在 agent/pre-step 注入记分榜给 AI。

  • 判定:exec.name === "skill",从 arguments.name 取 skill 名;结果带 isError 的不计分。
  • 两种次数:count 按会话去重(同会话同一 skill 只计 1,跨会话累加);loads 每次成功加载都 +1;callId 防同一调用重复写。
  • 会话表:记分时同步写顶层 sessions 表({ [sessionId]: { loads, distinct, skills, firstUsedAt, lastUsedAt } }),记录该会话加载过哪些 skill、各几次,供会话榜使用。
  • 路径解析:注入时经 skills 服务(get / list 的 path / resourceBase)解析 skill 实际文件路径,失败则扫描 ai-work-archive/skills/、插件 skills/、.dsh/skills/ 等目录兜底;设置页 UI 不显示路径。
  • 展示:宿主端 GET /api/skill-scoreboard 只读接口(返回 skill 榜 + 会话榜 + 概览字段)+ 浏览器半侧挂 设置 → 侧边栏 →「Skill 记分板」(settings.section 独立页面,三个选项卡)。
  • 注入(+):agent/pre-step 事件(与 dsh-git-push 相同时机),每个 agent 首次 step 注入一次:记分榜 Top N(含会话去重 / 加载两种次数)+ 每个 skill 的实际文件路径。开关 injectEnabled、条数 injectTopN。
  • 不扫会话日志:Session 没有公开 events 字段,且 agent/pre-step 发生在本步 skill 调用之前,扫日志会漏记——因此记分只在 tools/result 发生。
skill 工具执行成功
    ↓
tools/result 事件
    ↓
name === "skill" 且结果非错误
    ↓
skill-usage.json(skills 榜 + sessions 会话表)
    ←── GET /api/skill-scoreboard(宿主只读接口)
    ↓
设置侧边栏「Skill 记分板」三选项卡页面(浏览器)
    ↓
agent/pre-step(每个 agent 首次 step)
    ↓
记分榜 Top N + skill 实际路径 注入给 AI

文件目录结构及作用

路径 · 作用

lib/index.js · 插件入口:apply 监听 tools/result,同时记 count(会话去重)、loads(每次加载)与 sessions 会话表;注册 GET /api/skill-scoreboard(含会话榜)与 export/import;agent/pre-step 注入记分榜 + skill 实际路径

lib/store.js · 宿主半侧 · 数据层(1.0.2 从 index.js 拆出):读 saveData(临时文件 + rename 原子写)/ 归一化 normalizeScoreboardPayload / 合并 mergeScoreboard / 记分 recordSkillUse·recordSessionUse

lib/skillpath.js · 宿主半侧 · 路径解析(1.0.2 拆出):「skill 名 ↔ 实际文件路径」双向解析——resolveSkillPath(先问 skills 服务、失败扫文件系统兜底)与 makeSkillPathIndex(懒构建 + TTL 的路径索引,供 read 工具直接读 skill 文件记分)

lib/titles.js · 宿主半侧 · 会话标题(1.0.2 拆出):四级取标题(活跃会话快照 → 折叠日志 session/title 事件 → cwd 目录名 → 短 id 兜底),限并发 + 落盘缓存 session-titles.json + warm 预热

lib/routes.js · 宿主半侧 · HTTP 路由(1.0.1 拆出):GET /api/skill-scoreboard 与 export/import 端点

lib/util.js · 通用小工具(1.0.1 拆出):createSemaphore / shortIdOf / errMsg 等零依赖助手

lib/client.js · 浏览器半侧:注册 settings.section 侧边栏「Skill 记分板」+ 三选项卡页面(Skill 排行 / 会话榜 / 管理),排行分页、会话标题解析与打开会话、导入导出,中英双语

cordis.patch.yml · bundle patch:insert id: skill-scoreboard

skills/dsh-skill-scoreboard.md · 插件手册 skill

test/test-scoreboard.mjs · 宿主半侧单测:记分去重 + 会话表 + v1→v2 迁移 + API/导入导出(mock ctx + 临时数据文件)

test/test-client.mjs · 前端冒烟测试:最小 React 替身渲染三选项卡、翻页、分页、会话展开、管理页

test/_hooks/host-resolver.mjs · 测试期宿主依赖解析钩子(零依赖方案):把 @deepseek-ai/* 裸导入解析到宿主已装的 node_modules,使单测能 import lib/index.js——仓库不需装依赖、不需软链接

$DSH_HOME/.dsh/skill-scoreboard/skill-usage.json · 运行时记分数据(git 忽略,不入库)

package.json · 包名 / 版本 / dsh.bundle.patch / dsh.skills / dsh.client / files+repository 等 npm 发布元数据

.npmignore · npm pack 排除规则:*.bak / .trash/ / 锁文件(npm pack --dry-run 验证 6 文件入库)

安装

装到哪个 profile 由 --profile 决定。从 GitHub 源安装(公开仓库,匿名可装):

⚠️ dsh plugin --profile <名> add … 当前实测会挂死且零输出(两次都以 timeout 结束、日志为空)⇒

改在该 profile 目录里直跑 pnpm(dsh plugin 的设计就是把参数转发给 profile 的 pnpm,两者本应等价):

cd <DSH_HOME>/profiles/<profile>
pnpm add github:EIGHTfs/dsh-skill-scoreboard#<commit>     # 用 commit 固定版本(远端暂无 tag)

两条前置条件(缺一即失败;本插件有 prepare/prepack,两条都需要):

1. 本机要有 npm —— pnpm 构建 git 来源的包时会在临时目录执行 npm install;

缺 npm 时报 ERR_PNPM_PREPARE_PACKAGE + spawn ENOENT: npm: command not found。

2. pnpm 11 默认禁止 git 源依赖执行构建脚本(供应链策略)⇒ 先在 profile 的

pnpm-workspace.yaml 里放行,否则报 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED:

   allowBuilds:
     dsh-skill-scoreboard@https://codeload.github.com/EIGHTfs/dsh-skill-scoreboard/tar.gz/<commit>: true

⚠️ key 里带 commit 哈希 ⇒ 换版本/换提交要同步改这一行。

装完三步:profile package.json 的 dsh.profile.bundles 登记插件名 → 本插件 cordis.patch.yml

顶层 - insert: 新建 loader 行 → 重启宿主并在日志确认 loaded。

本地源码安装(file: 依赖 + 手工三步曲)见下一节。

启动脚本

本插件随 DSH web profile 装载,没有独立进程,也没有 start.sh。

# 1) 源码进 profile(真实目录拷贝,不要软链到无 node_modules 的源码目录)
cp -a ./dsh-skill-scoreboard profiles/web/local-plugins/dsh-skill-scoreboard

# 2) package.json 声明 file: 依赖,并写入 dsh.profile.bundles
#    "dsh-skill-scoreboard": "file:./local-plugins/dsh-skill-scoreboard"

# 3) cordis.patch.yml insert
#    - insert:
#        - id: skill-scoreboard
#          name: dsh-skill-scoreboard
#          config:
#            enabled: true

# 4) dry-run:能 import,且导出 name / inject / apply
cd profiles/web
node --input-type=module -e 'import * as m from "dsh-skill-scoreboard"; console.log(m.name, m.inject, typeof m.apply)'

# 5) 自测(零依赖:宿主依赖经 test/_hooks/ 解析钩子注入,仓库内不装 node_modules)
node --import ./test/_hooks/host-resolver.mjs --test test/*.mjs   # 两套单测一起跑
node --import ./test/_hooks/host-resolver.mjs test/test-scoreboard.mjs   # 宿主半侧
node 

…(truncated)

Rendered from the README published on npm. Formatted only, not edited.

Versions

Published on npm::4,最后更新 2026-10-17

Links

GitHub · Homepage · npm

Same category