dsh-skill-stats
Skill usage statistics panel for DeepSeek Harness: every installed skill with its use count, session coverage, and share of all skill use.
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 25, 2026
- Updated
- Oct 25, 2026
Introduction
dsh-skill-stats
Which skills are actually being used — and which ones you installed and forgot. A left-sidebar panel for DeepSeek Harness that lists every installed skill with its use count, the number of conversations it appeared in, and its share of all skill use.

Screenshots use sample data; the panel itself reads your own local session logs.
Why
Harness shows you which skills exist. It does not show you which skills you use. Two consequences:
- A skill you installed months ago and never touched looks exactly like your most-loaded one.
- A skill you rely on constantly has no evidence you can point at.
This panel answers both with the smallest honest dataset: the two moments where a skill is really used.
What counts as one use
Counting starts the first moment the plugin is enabled. Two live host events are the only inputs:
- an agent calls the
skilltool; - you invoke a skill with
/name.
Nothing else counts, and no session log is ever read — not on open, not in the background. Sessions that happened before you installed the plugin are invisible by design.
| Column | Meaning |
|---|---|
| Calls | Number of the two events since enablement, inside the selected window |
| Sessions | Distinct conversations that used the skill |
| Share | This skill's calls ÷ every counted call in the current filter |
| Last used | When the most recent use happened |
| Source dot | Where the skill comes from: you, a plugin, bundled with Harness, a project, or uninstalled |
Features
- Live counting, no log parsing. Counts come from the host's own events (
tools/resultandsession/event), are debounced into a small JSON file, and survive restarts. Opening the panel costs one file read. - Every installed skill is listed, including skills that are only callable as
/name(they carry a smallmanualtag) and plugin-provided skills. - Honest source labels.
bundledin the skills service only means "from a bundled skill root", so origin is resolved from the file's real location: a plugin's ownskill//skills/directory is labelled with that plugin's package name. - Time windows and filters: all time, today, 7 days, 30 days; filter by origin; search; "never used only"; optionally include skills that are no longer installed.
- Local only. The counter file lives in your own DSH home. No network requests, no telemetry.
Install
This package is plain JavaScript — no build step.
From GitHub (recommended until the npm release)
dsh plugin --profile web add github:HLhhh/dsh-skill-stats
Replace web with your profile name if it differs.
From a local checkout or tarball
npm pack # produces dsh-skill-stats-0.1.0.tgz
dsh plugin --profile web add ./dsh-skill-stats-0.1.0.tgz
From an agent session
Point the plugin manager at the package directory:
plugin_manager install_bundle target=/absolute/path/to/dsh-skill-stats
After installing, refresh the page once — the sidebar icon appears next to the other panel icons.
Update / remove
dsh plugin --profile web update github:HLhhh/dsh-skill-stats
dsh plugin --profile web remove dsh-skill-stats
Usage
| Entry | What it does |
|---|---|
| Sidebar bar-chart icon | Opens the Skill usage panel |
| Panel header | Installed / used / never used / total calls / sessions |
| Filter bar | Time window, source, search, never-used-only, include uninstalled |
| Table header | Click any column to sort; click again to reverse |
Nothing else registers: no tools, no slash commands, no changes to other panels.
Compatibility
| Surface | Status |
|---|---|
| DeepSeek Harness | 0.2.x (built and tested against 0.2.1-rc.3 Desktop and Web) |
| Node | ^22.19.0 || >=24.0.0 (uses zlib.zstdDecompressSync) |
| Platforms | Desktop and Web profiles with the skills feature |
How the inventory is built
ctx.skills.list() only reports the layer visible to a host plugin's own scope, and a desktop profile mounts ~/.dsh/skills inside the agent preset's standing composition. The panel therefore merges four sources and de-duplicates by name:
ctx.skills.list()— everything the global scope can see;- the disk skill roots —
$DSH_HOME/skills,~/.agents/skills,$DSH_BUNDLED_SKILL_DIR, and the default working directory's.dsh/skills/.agents/skills, parsingSKILL.mdfrontmatter (CRLF and BOM tolerant,disable-model-invocationrespected); - skills shipped inside installed plugin packages (
skill/,skills/,assets/), which is what turns a plugin's skill intoplugin:<package>instead of the genericbundled; - each agent preset's scope, so preset-mounted skills appear too.
The result is cached for a minute and invalidated by skills/change.
Known limitations
- No history. Counting begins when the plugin is first enabled; anything before that is not counted — by design, because it would require reading old session logs.
- Deleting
$DSH_HOME/storages/dsh-skill-stats/usage.jsonresets the baseline; reload the plugin afterwards so the new file is created at that moment. - A skill that is used but cannot be discovered by any inventory source appears under "uninstalled" (toggle it in the filter bar).
- Duplicate skill names are resolved to the effective one; the shadowed copy is not shown.
- Failure marking knows only the
isErrorresult the host reports for the skill tool.
Development
npm test # syntax + behavior smoke tests, no dependencies
npm pack --dry-run
Repository layout:
index.js host half: session scan, aggregation, /dsh-skill-stats/api
client.js browser half: sidebar icon + panel (plain JS, no build)
cordis.patch.yml one Loader row
locale/ plugin-manager title/description (en, zh)
icon.svg plugin and sidebar icon
docs/ screenshots
test/smoke.mjs dependency-free smoke tests, also run in CI
How changes land
This repository has one maintainer and an AI agent working on it. Before changing code, the agent asks which path to take — it never decides alone:
| Path | When |
|---|---|
| Pull request | Behaviour changes, version bumps, UI changes, anything that touches counting — and whenever review is wanted |
Direct push to main | Only when the maintainer explicitly asks for it |
main has no branch protection, on purpose: the choice is made per change by the maintainer, not enforced by the platform. Pull requests use ./.github/pull_request_template.md.
License
MIT — see LICENSE.